Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : */
9 :
10 : #include "sal/config.h"
11 :
12 : #include <cassert>
13 : #include <cstdarg>
14 : #include <cstddef>
15 : #include <cstdio>
16 : #include <cstdlib>
17 : #include <cstring>
18 : #include <sstream>
19 :
20 : #include <stdio.h>
21 : #include <string.h>
22 :
23 : #include "osl/thread.hxx"
24 : #include "rtl/string.h"
25 : #include "sal/detail/log.h"
26 : #include "sal/log.hxx"
27 : #include "sal/types.h"
28 :
29 : #include "logformat.hxx"
30 :
31 : #if defined ANDROID
32 : #include <android/log.h>
33 : #elif defined WNT
34 : #include <process.h>
35 : #define OSL_DETAIL_GETPID _getpid()
36 : #else
37 : #include <unistd.h>
38 : #define OSL_DETAIL_GETPID getpid()
39 : #endif
40 :
41 : #if HAVE_SYSLOG_H
42 : #include <syslog.h>
43 : // sal/osl/unx/salinit.cxx::sal_detail_initialize updates this:
44 : bool sal_use_syslog;
45 : #else
46 : enum { sal_use_syslog = false };
47 : #endif
48 :
49 : // Avoid the use of other sal code in this file as much as possible, so that
50 : // this code can be called from other sal code without causing endless
51 : // recursion.
52 :
53 : namespace {
54 :
55 0 : bool equalStrings(
56 : char const * string1, std::size_t length1, char const * string2,
57 : std::size_t length2)
58 : {
59 0 : return length1 == length2 && std::memcmp(string1, string2, length1) == 0;
60 : }
61 :
62 : #if !defined ANDROID
63 0 : char const * toString(sal_detail_LogLevel level) {
64 0 : switch (level) {
65 : default:
66 : assert(false); // this cannot happen
67 : // fall through
68 : case SAL_DETAIL_LOG_LEVEL_INFO:
69 0 : return "info";
70 : case SAL_DETAIL_LOG_LEVEL_WARN:
71 0 : return "warn";
72 : case SAL_DETAIL_LOG_LEVEL_DEBUG:
73 0 : return "debug";
74 : }
75 : }
76 : #endif
77 :
78 : // getenv is not thread safe, so minimize use of result; except on Android, see
79 : // 60628799633ffde502cb105b98d3f254f93115aa "Notice if SAL_LOG is changed while
80 : // the process is running":
81 : #if defined ANDROID
82 :
83 : char const * getEnvironmentVariable() {
84 : return std::getenv("SAL_LOG");
85 : }
86 :
87 : #else
88 :
89 0 : char const * getEnvironmentVariable_() {
90 0 : char const * p1 = std::getenv("SAL_LOG");
91 0 : if (p1 == 0) {
92 0 : return 0;
93 : }
94 0 : char const * p2 = strdup(p1); // leaked
95 0 : if (p2 == 0) {
96 0 : std::abort(); // cannot do much here
97 : }
98 0 : return p2;
99 : }
100 :
101 0 : char const * getEnvironmentVariable() {
102 0 : static char const * env = getEnvironmentVariable_();
103 0 : return env;
104 : }
105 :
106 : #endif
107 :
108 0 : bool report(sal_detail_LogLevel level, char const * area) {
109 0 : if (level == SAL_DETAIL_LOG_LEVEL_DEBUG)
110 0 : return true;
111 : assert(area != 0);
112 0 : char const * env = getEnvironmentVariable();
113 0 : if (env == 0) {
114 0 : env = "+WARN";
115 : }
116 0 : std::size_t areaLen = std::strlen(area);
117 : enum Sense { POSITIVE = 0, NEGATIVE = 1 };
118 0 : std::size_t senseLen[2] = { 0, 1 };
119 : // initial senseLen[POSITIVE] < senseLen[NEGATIVE], so that if there are
120 : // no matching switches at all, the result will be negative (and
121 : // initializing with 1 is safe as the length of a valid switch, even
122 : // without the "+"/"-" prefix, will always be > 1)
123 0 : for (char const * p = env;;) {
124 : Sense sense;
125 0 : switch (*p++) {
126 : case '\0':
127 0 : return senseLen[POSITIVE] >= senseLen[NEGATIVE];
128 : // if a specific item is both postiive and negative
129 : // (senseLen[POSITIVE] == senseLen[NEGATIVE]), default to
130 : // positive
131 : case '+':
132 0 : sense = POSITIVE;
133 0 : break;
134 : case '-':
135 0 : sense = NEGATIVE;
136 0 : break;
137 : default:
138 0 : return true; // upon an illegal SAL_LOG value, enable everything
139 : }
140 0 : char const * p1 = p;
141 0 : while (*p1 != '.' && *p1 != '+' && *p1 != '-' && *p1 != '\0') {
142 0 : ++p1;
143 : }
144 : bool match;
145 0 : if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("INFO"))) {
146 0 : match = level == SAL_DETAIL_LOG_LEVEL_INFO;
147 0 : } else if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("WARN")))
148 : {
149 0 : match = level == SAL_DETAIL_LOG_LEVEL_WARN;
150 : } else {
151 0 : return true;
152 : // upon an illegal SAL_LOG value, everything is considered
153 : // positive
154 : }
155 0 : char const * p2 = p1;
156 0 : while (*p2 != '+' && *p2 != '-' && *p2 != '\0') {
157 0 : ++p2;
158 : }
159 0 : if (match) {
160 0 : if (*p1 == '.') {
161 0 : ++p1;
162 0 : std::size_t n = p2 - p1;
163 0 : if ((n == areaLen && equalStrings(p1, n, area, areaLen))
164 0 : || (n < areaLen && area[n] == '.'
165 0 : && equalStrings(p1, n, area, n)))
166 : {
167 0 : senseLen[sense] = p2 - p;
168 : }
169 : } else {
170 0 : senseLen[sense] = p1 - p;
171 : }
172 : }
173 0 : p = p2;
174 0 : }
175 : }
176 :
177 0 : void log(
178 : sal_detail_LogLevel level, char const * area, char const * where,
179 : char const * message)
180 : {
181 0 : std::ostringstream s;
182 : #if !defined ANDROID
183 : // On Android, the area will be used as the "tag," and log info already
184 : // contains the PID
185 0 : if (!sal_use_syslog) {
186 0 : s << toString(level) << ':';
187 : }
188 0 : if (level != SAL_DETAIL_LOG_LEVEL_DEBUG) {
189 0 : s << area << ':';
190 : }
191 0 : s << OSL_DETAIL_GETPID << ':';
192 : #endif
193 0 : s << osl::Thread::getCurrentIdentifier() << ':';
194 0 : if (level == SAL_DETAIL_LOG_LEVEL_DEBUG) {
195 0 : s << ' ';
196 : } else {
197 : s << (where
198 0 : + (std::strncmp(where, SRCDIR "/", std::strlen(SRCDIR "/")) == 0
199 0 : ? std::strlen(SRCDIR "/") : 0));
200 : }
201 0 : s << message << '\n';
202 : #if defined ANDROID
203 : int android_log_level;
204 : switch (level) {
205 : case SAL_DETAIL_LOG_LEVEL_INFO:
206 : android_log_level = ANDROID_LOG_INFO;
207 : break;
208 : case SAL_DETAIL_LOG_LEVEL_WARN:
209 : android_log_level = ANDROID_LOG_WARN;
210 : break;
211 : case SAL_DETAIL_LOG_LEVEL_DEBUG:
212 : android_log_level = ANDROID_LOG_DEBUG;
213 : break;
214 : default:
215 : android_log_level = ANDROID_LOG_INFO;
216 : break;
217 : }
218 : __android_log_print(
219 : android_log_level, area == 0 ? "LibreOffice" : area, "%s",
220 : s.str().c_str());
221 : #else
222 0 : if (sal_use_syslog) {
223 : #if HAVE_SYSLOG_H
224 : int prio;
225 0 : switch (level) {
226 : case SAL_DETAIL_LOG_LEVEL_INFO:
227 0 : prio = LOG_INFO;
228 0 : break;
229 : default:
230 : assert(false); // this cannot happen
231 : // fall through
232 : case SAL_DETAIL_LOG_LEVEL_WARN:
233 0 : prio = LOG_WARNING;
234 0 : break;
235 : case SAL_DETAIL_LOG_LEVEL_DEBUG:
236 0 : prio = LOG_DEBUG;
237 0 : break;
238 : }
239 0 : syslog(prio, "%s", s.str().c_str());
240 : #endif
241 : } else {
242 0 : std::fputs(s.str().c_str(), stderr);
243 0 : }
244 : #endif
245 0 : }
246 :
247 : }
248 :
249 0 : void sal_detail_log(
250 : sal_detail_LogLevel level, char const * area, char const * where,
251 : char const * message)
252 : {
253 0 : if (report(level, area)) {
254 0 : log(level, area, where, message);
255 : }
256 0 : }
257 :
258 0 : void sal_detail_logFormat(
259 : sal_detail_LogLevel level, char const * area, char const * where,
260 : char const * format, ...)
261 : {
262 0 : if (report(level, area)) {
263 : std::va_list args;
264 0 : va_start(args, format);
265 0 : osl::detail::logFormat(level, area, where, format, args);
266 0 : va_end(args);
267 : }
268 0 : }
269 :
270 0 : void osl::detail::logFormat(
271 : sal_detail_LogLevel level, char const * area, char const * where,
272 : char const * format, std::va_list arguments)
273 : {
274 : char buf[1024];
275 0 : int const len = sizeof buf - RTL_CONSTASCII_LENGTH("...");
276 0 : int n = vsnprintf(buf, len, format, arguments);
277 0 : if (n < 0) {
278 0 : std::strcpy(buf, "???");
279 0 : } else if (n >= len) {
280 0 : std::strcpy(buf + len - 1, "...");
281 : }
282 0 : log(level, area, where, buf);
283 0 : }
284 :
285 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|