]> code.delx.au - pulseaudio/blob - src/pulsecore/log.c
perl -p -i -e 's/pa_rtclock_usec/pa_rtclock_now/g' `find . -name '*.[ch]'`
[pulseaudio] / src / pulsecore / log.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of the
10 License, or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32
33 #ifdef HAVE_EXECINFO_H
34 #include <execinfo.h>
35 #endif
36
37 #ifdef HAVE_SYSLOG_H
38 #include <syslog.h>
39 #endif
40
41 #include <pulse/utf8.h>
42 #include <pulse/xmalloc.h>
43 #include <pulse/util.h>
44 #include <pulse/timeval.h>
45
46 #include <pulsecore/macro.h>
47 #include <pulsecore/core-util.h>
48 #include <pulsecore/rtclock.h>
49 #include <pulsecore/once.h>
50 #include <pulsecore/ratelimit.h>
51
52 #include "log.h"
53
54 #define ENV_LOG_SYSLOG "PULSE_LOG_SYSLOG"
55 #define ENV_LOG_LEVEL "PULSE_LOG"
56 #define ENV_LOG_COLORS "PULSE_LOG_COLORS"
57 #define ENV_LOG_PRINT_TIME "PULSE_LOG_TIME"
58 #define ENV_LOG_PRINT_FILE "PULSE_LOG_FILE"
59 #define ENV_LOG_PRINT_META "PULSE_LOG_META"
60 #define ENV_LOG_PRINT_LEVEL "PULSE_LOG_LEVEL"
61 #define ENV_LOG_BACKTRACE "PULSE_LOG_BACKTRACE"
62 #define ENV_LOG_BACKTRACE_SKIP "PULSE_LOG_BACKTRACE_SKIP"
63
64 static char *ident = NULL; /* in local charset format */
65 static pa_log_target_t target = PA_LOG_STDERR, target_override;
66 static pa_bool_t target_override_set = FALSE;
67 static pa_log_level_t maximum_level = PA_LOG_ERROR, maximum_level_override = PA_LOG_ERROR;
68 static unsigned show_backtrace = 0, show_backtrace_override = 0, skip_backtrace = 0;
69 static pa_log_flags_t flags = 0, flags_override = 0;
70
71 #ifdef HAVE_SYSLOG_H
72 static const int level_to_syslog[] = {
73 [PA_LOG_ERROR] = LOG_ERR,
74 [PA_LOG_WARN] = LOG_WARNING,
75 [PA_LOG_NOTICE] = LOG_NOTICE,
76 [PA_LOG_INFO] = LOG_INFO,
77 [PA_LOG_DEBUG] = LOG_DEBUG
78 };
79 #endif
80
81 static const char level_to_char[] = {
82 [PA_LOG_ERROR] = 'E',
83 [PA_LOG_WARN] = 'W',
84 [PA_LOG_NOTICE] = 'N',
85 [PA_LOG_INFO] = 'I',
86 [PA_LOG_DEBUG] = 'D'
87 };
88
89 void pa_log_set_ident(const char *p) {
90 pa_xfree(ident);
91
92 if (!(ident = pa_utf8_to_locale(p)))
93 ident = pa_ascii_filter(p);
94 }
95
96 /* To make valgrind shut up. */
97 static void ident_destructor(void) PA_GCC_DESTRUCTOR;
98 static void ident_destructor(void) {
99 if (!pa_in_valgrind())
100 return;
101
102 pa_xfree(ident);
103 }
104
105 void pa_log_set_level(pa_log_level_t l) {
106 pa_assert(l < PA_LOG_LEVEL_MAX);
107
108 maximum_level = l;
109 }
110
111 void pa_log_set_target(pa_log_target_t t) {
112 pa_assert(t < PA_LOG_TARGET_MAX);
113
114 target = t;
115 }
116
117 void pa_log_set_flags(pa_log_flags_t _flags, pa_log_merge_t merge) {
118 pa_assert(!(_flags & ~(PA_LOG_COLORS|PA_LOG_PRINT_TIME|PA_LOG_PRINT_FILE|PA_LOG_PRINT_META|PA_LOG_PRINT_LEVEL)));
119
120 if (merge == PA_LOG_SET)
121 flags |= _flags;
122 else if (merge == PA_LOG_UNSET)
123 flags &= ~_flags;
124 else
125 flags = _flags;
126 }
127
128 void pa_log_set_show_backtrace(unsigned nlevels) {
129 show_backtrace = nlevels;
130 }
131
132 void pa_log_set_skip_backtrace(unsigned nlevels) {
133 skip_backtrace = nlevels;
134 }
135
136 #ifdef HAVE_EXECINFO_H
137
138 static char* get_backtrace(unsigned show_nframes) {
139 void* trace[32];
140 int n_frames;
141 char **symbols, *e, *r;
142 unsigned j, n, s;
143 size_t a;
144
145 pa_assert(show_nframes > 0);
146
147 n_frames = backtrace(trace, PA_ELEMENTSOF(trace));
148
149 if (n_frames <= 0)
150 return NULL;
151
152 symbols = backtrace_symbols(trace, n_frames);
153
154 if (!symbols)
155 return NULL;
156
157 s = skip_backtrace;
158 n = PA_MIN((unsigned) n_frames, s + show_nframes);
159
160 a = 4;
161
162 for (j = s; j < n; j++) {
163 if (j > s)
164 a += 2;
165 a += strlen(pa_path_get_filename(symbols[j]));
166 }
167
168 r = pa_xnew(char, a);
169
170 strcpy(r, " (");
171 e = r + 2;
172
173 for (j = s; j < n; j++) {
174 const char *sym;
175
176 if (j > s) {
177 strcpy(e, "<<");
178 e += 2;
179 }
180
181 sym = pa_path_get_filename(symbols[j]);
182
183 strcpy(e, sym);
184 e += strlen(sym);
185 }
186
187 strcpy(e, ")");
188
189 free(symbols);
190
191 return r;
192 }
193
194 #endif
195
196 static void init_defaults(void) {
197 const char *e;
198
199 if (!ident) {
200 char binary[256];
201 if (pa_get_binary_name(binary, sizeof(binary)))
202 pa_log_set_ident(binary);
203 }
204
205 if (getenv(ENV_LOG_SYSLOG)) {
206 target_override = PA_LOG_SYSLOG;
207 target_override_set = TRUE;
208 }
209
210 if ((e = getenv(ENV_LOG_LEVEL))) {
211 maximum_level_override = (pa_log_level_t) atoi(e);
212
213 if (maximum_level_override >= PA_LOG_LEVEL_MAX)
214 maximum_level_override = PA_LOG_LEVEL_MAX-1;
215 }
216
217 if (getenv(ENV_LOG_COLORS))
218 flags_override |= PA_LOG_COLORS;
219
220 if (getenv(ENV_LOG_PRINT_TIME))
221 flags_override |= PA_LOG_PRINT_TIME;
222
223 if (getenv(ENV_LOG_PRINT_FILE))
224 flags_override |= PA_LOG_PRINT_FILE;
225
226 if (getenv(ENV_LOG_PRINT_META))
227 flags_override |= PA_LOG_PRINT_META;
228
229 if (getenv(ENV_LOG_PRINT_LEVEL))
230 flags_override |= PA_LOG_PRINT_LEVEL;
231
232 if ((e = getenv(ENV_LOG_BACKTRACE))) {
233 show_backtrace_override = (unsigned) atoi(e);
234
235 if (show_backtrace_override <= 0)
236 show_backtrace_override = 0;
237 }
238
239 if ((e = getenv(ENV_LOG_BACKTRACE_SKIP))) {
240 skip_backtrace = (unsigned) atoi(e);
241
242 if (skip_backtrace <= 0)
243 skip_backtrace = 0;
244 }
245 }
246
247 void pa_log_levelv_meta(
248 pa_log_level_t level,
249 const char*file,
250 int line,
251 const char *func,
252 const char *format,
253 va_list ap) {
254
255 char *t, *n;
256 int saved_errno = errno;
257 char *bt = NULL;
258 pa_log_target_t _target;
259 pa_log_level_t _maximum_level;
260 unsigned _show_backtrace;
261 pa_log_flags_t _flags;
262
263 /* We don't use dynamic memory allocation here to minimize the hit
264 * in RT threads */
265 char text[16*1024], location[128], timestamp[32];
266
267 pa_assert(level < PA_LOG_LEVEL_MAX);
268 pa_assert(format);
269
270 PA_ONCE_BEGIN {
271 init_defaults();
272 } PA_ONCE_END;
273
274 _target = target_override_set ? target_override : target;
275 _maximum_level = PA_MAX(maximum_level, maximum_level_override);
276 _show_backtrace = PA_MAX(show_backtrace, show_backtrace_override);
277 _flags = flags | flags_override;
278
279 if (PA_LIKELY(level > _maximum_level)) {
280 errno = saved_errno;
281 return;
282 }
283
284 pa_vsnprintf(text, sizeof(text), format, ap);
285
286 if ((_flags & PA_LOG_PRINT_META) && file && line > 0 && func)
287 pa_snprintf(location, sizeof(location), "[%s:%i %s()] ", file, line, func);
288 else if ((_flags & (PA_LOG_PRINT_META|PA_LOG_PRINT_FILE)) && file)
289 pa_snprintf(location, sizeof(location), "%s: ", pa_path_get_filename(file));
290 else
291 location[0] = 0;
292
293 if (_flags & PA_LOG_PRINT_TIME) {
294 static pa_usec_t start, last;
295 pa_usec_t u, a, r;
296
297 u = pa_rtclock_now();
298
299 PA_ONCE_BEGIN {
300 start = u;
301 last = u;
302 } PA_ONCE_END;
303
304 r = u - last;
305 a = u - start;
306
307 /* This is not thread safe, but this is a debugging tool only
308 * anyway. */
309 last = u;
310
311 pa_snprintf(timestamp, sizeof(timestamp), "(%4llu.%03llu|%4llu.%03llu) ",
312 (unsigned long long) (a / PA_USEC_PER_SEC),
313 (unsigned long long) (((a / PA_USEC_PER_MSEC)) % 1000),
314 (unsigned long long) (r / PA_USEC_PER_SEC),
315 (unsigned long long) (((r / PA_USEC_PER_MSEC)) % 1000));
316
317 } else
318 timestamp[0] = 0;
319
320 #ifdef HAVE_EXECINFO_H
321 if (_show_backtrace > 0)
322 bt = get_backtrace(_show_backtrace);
323 #endif
324
325 if (!pa_utf8_valid(text))
326 pa_logl(level, "Invalid UTF-8 string following below:");
327
328 for (t = text; t; t = n) {
329 if ((n = strchr(t, '\n'))) {
330 *n = 0;
331 n++;
332 }
333
334 /* We ignore strings only made out of whitespace */
335 if (t[strspn(t, "\t ")] == 0)
336 continue;
337
338 switch (_target) {
339
340 case PA_LOG_STDERR: {
341 const char *prefix = "", *suffix = "", *grey = "";
342 char *local_t;
343
344 #ifndef OS_IS_WIN32
345 /* Yes indeed. Useless, but fun! */
346 if ((_flags & PA_LOG_COLORS) && isatty(STDERR_FILENO)) {
347 if (level <= PA_LOG_ERROR)
348 prefix = "\x1B[1;31m";
349 else if (level <= PA_LOG_WARN)
350 prefix = "\x1B[1m";
351
352 if (bt)
353 grey = "\x1B[2m";
354
355 if (grey[0] || prefix[0])
356 suffix = "\x1B[0m";
357 }
358 #endif
359
360 /* We shouldn't be using dynamic allocation here to
361 * minimize the hit in RT threads */
362 if ((local_t = pa_utf8_to_locale(t)))
363 t = local_t;
364
365 if (_flags & PA_LOG_PRINT_LEVEL)
366 fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, t, grey, pa_strempty(bt), suffix);
367 else
368 fprintf(stderr, "%s%s%s%s%s%s%s\n", timestamp, location, prefix, t, grey, pa_strempty(bt), suffix);
369
370 pa_xfree(local_t);
371
372 break;
373 }
374
375 #ifdef HAVE_SYSLOG_H
376 case PA_LOG_SYSLOG: {
377 char *local_t;
378
379 openlog(ident, LOG_PID, LOG_USER);
380
381 if ((local_t = pa_utf8_to_locale(t)))
382 t = local_t;
383
384 syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
385 pa_xfree(local_t);
386
387 break;
388 }
389 #endif
390
391 case PA_LOG_NULL:
392 default:
393 break;
394 }
395 }
396
397 pa_xfree(bt);
398 errno = saved_errno;
399 }
400
401 void pa_log_level_meta(
402 pa_log_level_t level,
403 const char*file,
404 int line,
405 const char *func,
406 const char *format, ...) {
407
408 va_list ap;
409 va_start(ap, format);
410 pa_log_levelv_meta(level, file, line, func, format, ap);
411 va_end(ap);
412 }
413
414 void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {
415 pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
416 }
417
418 void pa_log_level(pa_log_level_t level, const char *format, ...) {
419 va_list ap;
420
421 va_start(ap, format);
422 pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
423 va_end(ap);
424 }
425
426 pa_bool_t pa_log_ratelimit(void) {
427 /* Not more than 10 messages every 5s */
428 static PA_DEFINE_RATELIMIT(ratelimit, 5 * PA_USEC_PER_SEC, 10);
429
430 return pa_ratelimit_test(&ratelimit);
431 }