]> code.delx.au - pulseaudio/blob - src/pulsecore/log.c
Merge commit 'origin/master-tx'
[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 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
51 #include "log.h"
52
53 #define ENV_LOGLEVEL "PULSE_LOG"
54 #define ENV_LOGMETA "PULSE_LOG_META"
55 #define ENV_LOGTIME "PULSE_LOG_TIME"
56 #define ENV_LOGBACKTRACE "PULSE_LOG_BACKTRACE"
57
58 static char *log_ident = NULL, *log_ident_local = NULL;
59 static pa_log_target_t log_target = PA_LOG_STDERR;
60 static pa_log_func_t user_log_func = NULL;
61 static pa_log_level_t maximal_level = PA_LOG_ERROR;
62 static unsigned show_backtrace = 0;
63 static pa_bool_t show_meta = FALSE;
64 static pa_bool_t show_time = FALSE;
65
66 #ifdef HAVE_SYSLOG_H
67 static const int level_to_syslog[] = {
68 [PA_LOG_ERROR] = LOG_ERR,
69 [PA_LOG_WARN] = LOG_WARNING,
70 [PA_LOG_NOTICE] = LOG_NOTICE,
71 [PA_LOG_INFO] = LOG_INFO,
72 [PA_LOG_DEBUG] = LOG_DEBUG
73 };
74 #endif
75
76 static const char level_to_char[] = {
77 [PA_LOG_ERROR] = 'E',
78 [PA_LOG_WARN] = 'W',
79 [PA_LOG_NOTICE] = 'N',
80 [PA_LOG_INFO] = 'I',
81 [PA_LOG_DEBUG] = 'D'
82 };
83
84 void pa_log_set_ident(const char *p) {
85 pa_xfree(log_ident);
86 pa_xfree(log_ident_local);
87
88 log_ident = pa_xstrdup(p);
89 if (!(log_ident_local = pa_utf8_to_locale(log_ident)))
90 log_ident_local = pa_xstrdup(log_ident);
91 }
92
93 /* To make valgrind shut up. */
94 static void ident_destructor(void) PA_GCC_DESTRUCTOR;
95 static void ident_destructor(void) {
96 if (!pa_in_valgrind())
97 return;
98
99 pa_xfree(log_ident);
100 pa_xfree(log_ident_local);
101 }
102
103 void pa_log_set_maximal_level(pa_log_level_t l) {
104 pa_assert(l < PA_LOG_LEVEL_MAX);
105
106 maximal_level = l;
107 }
108
109 void pa_log_set_target(pa_log_target_t t, pa_log_func_t func) {
110 pa_assert(t == PA_LOG_USER || !func);
111
112 log_target = t;
113 user_log_func = func;
114 }
115
116 void pa_log_set_show_meta(pa_bool_t b) {
117 show_meta = b;
118 }
119
120 void pa_log_set_show_time(pa_bool_t b) {
121 show_time = b;
122 }
123
124 void pa_log_set_show_backtrace(unsigned nlevels) {
125 show_backtrace = nlevels;
126 }
127
128 #ifdef HAVE_EXECINFO_H
129
130 static char* get_backtrace(unsigned show_nframes) {
131 void* trace[32];
132 int n_frames;
133 char **symbols, *e, *r;
134 unsigned j, n;
135 size_t a;
136
137 if (show_nframes <= 0)
138 return NULL;
139
140 n_frames = backtrace(trace, PA_ELEMENTSOF(trace));
141
142 if (n_frames <= 0)
143 return NULL;
144
145 symbols = backtrace_symbols(trace, n_frames);
146
147 if (!symbols)
148 return NULL;
149
150 n = PA_MIN((unsigned) n_frames, show_nframes);
151
152 a = 4;
153
154 for (j = 0; j < n; j++) {
155 if (j > 0)
156 a += 2;
157 a += strlen(symbols[j]);
158 }
159
160 r = pa_xnew(char, a);
161
162 strcpy(r, " (");
163 e = r + 2;
164
165 for (j = 0; j < n; j++) {
166 if (j > 0) {
167 strcpy(e, "<<");
168 e += 2;
169 }
170
171 strcpy(e, symbols[j]);
172 e += strlen(symbols[j]);
173 }
174
175 strcpy(e, ")");
176
177 free(symbols);
178
179 return r;
180 }
181
182 #endif
183
184 void pa_log_levelv_meta(
185 pa_log_level_t level,
186 const char*file,
187 int line,
188 const char *func,
189 const char *format,
190 va_list ap) {
191
192 const char *e;
193 char *t, *n;
194 int saved_errno = errno;
195 char *bt = NULL;
196 pa_log_level_t ml;
197 #ifdef HAVE_EXECINFO_H
198 unsigned show_bt;
199 #endif
200
201 /* We don't use dynamic memory allocation here to minimize the hit
202 * in RT threads */
203 char text[4096], location[128], timestamp[32];
204
205 pa_assert(level < PA_LOG_LEVEL_MAX);
206 pa_assert(format);
207
208 ml = maximal_level;
209
210 if (PA_UNLIKELY((e = getenv(ENV_LOGLEVEL)))) {
211 pa_log_level_t eml = (pa_log_level_t) atoi(e);
212
213 if (eml > ml)
214 ml = eml;
215 }
216
217 if (PA_LIKELY(level > ml)) {
218 errno = saved_errno;
219 return;
220 }
221
222 pa_vsnprintf(text, sizeof(text), format, ap);
223
224 if ((show_meta || getenv(ENV_LOGMETA)) && file && line > 0 && func)
225 pa_snprintf(location, sizeof(location), "[%s:%i %s()] ", file, line, func);
226 else if (file)
227 pa_snprintf(location, sizeof(location), "%s: ", pa_path_get_filename(file));
228 else
229 location[0] = 0;
230
231 if (show_time || getenv(ENV_LOGTIME)) {
232 static pa_usec_t start, last;
233 pa_usec_t u, a, r;
234
235 u = pa_rtclock_usec();
236
237 PA_ONCE_BEGIN {
238 start = u;
239 last = u;
240 } PA_ONCE_END;
241
242 r = u - last;
243 a = u - start;
244
245 /* This is not thread safe, but this is a debugging tool only
246 * anyway. */
247 last = u;
248
249 pa_snprintf(timestamp, sizeof(timestamp), "(%4llu.%03llu|%4llu.%03llu) ",
250 (unsigned long long) (a / PA_USEC_PER_SEC),
251 (unsigned long long) (((a / PA_USEC_PER_MSEC)) % 1000),
252 (unsigned long long) (r / PA_USEC_PER_SEC),
253 (unsigned long long) (((r / PA_USEC_PER_MSEC)) % 1000));
254
255 } else
256 timestamp[0] = 0;
257
258 #ifdef HAVE_EXECINFO_H
259 show_bt = show_backtrace;
260
261 if ((e = getenv(ENV_LOGBACKTRACE))) {
262 unsigned ebt = (unsigned) atoi(e);
263
264 if (ebt > show_bt)
265 show_bt = ebt;
266 }
267
268 bt = get_backtrace(show_bt);
269 #endif
270
271 if (!pa_utf8_valid(text))
272 pa_log_level(level, __FILE__": invalid UTF-8 string following below:");
273
274 for (t = text; t; t = n) {
275 if ((n = strchr(t, '\n'))) {
276 *n = 0;
277 n++;
278 }
279
280 if (!*t)
281 continue;
282
283 switch (log_target) {
284 case PA_LOG_STDERR: {
285 const char *prefix = "", *suffix = "", *grey = "";
286 char *local_t;
287
288 #ifndef OS_IS_WIN32
289 /* Yes indeed. Useless, but fun! */
290 if (isatty(STDERR_FILENO)) {
291 if (level <= PA_LOG_ERROR)
292 prefix = "\x1B[1;31m";
293 else if (level <= PA_LOG_WARN)
294 prefix = "\x1B[1m";
295
296 if (bt)
297 grey = "\x1B[2m";
298
299 if (grey[0] || prefix[0])
300 suffix = "\x1B[0m";
301 }
302 #endif
303
304 /* We shouldn't be using dynamic allocation here to
305 * minimize the hit in RT threads */
306 local_t = pa_utf8_to_locale(t);
307 if (!local_t)
308 fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, t, grey, pa_strempty(bt), suffix);
309 else {
310 fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, local_t, grey, pa_strempty(bt), suffix);
311 pa_xfree(local_t);
312 }
313
314 break;
315 }
316
317 #ifdef HAVE_SYSLOG_H
318 case PA_LOG_SYSLOG: {
319 char *local_t;
320
321 openlog(log_ident_local ? log_ident_local : "???", LOG_PID, LOG_USER);
322
323 local_t = pa_utf8_to_locale(t);
324 if (!local_t)
325 syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
326 else {
327 syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, local_t, pa_strempty(bt));
328 pa_xfree(local_t);
329 }
330
331 closelog();
332 break;
333 }
334 #endif
335
336 case PA_LOG_USER: {
337 char x[1024];
338
339 pa_snprintf(x, sizeof(x), "%s%s%s", timestamp, location, t);
340 user_log_func(level, x);
341
342 break;
343 }
344
345 case PA_LOG_NULL:
346 default:
347 break;
348 }
349 }
350
351 errno = saved_errno;
352 }
353
354 void pa_log_level_meta(
355 pa_log_level_t level,
356 const char*file,
357 int line,
358 const char *func,
359 const char *format, ...) {
360
361 va_list ap;
362 va_start(ap, format);
363 pa_log_levelv_meta(level, file, line, func, format, ap);
364 va_end(ap);
365 }
366
367 void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {
368 pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
369 }
370
371 void pa_log_level(pa_log_level_t level, const char *format, ...) {
372 va_list ap;
373
374 va_start(ap, format);
375 pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
376 va_end(ap);
377 }