2 This file is part of PulseAudio.
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
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.
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.
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
33 #ifdef HAVE_EXECINFO_H
41 #include <pulse/utf8.h>
42 #include <pulse/xmalloc.h>
43 #include <pulse/util.h>
44 #include <pulse/timeval.h>
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>
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"
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;
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
81 static const char level_to_char[] = {
84 [PA_LOG_NOTICE] = 'N',
89 void pa_log_set_ident(const char *p) {
92 if (!(ident = pa_utf8_to_locale(p)))
93 ident = pa_ascii_filter(p);
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())
105 void pa_log_set_level(pa_log_level_t l) {
106 pa_assert(l < PA_LOG_LEVEL_MAX);
111 void pa_log_set_target(pa_log_target_t t) {
112 pa_assert(t < PA_LOG_TARGET_MAX);
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)));
120 if (merge == PA_LOG_SET)
122 else if (merge == PA_LOG_UNSET)
128 void pa_log_set_show_backtrace(unsigned nlevels) {
129 show_backtrace = nlevels;
132 void pa_log_set_skip_backtrace(unsigned nlevels) {
133 skip_backtrace = nlevels;
136 #ifdef HAVE_EXECINFO_H
138 static char* get_backtrace(unsigned show_nframes) {
141 char **symbols, *e, *r;
145 pa_assert(show_nframes > 0);
147 n_frames = backtrace(trace, PA_ELEMENTSOF(trace));
152 symbols = backtrace_symbols(trace, n_frames);
158 n = PA_MIN((unsigned) n_frames, s + show_nframes);
162 for (j = s; j < n; j++) {
165 a += strlen(pa_path_get_filename(symbols[j]));
168 r = pa_xnew(char, a);
173 for (j = s; j < n; j++) {
181 sym = pa_path_get_filename(symbols[j]);
196 static void init_defaults(void) {
201 if (pa_get_binary_name(binary, sizeof(binary)))
202 pa_log_set_ident(binary);
205 if (getenv(ENV_LOG_SYSLOG)) {
206 target_override = PA_LOG_SYSLOG;
207 target_override_set = TRUE;
210 if ((e = getenv(ENV_LOG_LEVEL))) {
211 maximum_level_override = (pa_log_level_t) atoi(e);
213 if (maximum_level_override >= PA_LOG_LEVEL_MAX)
214 maximum_level_override = PA_LOG_LEVEL_MAX-1;
217 if (getenv(ENV_LOG_COLORS))
218 flags_override |= PA_LOG_COLORS;
220 if (getenv(ENV_LOG_PRINT_TIME))
221 flags_override |= PA_LOG_PRINT_TIME;
223 if (getenv(ENV_LOG_PRINT_FILE))
224 flags_override |= PA_LOG_PRINT_FILE;
226 if (getenv(ENV_LOG_PRINT_META))
227 flags_override |= PA_LOG_PRINT_META;
229 if (getenv(ENV_LOG_PRINT_LEVEL))
230 flags_override |= PA_LOG_PRINT_LEVEL;
232 if ((e = getenv(ENV_LOG_BACKTRACE))) {
233 show_backtrace_override = (unsigned) atoi(e);
235 if (show_backtrace_override <= 0)
236 show_backtrace_override = 0;
239 if ((e = getenv(ENV_LOG_BACKTRACE_SKIP))) {
240 skip_backtrace = (unsigned) atoi(e);
242 if (skip_backtrace <= 0)
247 void pa_log_levelv_meta(
248 pa_log_level_t level,
256 int saved_errno = errno;
258 pa_log_target_t _target;
259 pa_log_level_t _maximum_level;
260 unsigned _show_backtrace;
261 pa_log_flags_t _flags;
263 /* We don't use dynamic memory allocation here to minimize the hit
265 char text[4096], location[128], timestamp[32];
267 pa_assert(level < PA_LOG_LEVEL_MAX);
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;
279 if (PA_LIKELY(level > _maximum_level)) {
284 pa_vsnprintf(text, sizeof(text), format, ap);
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))
289 pa_snprintf(location, sizeof(location), "%s: ", pa_path_get_filename(file));
293 if (_flags & PA_LOG_PRINT_TIME) {
294 static pa_usec_t start, last;
297 u = pa_rtclock_usec();
307 /* This is not thread safe, but this is a debugging tool only
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));
320 #ifdef HAVE_EXECINFO_H
321 if (_show_backtrace > 0)
322 bt = get_backtrace(_show_backtrace);
325 if (!pa_utf8_valid(text))
326 pa_log_level(level, __FILE__": invalid UTF-8 string following below:");
328 for (t = text; t; t = n) {
329 if ((n = strchr(t, '\n'))) {
334 /* We ignore strings only made out of whitespace */
335 if (t[strspn(t, "\t ")] == 0)
340 case PA_LOG_STDERR: {
341 const char *prefix = "", *suffix = "", *grey = "";
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)
355 if (grey[0] || prefix[0])
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)))
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);
368 fprintf(stderr, "%s%s%s%s%s%s%s\n", timestamp, location, prefix, t, grey, pa_strempty(bt), suffix);
376 case PA_LOG_SYSLOG: {
379 openlog(ident, LOG_PID, LOG_USER);
381 if ((local_t = pa_utf8_to_locale(t)))
384 syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
401 void pa_log_level_meta(
402 pa_log_level_t level,
406 const char *format, ...) {
409 va_start(ap, format);
410 pa_log_levelv_meta(level, file, line, func, format, ap);
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);
418 void pa_log_level(pa_log_level_t level, const char *format, ...) {
421 va_start(ap, format);
422 pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
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);
430 return pa_ratelimit_test(&ratelimit);