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;
82 static const int level_to_syslog
[] = {
83 [PA_LOG_ERROR
] = LOG_ERR
,
84 [PA_LOG_WARN
] = LOG_WARNING
,
85 [PA_LOG_NOTICE
] = LOG_NOTICE
,
86 [PA_LOG_INFO
] = LOG_INFO
,
87 [PA_LOG_DEBUG
] = LOG_DEBUG
91 static const char level_to_char
[] = {
94 [PA_LOG_NOTICE
] = 'N',
99 void pa_log_set_ident(const char *p
) {
102 if (!(ident
= pa_utf8_to_locale(p
)))
103 ident
= pa_ascii_filter(p
);
106 /* To make valgrind shut up. */
107 static void ident_destructor(void) PA_GCC_DESTRUCTOR
;
108 static void ident_destructor(void) {
109 if (!pa_in_valgrind())
115 void pa_log_set_level(pa_log_level_t l
) {
116 pa_assert(l
< PA_LOG_LEVEL_MAX
);
121 int pa_log_set_target(pa_log_target
*t
) {
133 if ((fd
= pa_open_cloexec(t
->file
, O_WRONLY
| O_TRUNC
| O_CREAT
, S_IRUSR
| S_IWUSR
)) < 0) {
134 pa_log(_("Failed to open target file '%s'."), t
->file
);
138 case PA_LOG_NEWFILE
: {
143 file_path
= pa_sprintf_malloc("%s.xx", t
->file
);
144 p
= file_path
+ strlen(t
->file
);
146 for (version
= 0; version
<= LOG_MAX_SUFFIX_NUMBER
; version
++) {
147 memset(p
, 0, 3); /* Overwrite the ".xx" part in file_path with zero bytes. */
150 pa_snprintf(p
, 4, ".%u", version
); /* Why 4? ".xx" + termitating zero byte. */
152 if ((fd
= pa_open_cloexec(file_path
, O_WRONLY
| O_TRUNC
| O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
)) >= 0)
156 if (version
> LOG_MAX_SUFFIX_NUMBER
) {
157 pa_log(_("Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."),
158 t
->file
, t
->file
, t
->file
, t
->file
, LOG_MAX_SUFFIX_NUMBER
);
162 pa_log_debug("Opened target file %s\n", file_path
);
169 target
.type
= t
->type
;
170 pa_xfree(target
.file
);
171 target
.file
= pa_xstrdup(t
->file
);
182 void pa_log_set_flags(pa_log_flags_t _flags
, pa_log_merge_t merge
) {
183 pa_assert(!(_flags
& ~(PA_LOG_COLORS
|PA_LOG_PRINT_TIME
|PA_LOG_PRINT_FILE
|PA_LOG_PRINT_META
|PA_LOG_PRINT_LEVEL
)));
185 if (merge
== PA_LOG_SET
)
187 else if (merge
== PA_LOG_UNSET
)
193 void pa_log_set_show_backtrace(unsigned nlevels
) {
194 show_backtrace
= nlevels
;
197 void pa_log_set_skip_backtrace(unsigned nlevels
) {
198 skip_backtrace
= nlevels
;
201 #ifdef HAVE_EXECINFO_H
203 static char* get_backtrace(unsigned show_nframes
) {
206 char **symbols
, *e
, *r
;
210 pa_assert(show_nframes
> 0);
212 n_frames
= backtrace(trace
, PA_ELEMENTSOF(trace
));
217 symbols
= backtrace_symbols(trace
, n_frames
);
223 n
= PA_MIN((unsigned) n_frames
, s
+ show_nframes
);
227 for (j
= s
; j
< n
; j
++) {
230 a
+= strlen(pa_path_get_filename(symbols
[j
]));
233 r
= pa_xnew(char, a
);
238 for (j
= s
; j
< n
; j
++) {
246 sym
= pa_path_get_filename(symbols
[j
]);
261 static void init_defaults(void) {
268 if (pa_get_binary_name(binary
, sizeof(binary
)))
269 pa_log_set_ident(binary
);
272 if (getenv(ENV_LOG_SYSLOG
)) {
273 target_override
= PA_LOG_SYSLOG
;
274 target_override_set
= true;
277 if ((e
= getenv(ENV_LOG_LEVEL
))) {
278 maximum_level_override
= (pa_log_level_t
) atoi(e
);
280 if (maximum_level_override
>= PA_LOG_LEVEL_MAX
)
281 maximum_level_override
= PA_LOG_LEVEL_MAX
-1;
284 if (getenv(ENV_LOG_COLORS
))
285 flags_override
|= PA_LOG_COLORS
;
287 if (getenv(ENV_LOG_PRINT_TIME
))
288 flags_override
|= PA_LOG_PRINT_TIME
;
290 if (getenv(ENV_LOG_PRINT_FILE
))
291 flags_override
|= PA_LOG_PRINT_FILE
;
293 if (getenv(ENV_LOG_PRINT_META
))
294 flags_override
|= PA_LOG_PRINT_META
;
296 if (getenv(ENV_LOG_PRINT_LEVEL
))
297 flags_override
|= PA_LOG_PRINT_LEVEL
;
299 if ((e
= getenv(ENV_LOG_BACKTRACE
))) {
300 show_backtrace_override
= (unsigned) atoi(e
);
302 if (show_backtrace_override
<= 0)
303 show_backtrace_override
= 0;
306 if ((e
= getenv(ENV_LOG_BACKTRACE_SKIP
))) {
307 skip_backtrace
= (unsigned) atoi(e
);
309 if (skip_backtrace
<= 0)
313 if (getenv(ENV_LOG_NO_RATELIMIT
))
314 no_rate_limit
= true;
319 void pa_log_levelv_meta(
320 pa_log_level_t level
,
328 int saved_errno
= errno
;
330 pa_log_target_type_t _target
;
331 pa_log_level_t _maximum_level
;
332 unsigned _show_backtrace
;
333 pa_log_flags_t _flags
;
335 /* We don't use dynamic memory allocation here to minimize the hit
337 char text
[16*1024], location
[128], timestamp
[32];
339 pa_assert(level
< PA_LOG_LEVEL_MAX
);
344 _target
= target_override_set
? target_override
: target
.type
;
345 _maximum_level
= PA_MAX(maximum_level
, maximum_level_override
);
346 _show_backtrace
= PA_MAX(show_backtrace
, show_backtrace_override
);
347 _flags
= flags
| flags_override
;
349 if (PA_LIKELY(level
> _maximum_level
)) {
354 pa_vsnprintf(text
, sizeof(text
), format
, ap
);
356 if ((_flags
& PA_LOG_PRINT_META
) && file
&& line
> 0 && func
)
357 pa_snprintf(location
, sizeof(location
), "[%s][%s:%i %s()] ",
358 pa_strnull(pa_thread_get_name(pa_thread_self())), file
, line
, func
);
359 else if ((_flags
& (PA_LOG_PRINT_META
|PA_LOG_PRINT_FILE
)) && file
)
360 pa_snprintf(location
, sizeof(location
), "[%s] %s: ",
361 pa_strnull(pa_thread_get_name(pa_thread_self())), pa_path_get_filename(file
));
365 if (_flags
& PA_LOG_PRINT_TIME
) {
366 static pa_usec_t start
, last
;
369 u
= pa_rtclock_now();
379 /* This is not thread safe, but this is a debugging tool only
383 pa_snprintf(timestamp
, sizeof(timestamp
), "(%4llu.%03llu|%4llu.%03llu) ",
384 (unsigned long long) (a
/ PA_USEC_PER_SEC
),
385 (unsigned long long) (((a
/ PA_USEC_PER_MSEC
)) % 1000),
386 (unsigned long long) (r
/ PA_USEC_PER_SEC
),
387 (unsigned long long) (((r
/ PA_USEC_PER_MSEC
)) % 1000));
392 #ifdef HAVE_EXECINFO_H
393 if (_show_backtrace
> 0)
394 bt
= get_backtrace(_show_backtrace
);
397 if (!pa_utf8_valid(text
))
398 pa_logl(level
, "Invalid UTF-8 string following below:");
400 for (t
= text
; t
; t
= n
) {
401 if ((n
= strchr(t
, '\n'))) {
406 /* We ignore strings only made out of whitespace */
407 if (t
[strspn(t
, "\t ")] == 0)
412 case PA_LOG_STDERR
: {
413 const char *prefix
= "", *suffix
= "", *grey
= "";
417 /* Yes indeed. Useless, but fun! */
418 if ((_flags
& PA_LOG_COLORS
) && isatty(STDERR_FILENO
)) {
419 if (level
<= PA_LOG_ERROR
)
420 prefix
= "\x1B[1;31m";
421 else if (level
<= PA_LOG_WARN
)
427 if (grey
[0] || prefix
[0])
432 /* We shouldn't be using dynamic allocation here to
433 * minimize the hit in RT threads */
434 if ((local_t
= pa_utf8_to_locale(t
)))
437 if (_flags
& PA_LOG_PRINT_LEVEL
)
438 fprintf(stderr
, "%s%c: %s%s%s%s%s%s\n", timestamp
, level_to_char
[level
], location
, prefix
, t
, grey
, pa_strempty(bt
), suffix
);
440 fprintf(stderr
, "%s%s%s%s%s%s%s\n", timestamp
, location
, prefix
, t
, grey
, pa_strempty(bt
), suffix
);
451 case PA_LOG_SYSLOG
: {
454 openlog(ident
, LOG_PID
, LOG_USER
);
456 if ((local_t
= pa_utf8_to_locale(t
)))
459 syslog(level_to_syslog
[level
], "%s%s%s%s", timestamp
, location
, t
, pa_strempty(bt
));
467 case PA_LOG_NEWFILE
: {
471 pa_snprintf(metadata
, sizeof(metadata
), "\n%c %s %s", level_to_char
[level
], timestamp
, location
);
473 if ((write(log_fd
, metadata
, strlen(metadata
)) < 0) || (write(log_fd
, t
, strlen(t
)) < 0)) {
474 pa_log_target new_target
= { .type
= PA_LOG_STDERR
, .file
= NULL
};
476 fprintf(stderr
, "%s\n", "Error writing logs to a file descriptor. Redirect log messages to console.");
477 fprintf(stderr
, "%s %s\n", metadata
, t
);
478 pa_log_set_target(&new_target
);
494 void pa_log_level_meta(
495 pa_log_level_t level
,
499 const char *format
, ...) {
502 va_start(ap
, format
);
503 pa_log_levelv_meta(level
, file
, line
, func
, format
, ap
);
507 void pa_log_levelv(pa_log_level_t level
, const char *format
, va_list ap
) {
508 pa_log_levelv_meta(level
, NULL
, 0, NULL
, format
, ap
);
511 void pa_log_level(pa_log_level_t level
, const char *format
, ...) {
514 va_start(ap
, format
);
515 pa_log_levelv_meta(level
, NULL
, 0, NULL
, format
, ap
);
519 bool pa_log_ratelimit(pa_log_level_t level
) {
520 /* Not more than 10 messages every 5s */
521 static PA_DEFINE_RATELIMIT(ratelimit
, 5 * PA_USEC_PER_SEC
, 10);
528 return pa_ratelimit_test(&ratelimit
, level
);
531 pa_log_target
*pa_log_target_new(pa_log_target_type_t type
, const char *file
) {
532 pa_log_target
*t
= NULL
;
534 t
= pa_xnew(pa_log_target
, 1);
537 t
->file
= pa_xstrdup(file
);
542 void pa_log_target_free(pa_log_target
*t
) {
549 pa_log_target
*pa_log_parse_target(const char *string
) {
550 pa_log_target
*t
= NULL
;
554 if (pa_streq(string
, "stderr"))
555 t
= pa_log_target_new(PA_LOG_STDERR
, NULL
);
556 else if (pa_streq(string
, "syslog"))
557 t
= pa_log_target_new(PA_LOG_SYSLOG
, NULL
);
558 else if (pa_streq(string
, "null"))
559 t
= pa_log_target_new(PA_LOG_NULL
, NULL
);
560 else if (pa_startswith(string
, "file:"))
561 t
= pa_log_target_new(PA_LOG_FILE
, string
+ 5);
562 else if (pa_startswith(string
, "newfile:"))
563 t
= pa_log_target_new(PA_LOG_NEWFILE
, string
+ 8);
565 pa_log(_("Invalid log target."));
570 char *pa_log_target_to_string(const pa_log_target
*t
) {
577 string
= pa_xstrdup("stderr");
580 string
= pa_xstrdup("syslog");
583 string
= pa_xstrdup("null");
586 string
= pa_sprintf_malloc("file:%s", t
->file
);
589 string
= pa_sprintf_malloc("newfile:%s", t
->file
);