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
35 #ifdef HAVE_EXECINFO_H
43 #ifdef HAVE_SYSTEMD_JOURNAL
44 #include <systemd/sd-journal.h>
47 #include <pulse/gccmacro.h>
48 #include <pulse/rtclock.h>
49 #include <pulse/utf8.h>
50 #include <pulse/xmalloc.h>
51 #include <pulse/util.h>
52 #include <pulse/timeval.h>
54 #include <pulsecore/macro.h>
55 #include <pulsecore/core-util.h>
56 #include <pulsecore/core-error.h>
57 #include <pulsecore/once.h>
58 #include <pulsecore/ratelimit.h>
59 #include <pulsecore/thread.h>
60 #include <pulsecore/i18n.h>
66 #define DLOG_TAG "PULSEAUDIO"
68 #define COLOR_BLACK 30
70 #define COLOR_GREEN 32
72 #define COLOR_MAGENTA 35
74 #define COLOR_WHITE 97
75 #define COLOR_B_GRAY 100
76 #define COLOR_B_RED 101
77 #define COLOR_B_GREEN 102
78 #define COLOR_B_YELLOW 103
79 #define COLOR_B_BLUE 104
80 #define COLOR_B_MAGENTA 105
81 #define COLOR_B_CYAN 106
82 #define COLOR_REVERSE 7
85 #define ENV_LOG_SYSLOG "PULSE_LOG_SYSLOG"
86 #define ENV_LOG_LEVEL "PULSE_LOG"
87 #define ENV_LOG_COLORS "PULSE_LOG_COLORS"
88 #define ENV_LOG_PRINT_TIME "PULSE_LOG_TIME"
89 #define ENV_LOG_PRINT_FILE "PULSE_LOG_FILE"
90 #define ENV_LOG_PRINT_META "PULSE_LOG_META"
91 #define ENV_LOG_PRINT_LEVEL "PULSE_LOG_LEVEL"
92 #define ENV_LOG_BACKTRACE "PULSE_LOG_BACKTRACE"
93 #define ENV_LOG_BACKTRACE_SKIP "PULSE_LOG_BACKTRACE_SKIP"
94 #define ENV_LOG_NO_RATELIMIT "PULSE_LOG_NO_RATE_LIMIT"
95 #define LOG_MAX_SUFFIX_NUMBER 99
97 static char *ident = NULL; /* in local charset format */
98 static pa_log_target target = { PA_LOG_STDERR, NULL };
99 static pa_log_target_type_t target_override;
100 static bool target_override_set = false;
101 static pa_log_level_t maximum_level = PA_LOG_ERROR, maximum_level_override = PA_LOG_ERROR;
102 static unsigned show_backtrace = 0, show_backtrace_override = 0, skip_backtrace = 0;
103 static pa_log_flags_t flags = 0, flags_override = 0;
104 static bool no_rate_limit = false;
105 static int log_fd = -1;
106 static int write_type = 0;
109 static const int level_to_syslog[] = {
110 [PA_LOG_ERROR] = LOG_ERR,
111 [PA_LOG_WARN] = LOG_WARNING,
112 [PA_LOG_NOTICE] = LOG_NOTICE,
113 [PA_LOG_INFO] = LOG_INFO,
114 [PA_LOG_DEBUG] = LOG_DEBUG
118 /* These are actually equivalent to the syslog ones
119 * but we don't want to depend on syslog.h */
120 #ifdef HAVE_SYSTEMD_JOURNAL
121 static const int level_to_journal[] = {
130 static const char level_to_char[] = {
131 [PA_LOG_ERROR] = 'E',
133 [PA_LOG_NOTICE] = 'N',
138 void pa_log_set_ident(const char *p) {
141 if (!(ident = pa_utf8_to_locale(p)))
142 ident = pa_ascii_filter(p);
145 /* To make valgrind shut up. */
146 static void ident_destructor(void) PA_GCC_DESTRUCTOR;
147 static void ident_destructor(void) {
148 if (!pa_in_valgrind())
154 void pa_log_set_level(pa_log_level_t l) {
155 pa_assert(l < PA_LOG_LEVEL_MAX);
160 int pa_log_set_target(pa_log_target *t) {
169 #ifdef HAVE_SYSTEMD_JOURNAL
174 case PA_LOG_DLOG_COLOR:
179 if ((fd = pa_open_cloexec(t->file, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
180 pa_log(_("Failed to open target file '%s'."), t->file);
184 case PA_LOG_NEWFILE: {
189 file_path = pa_sprintf_malloc("%s.xx", t->file);
190 p = file_path + strlen(t->file);
192 for (version = 0; version <= LOG_MAX_SUFFIX_NUMBER; version++) {
193 memset(p, 0, 3); /* Overwrite the ".xx" part in file_path with zero bytes. */
196 pa_snprintf(p, 4, ".%u", version); /* Why 4? ".xx" + termitating zero byte. */
198 if ((fd = pa_open_cloexec(file_path, O_WRONLY | O_TRUNC | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) >= 0)
202 if (version > LOG_MAX_SUFFIX_NUMBER) {
203 pa_log(_("Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."),
204 t->file, t->file, t->file, t->file, LOG_MAX_SUFFIX_NUMBER);
208 pa_log_debug("Opened target file %s\n", file_path);
215 target.type = t->type;
216 pa_xfree(target.file);
217 target.file = pa_xstrdup(t->file);
228 void pa_log_set_flags(pa_log_flags_t _flags, pa_log_merge_t merge) {
229 pa_assert(!(_flags & ~(PA_LOG_COLORS|PA_LOG_PRINT_TIME|PA_LOG_PRINT_FILE|PA_LOG_PRINT_META|PA_LOG_PRINT_LEVEL)));
231 if (merge == PA_LOG_SET)
233 else if (merge == PA_LOG_UNSET)
239 void pa_log_set_show_backtrace(unsigned nlevels) {
240 show_backtrace = nlevels;
243 void pa_log_set_skip_backtrace(unsigned nlevels) {
244 skip_backtrace = nlevels;
247 #ifdef HAVE_EXECINFO_H
249 static char* get_backtrace(unsigned show_nframes) {
252 char **symbols, *e, *r;
256 pa_assert(show_nframes > 0);
258 n_frames = backtrace(trace, PA_ELEMENTSOF(trace));
263 symbols = backtrace_symbols(trace, n_frames);
269 n = PA_MIN((unsigned) n_frames, s + show_nframes);
273 for (j = s; j < n; j++) {
276 a += strlen(pa_path_get_filename(symbols[j]));
279 r = pa_xnew(char, a);
284 for (j = s; j < n; j++) {
292 sym = pa_path_get_filename(symbols[j]);
307 static void init_defaults(void) {
314 if (pa_get_binary_name(binary, sizeof(binary)))
315 pa_log_set_ident(binary);
318 if (getenv(ENV_LOG_SYSLOG)) {
319 target_override = PA_LOG_SYSLOG;
320 target_override_set = true;
323 if ((e = getenv(ENV_LOG_LEVEL))) {
324 maximum_level_override = (pa_log_level_t) atoi(e);
326 if (maximum_level_override >= PA_LOG_LEVEL_MAX)
327 maximum_level_override = PA_LOG_LEVEL_MAX-1;
330 if (getenv(ENV_LOG_COLORS))
331 flags_override |= PA_LOG_COLORS;
333 if (getenv(ENV_LOG_PRINT_TIME))
334 flags_override |= PA_LOG_PRINT_TIME;
336 if (getenv(ENV_LOG_PRINT_FILE))
337 flags_override |= PA_LOG_PRINT_FILE;
339 if (getenv(ENV_LOG_PRINT_META))
340 flags_override |= PA_LOG_PRINT_META;
342 if (getenv(ENV_LOG_PRINT_LEVEL))
343 flags_override |= PA_LOG_PRINT_LEVEL;
345 if ((e = getenv(ENV_LOG_BACKTRACE))) {
346 show_backtrace_override = (unsigned) atoi(e);
348 if (show_backtrace_override <= 0)
349 show_backtrace_override = 0;
352 if ((e = getenv(ENV_LOG_BACKTRACE_SKIP))) {
353 skip_backtrace = (unsigned) atoi(e);
355 if (skip_backtrace <= 0)
359 if (getenv(ENV_LOG_NO_RATELIMIT))
360 no_rate_limit = true;
366 static void log_syslog(pa_log_level_t level, char *t, char *timestamp, char *location, char *bt) {
369 openlog(ident, LOG_PID, LOG_USER);
371 if ((local_t = pa_utf8_to_locale(t)))
374 syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
379 void pa_log_levelv_meta(
380 pa_log_level_t level,
388 int saved_errno = errno;
390 pa_log_target_type_t _target;
391 pa_log_level_t _maximum_level;
392 unsigned _show_backtrace;
393 pa_log_flags_t _flags;
395 /* We don't use dynamic memory allocation here to minimize the hit
397 char text[16*1024], location[128], timestamp[32];
399 pa_assert(level < PA_LOG_LEVEL_MAX);
404 _target = target_override_set ? target_override : target.type;
405 _maximum_level = PA_MAX(maximum_level, maximum_level_override);
406 _show_backtrace = PA_MAX(show_backtrace, show_backtrace_override);
407 _flags = flags | flags_override;
409 if (PA_LIKELY(level > _maximum_level)) {
414 pa_vsnprintf(text, sizeof(text), format, ap);
416 if ((_flags & PA_LOG_PRINT_META) && file && line > 0 && func)
417 pa_snprintf(location, sizeof(location), "[%s][%s:%i %s()] ",
418 pa_strnull(pa_thread_get_name(pa_thread_self())), file, line, func);
419 else if ((_flags & (PA_LOG_PRINT_META|PA_LOG_PRINT_FILE)) && file)
420 pa_snprintf(location, sizeof(location), "[%s] %s: ",
421 pa_strnull(pa_thread_get_name(pa_thread_self())), pa_path_get_filename(file));
425 if (_flags & PA_LOG_PRINT_TIME) {
426 static pa_usec_t start, last;
429 u = pa_rtclock_now();
439 /* This is not thread safe, but this is a debugging tool only
443 pa_snprintf(timestamp, sizeof(timestamp), "(%4llu.%03llu|%4llu.%03llu) ",
444 (unsigned long long) (a / PA_USEC_PER_SEC),
445 (unsigned long long) (((a / PA_USEC_PER_MSEC)) % 1000),
446 (unsigned long long) (r / PA_USEC_PER_SEC),
447 (unsigned long long) (((r / PA_USEC_PER_MSEC)) % 1000));
452 #ifdef HAVE_EXECINFO_H
453 if (_show_backtrace > 0)
454 bt = get_backtrace(_show_backtrace);
457 if (!pa_utf8_valid(text))
458 pa_logl(level, "Invalid UTF-8 string following below:");
460 for (t = text; t; t = n) {
461 if ((n = strchr(t, '\n'))) {
466 /* We ignore strings only made out of whitespace */
467 if (t[strspn(t, "\t ")] == 0)
472 case PA_LOG_STDERR: {
473 const char *prefix = "", *suffix = "", *grey = "";
477 /* Yes indeed. Useless, but fun! */
478 if ((_flags & PA_LOG_COLORS) && isatty(STDERR_FILENO)) {
479 if (level <= PA_LOG_ERROR)
480 prefix = "\x1B[1;31m";
481 else if (level <= PA_LOG_WARN)
487 if (grey[0] || prefix[0])
492 /* We shouldn't be using dynamic allocation here to
493 * minimize the hit in RT threads */
494 if ((local_t = pa_utf8_to_locale(t)))
497 if (_flags & PA_LOG_PRINT_LEVEL)
498 fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, t, grey, pa_strempty(bt), suffix);
500 fprintf(stderr, "%s%s%s%s%s%s%s\n", timestamp, location, prefix, t, grey, pa_strempty(bt), suffix);
512 log_syslog(level, t, timestamp, location, bt);
516 #ifdef HAVE_SYSTEMD_JOURNAL
518 if (sd_journal_send("MESSAGE=%s", t,
519 "PRIORITY=%i", level_to_journal[level],
520 "CODE_FILE=%s", file,
521 "CODE_FUNC=%s", func,
522 "CODE_LINE=%d", line,
525 pa_log_target new_target = { .type = PA_LOG_SYSLOG, .file = NULL };
527 syslog(level_to_syslog[PA_LOG_ERROR], "%s%s%s", timestamp, __FILE__,
528 "Error writing logs to the journal. Redirect log messages to syslog.");
529 log_syslog(level, t, timestamp, location, bt);
531 pa_log_target new_target = { .type = PA_LOG_STDERR, .file = NULL };
534 fprintf(stderr, "%s\n", "Error writing logs to the journal. Redirect log messages to console.");
535 fprintf(stderr, "%s %s\n", metadata, t);
537 pa_log_set_target(&new_target);
543 case PA_LOG_NEWFILE: {
546 if ((local_t = pa_utf8_to_locale(t)))
552 if (_flags & PA_LOG_PRINT_LEVEL)
553 pa_snprintf(metadata, sizeof(metadata), "%s%c: %s", timestamp, level_to_char[level], location);
555 pa_snprintf(metadata, sizeof(metadata), "%s%s", timestamp, location);
557 if ((pa_write(log_fd, metadata, strlen(metadata), &write_type) < 0)
558 || (pa_write(log_fd, t, strlen(t), &write_type) < 0)
559 || (bt && pa_write(log_fd, bt, strlen(bt), &write_type) < 0)
560 || (pa_write(log_fd, "\n", 1, &write_type) < 0)) {
561 pa_log_target new_target = { .type = PA_LOG_STDERR, .file = NULL };
563 fprintf(stderr, "%s\n", "Error writing logs to a file descriptor. Redirect log messages to console.");
564 fprintf(stderr, "%s %s\n", metadata, t);
565 pa_log_set_target(&new_target);
578 openlog(ident, LOG_PID, LOG_USER);
580 if ((local_t = pa_utf8_to_locale(t)))
586 SLOG (LOG_DEBUG, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
589 case PA_LOG_NOTICE: /* no notice category in dlog, use info instead */
590 SLOG (LOG_INFO, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
593 SLOG (LOG_WARN, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
596 SLOG (LOG_ERROR, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
599 SLOG (LOG_DEBUG, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
607 case PA_LOG_DLOG_COLOR: {
610 openlog(ident, LOG_PID, LOG_USER);
612 if ((local_t = pa_utf8_to_locale(t)))
618 SLOG (LOG_DEBUG, DLOG_TAG, "\033[%dm%s%s%s%s\033[0m", COLOR_GREEN, timestamp, location, t, pa_strempty(bt));
621 case PA_LOG_NOTICE: /* no notice category in dlog, use info instead */
622 SLOG (LOG_INFO, DLOG_TAG, "\033[%dm%s%s%s%s\033[0m", COLOR_BLUE, timestamp, location, t, pa_strempty(bt));
625 SLOG (LOG_WARN, DLOG_TAG, "\033[%dm%s%s%s%s\033[0m", COLOR_MAGENTA, timestamp, location, t, pa_strempty(bt));
628 SLOG (LOG_ERROR, DLOG_TAG, "\033[%dm%s%s%s%s\033[0m", COLOR_RED, timestamp, location, t, pa_strempty(bt));
631 SLOG (LOG_DEBUG, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
651 void pa_log_level_meta(
652 pa_log_level_t level,
656 const char *format, ...) {
659 va_start(ap, format);
660 pa_log_levelv_meta(level, file, line, func, format, ap);
664 void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {
665 pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
668 void pa_log_level(pa_log_level_t level, const char *format, ...) {
671 va_start(ap, format);
672 pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
676 bool pa_log_ratelimit(pa_log_level_t level) {
677 /* Not more than 10 messages every 5s */
678 static PA_DEFINE_RATELIMIT(ratelimit, 5 * PA_USEC_PER_SEC, 10);
685 return pa_ratelimit_test(&ratelimit, level);
688 pa_log_target *pa_log_target_new(pa_log_target_type_t type, const char *file) {
689 pa_log_target *t = NULL;
691 t = pa_xnew(pa_log_target, 1);
694 t->file = pa_xstrdup(file);
699 void pa_log_target_free(pa_log_target *t) {
706 pa_log_target *pa_log_parse_target(const char *string) {
707 pa_log_target *t = NULL;
711 if (pa_streq(string, "stderr"))
712 t = pa_log_target_new(PA_LOG_STDERR, NULL);
713 else if (pa_streq(string, "syslog"))
714 t = pa_log_target_new(PA_LOG_SYSLOG, NULL);
715 #ifdef HAVE_SYSTEMD_JOURNAL
716 else if (pa_streq(string, "journal"))
717 t = pa_log_target_new(PA_LOG_JOURNAL, NULL);
719 else if (pa_streq(string, "null"))
720 t = pa_log_target_new(PA_LOG_NULL, NULL);
721 else if (pa_startswith(string, "file:"))
722 t = pa_log_target_new(PA_LOG_FILE, string + 5);
723 else if (pa_startswith(string, "newfile:"))
724 t = pa_log_target_new(PA_LOG_NEWFILE, string + 8);
726 else if (pa_streq(string, "dlog"))
727 t = pa_log_target_new(PA_LOG_DLOG, NULL);
728 else if (pa_streq(string, "dlog-color"))
729 t = pa_log_target_new(PA_LOG_DLOG_COLOR, NULL);
732 pa_log(_("Invalid log target."));
737 char *pa_log_target_to_string(const pa_log_target *t) {
744 string = pa_xstrdup("stderr");
747 string = pa_xstrdup("syslog");
749 #ifdef HAVE_SYSTEMD_JOURNAL
751 string = pa_xstrdup("journal");
756 string = pa_xstrdup("dlog");
758 case PA_LOG_DLOG_COLOR:
759 string = pa_xstrdup("dlog-color");
763 string = pa_xstrdup("null");
766 string = pa_sprintf_malloc("file:%s", t->file);
769 string = pa_sprintf_malloc("newfile:%s", t->file);