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
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>
64 #define ENV_LOG_SYSLOG "PULSE_LOG_SYSLOG"
65 #define ENV_LOG_LEVEL "PULSE_LOG"
66 #define ENV_LOG_COLORS "PULSE_LOG_COLORS"
67 #define ENV_LOG_PRINT_TIME "PULSE_LOG_TIME"
68 #define ENV_LOG_PRINT_FILE "PULSE_LOG_FILE"
69 #define ENV_LOG_PRINT_META "PULSE_LOG_META"
70 #define ENV_LOG_PRINT_LEVEL "PULSE_LOG_LEVEL"
71 #define ENV_LOG_BACKTRACE "PULSE_LOG_BACKTRACE"
72 #define ENV_LOG_BACKTRACE_SKIP "PULSE_LOG_BACKTRACE_SKIP"
73 #define ENV_LOG_NO_RATELIMIT "PULSE_LOG_NO_RATE_LIMIT"
74 #define LOG_MAX_SUFFIX_NUMBER 99
76 static char *ident
= NULL
; /* in local charset format */
77 static pa_log_target target
= { PA_LOG_STDERR
, NULL
};
78 static pa_log_target_type_t target_override
;
79 static bool target_override_set
= false;
80 static pa_log_level_t maximum_level
= PA_LOG_ERROR
, maximum_level_override
= PA_LOG_ERROR
;
81 static unsigned show_backtrace
= 0, show_backtrace_override
= 0, skip_backtrace
= 0;
82 static pa_log_flags_t flags
= 0, flags_override
= 0;
83 static bool no_rate_limit
= false;
84 static int log_fd
= -1;
85 static int write_type
= 0;
88 static const int level_to_syslog
[] = {
89 [PA_LOG_ERROR
] = LOG_ERR
,
90 [PA_LOG_WARN
] = LOG_WARNING
,
91 [PA_LOG_NOTICE
] = LOG_NOTICE
,
92 [PA_LOG_INFO
] = LOG_INFO
,
93 [PA_LOG_DEBUG
] = LOG_DEBUG
97 /* These are actually equivalent to the syslog ones
98 * but we don't want to depend on syslog.h */
100 static const int level_to_journal
[] = {
109 static const char level_to_char
[] = {
110 [PA_LOG_ERROR
] = 'E',
112 [PA_LOG_NOTICE
] = 'N',
117 void pa_log_set_ident(const char *p
) {
120 if (!(ident
= pa_utf8_to_locale(p
)))
121 ident
= pa_ascii_filter(p
);
124 /* To make valgrind shut up. */
125 static void ident_destructor(void) PA_GCC_DESTRUCTOR
;
126 static void ident_destructor(void) {
127 if (!pa_in_valgrind())
133 void pa_log_set_level(pa_log_level_t l
) {
134 pa_assert(l
< PA_LOG_LEVEL_MAX
);
139 int pa_log_set_target(pa_log_target
*t
) {
154 if ((fd
= pa_open_cloexec(t
->file
, O_WRONLY
| O_TRUNC
| O_CREAT
, S_IRUSR
| S_IWUSR
)) < 0) {
155 pa_log(_("Failed to open target file '%s'."), t
->file
);
159 case PA_LOG_NEWFILE
: {
164 file_path
= pa_sprintf_malloc("%s.xx", t
->file
);
165 p
= file_path
+ strlen(t
->file
);
167 for (version
= 0; version
<= LOG_MAX_SUFFIX_NUMBER
; version
++) {
168 memset(p
, 0, 3); /* Overwrite the ".xx" part in file_path with zero bytes. */
171 pa_snprintf(p
, 4, ".%u", version
); /* Why 4? ".xx" + termitating zero byte. */
173 if ((fd
= pa_open_cloexec(file_path
, O_WRONLY
| O_TRUNC
| O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
)) >= 0)
177 if (version
> LOG_MAX_SUFFIX_NUMBER
) {
178 pa_log(_("Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."),
179 t
->file
, t
->file
, t
->file
, t
->file
, LOG_MAX_SUFFIX_NUMBER
);
183 pa_log_debug("Opened target file %s\n", file_path
);
190 target
.type
= t
->type
;
191 pa_xfree(target
.file
);
192 target
.file
= pa_xstrdup(t
->file
);
203 void pa_log_set_flags(pa_log_flags_t _flags
, pa_log_merge_t merge
) {
204 pa_assert(!(_flags
& ~(PA_LOG_COLORS
|PA_LOG_PRINT_TIME
|PA_LOG_PRINT_FILE
|PA_LOG_PRINT_META
|PA_LOG_PRINT_LEVEL
)));
206 if (merge
== PA_LOG_SET
)
208 else if (merge
== PA_LOG_UNSET
)
214 void pa_log_set_show_backtrace(unsigned nlevels
) {
215 show_backtrace
= nlevels
;
218 void pa_log_set_skip_backtrace(unsigned nlevels
) {
219 skip_backtrace
= nlevels
;
222 #ifdef HAVE_EXECINFO_H
224 static char* get_backtrace(unsigned show_nframes
) {
227 char **symbols
, *e
, *r
;
231 pa_assert(show_nframes
> 0);
233 n_frames
= backtrace(trace
, PA_ELEMENTSOF(trace
));
238 symbols
= backtrace_symbols(trace
, n_frames
);
244 n
= PA_MIN((unsigned) n_frames
, s
+ show_nframes
);
248 for (j
= s
; j
< n
; j
++) {
251 a
+= strlen(pa_path_get_filename(symbols
[j
]));
254 r
= pa_xnew(char, a
);
259 for (j
= s
; j
< n
; j
++) {
267 sym
= pa_path_get_filename(symbols
[j
]);
282 static void init_defaults(void) {
289 if (pa_get_binary_name(binary
, sizeof(binary
)))
290 pa_log_set_ident(binary
);
293 if (getenv(ENV_LOG_SYSLOG
)) {
294 target_override
= PA_LOG_SYSLOG
;
295 target_override_set
= true;
298 if ((e
= getenv(ENV_LOG_LEVEL
))) {
299 maximum_level_override
= (pa_log_level_t
) atoi(e
);
301 if (maximum_level_override
>= PA_LOG_LEVEL_MAX
)
302 maximum_level_override
= PA_LOG_LEVEL_MAX
-1;
305 if (getenv(ENV_LOG_COLORS
))
306 flags_override
|= PA_LOG_COLORS
;
308 if (getenv(ENV_LOG_PRINT_TIME
))
309 flags_override
|= PA_LOG_PRINT_TIME
;
311 if (getenv(ENV_LOG_PRINT_FILE
))
312 flags_override
|= PA_LOG_PRINT_FILE
;
314 if (getenv(ENV_LOG_PRINT_META
))
315 flags_override
|= PA_LOG_PRINT_META
;
317 if (getenv(ENV_LOG_PRINT_LEVEL
))
318 flags_override
|= PA_LOG_PRINT_LEVEL
;
320 if ((e
= getenv(ENV_LOG_BACKTRACE
))) {
321 show_backtrace_override
= (unsigned) atoi(e
);
323 if (show_backtrace_override
<= 0)
324 show_backtrace_override
= 0;
327 if ((e
= getenv(ENV_LOG_BACKTRACE_SKIP
))) {
328 skip_backtrace
= (unsigned) atoi(e
);
330 if (skip_backtrace
<= 0)
334 if (getenv(ENV_LOG_NO_RATELIMIT
))
335 no_rate_limit
= true;
341 static void log_syslog(pa_log_level_t level
, char *t
, char *timestamp
, char *location
, char *bt
) {
344 openlog(ident
, LOG_PID
, LOG_USER
);
346 if ((local_t
= pa_utf8_to_locale(t
)))
349 syslog(level_to_syslog
[level
], "%s%s%s%s", timestamp
, location
, t
, pa_strempty(bt
));
354 void pa_log_levelv_meta(
355 pa_log_level_t level
,
363 int saved_errno
= errno
;
365 pa_log_target_type_t _target
;
366 pa_log_level_t _maximum_level
;
367 unsigned _show_backtrace
;
368 pa_log_flags_t _flags
;
370 /* We don't use dynamic memory allocation here to minimize the hit
372 char text
[16*1024], location
[128], timestamp
[32];
374 pa_assert(level
< PA_LOG_LEVEL_MAX
);
379 _target
= target_override_set
? target_override
: target
.type
;
380 _maximum_level
= PA_MAX(maximum_level
, maximum_level_override
);
381 _show_backtrace
= PA_MAX(show_backtrace
, show_backtrace_override
);
382 _flags
= flags
| flags_override
;
384 if (PA_LIKELY(level
> _maximum_level
)) {
389 pa_vsnprintf(text
, sizeof(text
), format
, ap
);
391 if ((_flags
& PA_LOG_PRINT_META
) && file
&& line
> 0 && func
)
392 pa_snprintf(location
, sizeof(location
), "[%s][%s:%i %s()] ",
393 pa_strnull(pa_thread_get_name(pa_thread_self())), file
, line
, func
);
394 else if ((_flags
& (PA_LOG_PRINT_META
|PA_LOG_PRINT_FILE
)) && file
)
395 pa_snprintf(location
, sizeof(location
), "[%s] %s: ",
396 pa_strnull(pa_thread_get_name(pa_thread_self())), pa_path_get_filename(file
));
400 if (_flags
& PA_LOG_PRINT_TIME
) {
401 static pa_usec_t start
, last
;
404 u
= pa_rtclock_now();
414 /* This is not thread safe, but this is a debugging tool only
418 pa_snprintf(timestamp
, sizeof(timestamp
), "(%4llu.%03llu|%4llu.%03llu) ",
419 (unsigned long long) (a
/ PA_USEC_PER_SEC
),
420 (unsigned long long) (((a
/ PA_USEC_PER_MSEC
)) % 1000),
421 (unsigned long long) (r
/ PA_USEC_PER_SEC
),
422 (unsigned long long) (((r
/ PA_USEC_PER_MSEC
)) % 1000));
427 #ifdef HAVE_EXECINFO_H
428 if (_show_backtrace
> 0)
429 bt
= get_backtrace(_show_backtrace
);
432 if (!pa_utf8_valid(text
))
433 pa_logl(level
, "Invalid UTF-8 string following below:");
435 for (t
= text
; t
; t
= n
) {
436 if ((n
= strchr(t
, '\n'))) {
441 /* We ignore strings only made out of whitespace */
442 if (t
[strspn(t
, "\t ")] == 0)
447 case PA_LOG_STDERR
: {
448 const char *prefix
= "", *suffix
= "", *grey
= "";
452 /* Yes indeed. Useless, but fun! */
453 if ((_flags
& PA_LOG_COLORS
) && isatty(STDERR_FILENO
)) {
454 if (level
<= PA_LOG_ERROR
)
455 prefix
= "\x1B[1;31m";
456 else if (level
<= PA_LOG_WARN
)
462 if (grey
[0] || prefix
[0])
467 /* We shouldn't be using dynamic allocation here to
468 * minimize the hit in RT threads */
469 if ((local_t
= pa_utf8_to_locale(t
)))
472 if (_flags
& PA_LOG_PRINT_LEVEL
)
473 fprintf(stderr
, "%s%c: %s%s%s%s%s%s\n", timestamp
, level_to_char
[level
], location
, prefix
, t
, grey
, pa_strempty(bt
), suffix
);
475 fprintf(stderr
, "%s%s%s%s%s%s%s\n", timestamp
, location
, prefix
, t
, grey
, pa_strempty(bt
), suffix
);
487 log_syslog(level
, t
, timestamp
, location
, bt
);
493 if (sd_journal_send("MESSAGE=%s", t
,
494 "PRIORITY=%i", level_to_journal
[level
],
495 "CODE_FILE=%s", file
,
496 "CODE_FUNC=%s", func
,
497 "CODE_LINE=%d", line
,
500 pa_log_target new_target
= { .type
= PA_LOG_SYSLOG
, .file
= NULL
};
502 syslog(level_to_syslog
[PA_LOG_ERROR
], "%s%s%s", timestamp
, __FILE__
,
503 "Error writing logs to the journal. Redirect log messages to syslog.");
504 log_syslog(level
, t
, timestamp
, location
, bt
);
506 pa_log_target new_target
= { .type
= PA_LOG_STDERR
, .file
= NULL
};
509 fprintf(stderr
, "%s\n", "Error writing logs to the journal. Redirect log messages to console.");
510 fprintf(stderr
, "%s %s\n", metadata
, t
);
512 pa_log_set_target(&new_target
);
518 case PA_LOG_NEWFILE
: {
521 if ((local_t
= pa_utf8_to_locale(t
)))
527 if (_flags
& PA_LOG_PRINT_LEVEL
)
528 pa_snprintf(metadata
, sizeof(metadata
), "%s%c: %s", timestamp
, level_to_char
[level
], location
);
530 pa_snprintf(metadata
, sizeof(metadata
), "%s%s", timestamp
, location
);
532 if ((pa_write(log_fd
, metadata
, strlen(metadata
), &write_type
) < 0)
533 || (pa_write(log_fd
, t
, strlen(t
), &write_type
) < 0)
534 || (bt
&& pa_write(log_fd
, bt
, strlen(bt
), &write_type
) < 0)
535 || (pa_write(log_fd
, "\n", 1, &write_type
) < 0)) {
536 pa_log_target new_target
= { .type
= PA_LOG_STDERR
, .file
= NULL
};
538 fprintf(stderr
, "%s\n", "Error writing logs to a file descriptor. Redirect log messages to console.");
539 fprintf(stderr
, "%s %s\n", metadata
, t
);
540 pa_log_set_target(&new_target
);
558 void pa_log_level_meta(
559 pa_log_level_t level
,
563 const char *format
, ...) {
566 va_start(ap
, format
);
567 pa_log_levelv_meta(level
, file
, line
, func
, format
, ap
);
571 void pa_log_levelv(pa_log_level_t level
, const char *format
, va_list ap
) {
572 pa_log_levelv_meta(level
, NULL
, 0, NULL
, format
, ap
);
575 void pa_log_level(pa_log_level_t level
, const char *format
, ...) {
578 va_start(ap
, format
);
579 pa_log_levelv_meta(level
, NULL
, 0, NULL
, format
, ap
);
583 bool pa_log_ratelimit(pa_log_level_t level
) {
584 /* Not more than 10 messages every 5s */
585 static PA_DEFINE_RATELIMIT(ratelimit
, 5 * PA_USEC_PER_SEC
, 10);
592 return pa_ratelimit_test(&ratelimit
, level
);
595 pa_log_target
*pa_log_target_new(pa_log_target_type_t type
, const char *file
) {
596 pa_log_target
*t
= NULL
;
598 t
= pa_xnew(pa_log_target
, 1);
601 t
->file
= pa_xstrdup(file
);
606 void pa_log_target_free(pa_log_target
*t
) {
613 pa_log_target
*pa_log_parse_target(const char *string
) {
614 pa_log_target
*t
= NULL
;
618 if (pa_streq(string
, "stderr"))
619 t
= pa_log_target_new(PA_LOG_STDERR
, NULL
);
620 else if (pa_streq(string
, "syslog"))
621 t
= pa_log_target_new(PA_LOG_SYSLOG
, NULL
);
623 else if (pa_streq(string
, "journal"))
624 t
= pa_log_target_new(PA_LOG_JOURNAL
, NULL
);
626 else if (pa_streq(string
, "null"))
627 t
= pa_log_target_new(PA_LOG_NULL
, NULL
);
628 else if (pa_startswith(string
, "file:"))
629 t
= pa_log_target_new(PA_LOG_FILE
, string
+ 5);
630 else if (pa_startswith(string
, "newfile:"))
631 t
= pa_log_target_new(PA_LOG_NEWFILE
, string
+ 8);
633 pa_log(_("Invalid log target."));
638 char *pa_log_target_to_string(const pa_log_target
*t
) {
645 string
= pa_xstrdup("stderr");
648 string
= pa_xstrdup("syslog");
652 string
= pa_xstrdup("journal");
656 string
= pa_xstrdup("null");
659 string
= pa_sprintf_malloc("file:%s", t
->file
);
662 string
= pa_sprintf_malloc("newfile:%s", t
->file
);