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
34 #ifdef HAVE_EXECINFO_H
42 #include <pulse/gccmacro.h>
43 #include <pulse/rtclock.h>
44 #include <pulse/utf8.h>
45 #include <pulse/xmalloc.h>
46 #include <pulse/util.h>
47 #include <pulse/timeval.h>
49 #include <pulsecore/macro.h>
50 #include <pulsecore/core-util.h>
51 #include <pulsecore/core-error.h>
52 #include <pulsecore/once.h>
53 #include <pulsecore/ratelimit.h>
54 #include <pulsecore/thread.h>
55 #include <pulsecore/i18n.h>
59 #define ENV_LOG_SYSLOG "PULSE_LOG_SYSLOG"
60 #define ENV_LOG_LEVEL "PULSE_LOG"
61 #define ENV_LOG_COLORS "PULSE_LOG_COLORS"
62 #define ENV_LOG_PRINT_TIME "PULSE_LOG_TIME"
63 #define ENV_LOG_PRINT_FILE "PULSE_LOG_FILE"
64 #define ENV_LOG_PRINT_META "PULSE_LOG_META"
65 #define ENV_LOG_PRINT_LEVEL "PULSE_LOG_LEVEL"
66 #define ENV_LOG_BACKTRACE "PULSE_LOG_BACKTRACE"
67 #define ENV_LOG_BACKTRACE_SKIP "PULSE_LOG_BACKTRACE_SKIP"
68 #define ENV_LOG_NO_RATELIMIT "PULSE_LOG_NO_RATE_LIMIT"
69 #define LOG_MAX_SUFFIX_NUMBER 99
71 static char *ident = NULL; /* in local charset format */
72 static pa_log_target target = { PA_LOG_STDERR, NULL };
73 static pa_log_target_type_t target_override;
74 static bool target_override_set = false;
75 static pa_log_level_t maximum_level = PA_LOG_ERROR, maximum_level_override = PA_LOG_ERROR;
76 static unsigned show_backtrace = 0, show_backtrace_override = 0, skip_backtrace = 0;
77 static pa_log_flags_t flags = 0, flags_override = 0;
78 static bool no_rate_limit = false;
79 static int log_fd = -1;
80 static int write_type = 0;
83 static const int level_to_syslog[] = {
84 [PA_LOG_ERROR] = LOG_ERR,
85 [PA_LOG_WARN] = LOG_WARNING,
86 [PA_LOG_NOTICE] = LOG_NOTICE,
87 [PA_LOG_INFO] = LOG_INFO,
88 [PA_LOG_DEBUG] = LOG_DEBUG
92 static const char level_to_char[] = {
95 [PA_LOG_NOTICE] = 'N',
100 void pa_log_set_ident(const char *p) {
103 if (!(ident = pa_utf8_to_locale(p)))
104 ident = pa_ascii_filter(p);
107 /* To make valgrind shut up. */
108 static void ident_destructor(void) PA_GCC_DESTRUCTOR;
109 static void ident_destructor(void) {
110 if (!pa_in_valgrind())
116 void pa_log_set_level(pa_log_level_t l) {
117 pa_assert(l < PA_LOG_LEVEL_MAX);
122 int pa_log_set_target(pa_log_target *t) {
134 if ((fd = pa_open_cloexec(t->file, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
135 pa_log(_("Failed to open target file '%s'."), t->file);
139 case PA_LOG_NEWFILE: {
144 file_path = pa_sprintf_malloc("%s.xx", t->file);
145 p = file_path + strlen(t->file);
147 for (version = 0; version <= LOG_MAX_SUFFIX_NUMBER; version++) {
148 memset(p, 0, 3); /* Overwrite the ".xx" part in file_path with zero bytes. */
151 pa_snprintf(p, 4, ".%u", version); /* Why 4? ".xx" + termitating zero byte. */
153 if ((fd = pa_open_cloexec(file_path, O_WRONLY | O_TRUNC | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) >= 0)
157 if (version > LOG_MAX_SUFFIX_NUMBER) {
158 pa_log(_("Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."),
159 t->file, t->file, t->file, t->file, LOG_MAX_SUFFIX_NUMBER);
163 pa_log_debug("Opened target file %s\n", file_path);
170 target.type = t->type;
171 pa_xfree(target.file);
172 target.file = pa_xstrdup(t->file);
183 void pa_log_set_flags(pa_log_flags_t _flags, pa_log_merge_t merge) {
184 pa_assert(!(_flags & ~(PA_LOG_COLORS|PA_LOG_PRINT_TIME|PA_LOG_PRINT_FILE|PA_LOG_PRINT_META|PA_LOG_PRINT_LEVEL)));
186 if (merge == PA_LOG_SET)
188 else if (merge == PA_LOG_UNSET)
194 void pa_log_set_show_backtrace(unsigned nlevels) {
195 show_backtrace = nlevels;
198 void pa_log_set_skip_backtrace(unsigned nlevels) {
199 skip_backtrace = nlevels;
202 #ifdef HAVE_EXECINFO_H
204 static char* get_backtrace(unsigned show_nframes) {
207 char **symbols, *e, *r;
211 pa_assert(show_nframes > 0);
213 n_frames = backtrace(trace, PA_ELEMENTSOF(trace));
218 symbols = backtrace_symbols(trace, n_frames);
224 n = PA_MIN((unsigned) n_frames, s + show_nframes);
228 for (j = s; j < n; j++) {
231 a += strlen(pa_path_get_filename(symbols[j]));
234 r = pa_xnew(char, a);
239 for (j = s; j < n; j++) {
247 sym = pa_path_get_filename(symbols[j]);
262 static void init_defaults(void) {
269 if (pa_get_binary_name(binary, sizeof(binary)))
270 pa_log_set_ident(binary);
273 if (getenv(ENV_LOG_SYSLOG)) {
274 target_override = PA_LOG_SYSLOG;
275 target_override_set = true;
278 if ((e = getenv(ENV_LOG_LEVEL))) {
279 maximum_level_override = (pa_log_level_t) atoi(e);
281 if (maximum_level_override >= PA_LOG_LEVEL_MAX)
282 maximum_level_override = PA_LOG_LEVEL_MAX-1;
285 if (getenv(ENV_LOG_COLORS))
286 flags_override |= PA_LOG_COLORS;
288 if (getenv(ENV_LOG_PRINT_TIME))
289 flags_override |= PA_LOG_PRINT_TIME;
291 if (getenv(ENV_LOG_PRINT_FILE))
292 flags_override |= PA_LOG_PRINT_FILE;
294 if (getenv(ENV_LOG_PRINT_META))
295 flags_override |= PA_LOG_PRINT_META;
297 if (getenv(ENV_LOG_PRINT_LEVEL))
298 flags_override |= PA_LOG_PRINT_LEVEL;
300 if ((e = getenv(ENV_LOG_BACKTRACE))) {
301 show_backtrace_override = (unsigned) atoi(e);
303 if (show_backtrace_override <= 0)
304 show_backtrace_override = 0;
307 if ((e = getenv(ENV_LOG_BACKTRACE_SKIP))) {
308 skip_backtrace = (unsigned) atoi(e);
310 if (skip_backtrace <= 0)
314 if (getenv(ENV_LOG_NO_RATELIMIT))
315 no_rate_limit = true;
320 void pa_log_levelv_meta(
321 pa_log_level_t level,
329 int saved_errno = errno;
331 pa_log_target_type_t _target;
332 pa_log_level_t _maximum_level;
333 unsigned _show_backtrace;
334 pa_log_flags_t _flags;
336 /* We don't use dynamic memory allocation here to minimize the hit
338 char text[16*1024], location[128], timestamp[32];
340 pa_assert(level < PA_LOG_LEVEL_MAX);
345 _target = target_override_set ? target_override : target.type;
346 _maximum_level = PA_MAX(maximum_level, maximum_level_override);
347 _show_backtrace = PA_MAX(show_backtrace, show_backtrace_override);
348 _flags = flags | flags_override;
350 if (PA_LIKELY(level > _maximum_level)) {
355 pa_vsnprintf(text, sizeof(text), format, ap);
357 if ((_flags & PA_LOG_PRINT_META) && file && line > 0 && func)
358 pa_snprintf(location, sizeof(location), "[%s][%s:%i %s()] ",
359 pa_strnull(pa_thread_get_name(pa_thread_self())), file, line, func);
360 else if ((_flags & (PA_LOG_PRINT_META|PA_LOG_PRINT_FILE)) && file)
361 pa_snprintf(location, sizeof(location), "[%s] %s: ",
362 pa_strnull(pa_thread_get_name(pa_thread_self())), pa_path_get_filename(file));
366 if (_flags & PA_LOG_PRINT_TIME) {
367 static pa_usec_t start, last;
370 u = pa_rtclock_now();
380 /* This is not thread safe, but this is a debugging tool only
384 pa_snprintf(timestamp, sizeof(timestamp), "(%4llu.%03llu|%4llu.%03llu) ",
385 (unsigned long long) (a / PA_USEC_PER_SEC),
386 (unsigned long long) (((a / PA_USEC_PER_MSEC)) % 1000),
387 (unsigned long long) (r / PA_USEC_PER_SEC),
388 (unsigned long long) (((r / PA_USEC_PER_MSEC)) % 1000));
393 #ifdef HAVE_EXECINFO_H
394 if (_show_backtrace > 0)
395 bt = get_backtrace(_show_backtrace);
398 if (!pa_utf8_valid(text))
399 pa_logl(level, "Invalid UTF-8 string following below:");
401 for (t = text; t; t = n) {
402 if ((n = strchr(t, '\n'))) {
407 /* We ignore strings only made out of whitespace */
408 if (t[strspn(t, "\t ")] == 0)
413 case PA_LOG_STDERR: {
414 const char *prefix = "", *suffix = "", *grey = "";
418 /* Yes indeed. Useless, but fun! */
419 if ((_flags & PA_LOG_COLORS) && isatty(STDERR_FILENO)) {
420 if (level <= PA_LOG_ERROR)
421 prefix = "\x1B[1;31m";
422 else if (level <= PA_LOG_WARN)
428 if (grey[0] || prefix[0])
433 /* We shouldn't be using dynamic allocation here to
434 * minimize the hit in RT threads */
435 if ((local_t = pa_utf8_to_locale(t)))
438 if (_flags & PA_LOG_PRINT_LEVEL)
439 fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, t, grey, pa_strempty(bt), suffix);
441 fprintf(stderr, "%s%s%s%s%s%s%s\n", timestamp, location, prefix, t, grey, pa_strempty(bt), suffix);
452 case PA_LOG_SYSLOG: {
455 openlog(ident, LOG_PID, LOG_USER);
457 if ((local_t = pa_utf8_to_locale(t)))
460 syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
468 case PA_LOG_NEWFILE: {
472 pa_snprintf(metadata, sizeof(metadata), "\n%c %s %s", level_to_char[level], timestamp, location);
474 if ((pa_write(log_fd, metadata, strlen(metadata), &write_type) < 0) || (pa_write(log_fd, t, strlen(t), &write_type) < 0)) {
475 pa_log_target new_target = { .type = PA_LOG_STDERR, .file = NULL };
477 fprintf(stderr, "%s\n", "Error writing logs to a file descriptor. Redirect log messages to console.");
478 fprintf(stderr, "%s %s\n", metadata, t);
479 pa_log_set_target(&new_target);
495 void pa_log_level_meta(
496 pa_log_level_t level,
500 const char *format, ...) {
503 va_start(ap, format);
504 pa_log_levelv_meta(level, file, line, func, format, ap);
508 void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {
509 pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
512 void pa_log_level(pa_log_level_t level, const char *format, ...) {
515 va_start(ap, format);
516 pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
520 bool pa_log_ratelimit(pa_log_level_t level) {
521 /* Not more than 10 messages every 5s */
522 static PA_DEFINE_RATELIMIT(ratelimit, 5 * PA_USEC_PER_SEC, 10);
529 return pa_ratelimit_test(&ratelimit, level);
532 pa_log_target *pa_log_target_new(pa_log_target_type_t type, const char *file) {
533 pa_log_target *t = NULL;
535 t = pa_xnew(pa_log_target, 1);
538 t->file = pa_xstrdup(file);
543 void pa_log_target_free(pa_log_target *t) {
550 pa_log_target *pa_log_parse_target(const char *string) {
551 pa_log_target *t = NULL;
555 if (pa_streq(string, "stderr"))
556 t = pa_log_target_new(PA_LOG_STDERR, NULL);
557 else if (pa_streq(string, "syslog"))
558 t = pa_log_target_new(PA_LOG_SYSLOG, NULL);
559 else if (pa_streq(string, "null"))
560 t = pa_log_target_new(PA_LOG_NULL, NULL);
561 else if (pa_startswith(string, "file:"))
562 t = pa_log_target_new(PA_LOG_FILE, string + 5);
563 else if (pa_startswith(string, "newfile:"))
564 t = pa_log_target_new(PA_LOG_NEWFILE, string + 8);
566 pa_log(_("Invalid log target."));
571 char *pa_log_target_to_string(const pa_log_target *t) {
578 string = pa_xstrdup("stderr");
581 string = pa_xstrdup("syslog");
584 string = pa_xstrdup("null");
587 string = pa_sprintf_malloc("file:%s", t->file);
590 string = pa_sprintf_malloc("newfile:%s", t->file);