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_fd(int fd
) {
196 else if (log_fd
>= 0) {
202 void pa_log_set_show_backtrace(unsigned nlevels
) {
203 show_backtrace
= nlevels
;
206 void pa_log_set_skip_backtrace(unsigned nlevels
) {
207 skip_backtrace
= nlevels
;
210 #ifdef HAVE_EXECINFO_H
212 static char* get_backtrace(unsigned show_nframes
) {
215 char **symbols
, *e
, *r
;
219 pa_assert(show_nframes
> 0);
221 n_frames
= backtrace(trace
, PA_ELEMENTSOF(trace
));
226 symbols
= backtrace_symbols(trace
, n_frames
);
232 n
= PA_MIN((unsigned) n_frames
, s
+ show_nframes
);
236 for (j
= s
; j
< n
; j
++) {
239 a
+= strlen(pa_path_get_filename(symbols
[j
]));
242 r
= pa_xnew(char, a
);
247 for (j
= s
; j
< n
; j
++) {
255 sym
= pa_path_get_filename(symbols
[j
]);
270 static void init_defaults(void) {
277 if (pa_get_binary_name(binary
, sizeof(binary
)))
278 pa_log_set_ident(binary
);
281 if (getenv(ENV_LOG_SYSLOG
)) {
282 target_override
= PA_LOG_SYSLOG
;
283 target_override_set
= true;
286 if ((e
= getenv(ENV_LOG_LEVEL
))) {
287 maximum_level_override
= (pa_log_level_t
) atoi(e
);
289 if (maximum_level_override
>= PA_LOG_LEVEL_MAX
)
290 maximum_level_override
= PA_LOG_LEVEL_MAX
-1;
293 if (getenv(ENV_LOG_COLORS
))
294 flags_override
|= PA_LOG_COLORS
;
296 if (getenv(ENV_LOG_PRINT_TIME
))
297 flags_override
|= PA_LOG_PRINT_TIME
;
299 if (getenv(ENV_LOG_PRINT_FILE
))
300 flags_override
|= PA_LOG_PRINT_FILE
;
302 if (getenv(ENV_LOG_PRINT_META
))
303 flags_override
|= PA_LOG_PRINT_META
;
305 if (getenv(ENV_LOG_PRINT_LEVEL
))
306 flags_override
|= PA_LOG_PRINT_LEVEL
;
308 if ((e
= getenv(ENV_LOG_BACKTRACE
))) {
309 show_backtrace_override
= (unsigned) atoi(e
);
311 if (show_backtrace_override
<= 0)
312 show_backtrace_override
= 0;
315 if ((e
= getenv(ENV_LOG_BACKTRACE_SKIP
))) {
316 skip_backtrace
= (unsigned) atoi(e
);
318 if (skip_backtrace
<= 0)
322 if (getenv(ENV_LOG_NO_RATELIMIT
))
323 no_rate_limit
= true;
328 void pa_log_levelv_meta(
329 pa_log_level_t level
,
337 int saved_errno
= errno
;
339 pa_log_target_type_t _target
;
340 pa_log_level_t _maximum_level
;
341 unsigned _show_backtrace
;
342 pa_log_flags_t _flags
;
344 /* We don't use dynamic memory allocation here to minimize the hit
346 char text
[16*1024], location
[128], timestamp
[32];
348 pa_assert(level
< PA_LOG_LEVEL_MAX
);
353 _target
= target_override_set
? target_override
: target
.type
;
354 _maximum_level
= PA_MAX(maximum_level
, maximum_level_override
);
355 _show_backtrace
= PA_MAX(show_backtrace
, show_backtrace_override
);
356 _flags
= flags
| flags_override
;
358 if (PA_LIKELY(level
> _maximum_level
)) {
363 pa_vsnprintf(text
, sizeof(text
), format
, ap
);
365 if ((_flags
& PA_LOG_PRINT_META
) && file
&& line
> 0 && func
)
366 pa_snprintf(location
, sizeof(location
), "[%s][%s:%i %s()] ",
367 pa_strnull(pa_thread_get_name(pa_thread_self())), file
, line
, func
);
368 else if ((_flags
& (PA_LOG_PRINT_META
|PA_LOG_PRINT_FILE
)) && file
)
369 pa_snprintf(location
, sizeof(location
), "[%s] %s: ",
370 pa_strnull(pa_thread_get_name(pa_thread_self())), pa_path_get_filename(file
));
374 if (_flags
& PA_LOG_PRINT_TIME
) {
375 static pa_usec_t start
, last
;
378 u
= pa_rtclock_now();
388 /* This is not thread safe, but this is a debugging tool only
392 pa_snprintf(timestamp
, sizeof(timestamp
), "(%4llu.%03llu|%4llu.%03llu) ",
393 (unsigned long long) (a
/ PA_USEC_PER_SEC
),
394 (unsigned long long) (((a
/ PA_USEC_PER_MSEC
)) % 1000),
395 (unsigned long long) (r
/ PA_USEC_PER_SEC
),
396 (unsigned long long) (((r
/ PA_USEC_PER_MSEC
)) % 1000));
401 #ifdef HAVE_EXECINFO_H
402 if (_show_backtrace
> 0)
403 bt
= get_backtrace(_show_backtrace
);
406 if (!pa_utf8_valid(text
))
407 pa_logl(level
, "Invalid UTF-8 string following below:");
409 for (t
= text
; t
; t
= n
) {
410 if ((n
= strchr(t
, '\n'))) {
415 /* We ignore strings only made out of whitespace */
416 if (t
[strspn(t
, "\t ")] == 0)
421 case PA_LOG_STDERR
: {
422 const char *prefix
= "", *suffix
= "", *grey
= "";
426 /* Yes indeed. Useless, but fun! */
427 if ((_flags
& PA_LOG_COLORS
) && isatty(STDERR_FILENO
)) {
428 if (level
<= PA_LOG_ERROR
)
429 prefix
= "\x1B[1;31m";
430 else if (level
<= PA_LOG_WARN
)
436 if (grey
[0] || prefix
[0])
441 /* We shouldn't be using dynamic allocation here to
442 * minimize the hit in RT threads */
443 if ((local_t
= pa_utf8_to_locale(t
)))
446 if (_flags
& PA_LOG_PRINT_LEVEL
)
447 fprintf(stderr
, "%s%c: %s%s%s%s%s%s\n", timestamp
, level_to_char
[level
], location
, prefix
, t
, grey
, pa_strempty(bt
), suffix
);
449 fprintf(stderr
, "%s%s%s%s%s%s%s\n", timestamp
, location
, prefix
, t
, grey
, pa_strempty(bt
), suffix
);
460 case PA_LOG_SYSLOG
: {
463 openlog(ident
, LOG_PID
, LOG_USER
);
465 if ((local_t
= pa_utf8_to_locale(t
)))
468 syslog(level_to_syslog
[level
], "%s%s%s%s", timestamp
, location
, t
, pa_strempty(bt
));
476 case PA_LOG_NEWFILE
: {
480 pa_snprintf(metadata
, sizeof(metadata
), "\n%c %s %s", level_to_char
[level
], timestamp
, location
);
482 if ((write(log_fd
, metadata
, strlen(metadata
)) < 0) || (write(log_fd
, t
, strlen(t
)) < 0)) {
483 pa_log_target new_target
= { .type
= PA_LOG_STDERR
, .file
= NULL
};
486 fprintf(stderr
, "%s\n", "Error writing logs to a file descriptor. Redirect log messages to console.");
487 fprintf(stderr
, "%s %s\n", metadata
, t
);
488 pa_log_set_target(&new_target
);
504 void pa_log_level_meta(
505 pa_log_level_t level
,
509 const char *format
, ...) {
512 va_start(ap
, format
);
513 pa_log_levelv_meta(level
, file
, line
, func
, format
, ap
);
517 void pa_log_levelv(pa_log_level_t level
, const char *format
, va_list ap
) {
518 pa_log_levelv_meta(level
, NULL
, 0, NULL
, format
, ap
);
521 void pa_log_level(pa_log_level_t level
, const char *format
, ...) {
524 va_start(ap
, format
);
525 pa_log_levelv_meta(level
, NULL
, 0, NULL
, format
, ap
);
529 bool pa_log_ratelimit(pa_log_level_t level
) {
530 /* Not more than 10 messages every 5s */
531 static PA_DEFINE_RATELIMIT(ratelimit
, 5 * PA_USEC_PER_SEC
, 10);
538 return pa_ratelimit_test(&ratelimit
, level
);
541 pa_log_target
*pa_log_target_new(pa_log_target_type_t type
, const char *file
) {
542 pa_log_target
*t
= NULL
;
544 t
= pa_xnew(pa_log_target
, 1);
547 t
->file
= pa_xstrdup(file
);
552 void pa_log_target_free(pa_log_target
*t
) {
559 pa_log_target
*pa_log_parse_target(const char *string
) {
560 pa_log_target
*t
= NULL
;
564 if (pa_streq(string
, "stderr"))
565 t
= pa_log_target_new(PA_LOG_STDERR
, NULL
);
566 else if (pa_streq(string
, "syslog"))
567 t
= pa_log_target_new(PA_LOG_SYSLOG
, NULL
);
568 else if (pa_streq(string
, "null"))
569 t
= pa_log_target_new(PA_LOG_NULL
, NULL
);
570 else if (pa_startswith(string
, "file:"))
571 t
= pa_log_target_new(PA_LOG_FILE
, string
+ 5);
572 else if (pa_startswith(string
, "newfile:"))
573 t
= pa_log_target_new(PA_LOG_NEWFILE
, string
+ 8);
575 pa_log(_("Invalid log target."));
580 char *pa_log_target_to_string(const pa_log_target
*t
) {
587 string
= pa_xstrdup("stderr");
590 string
= pa_xstrdup("syslog");
593 string
= pa_xstrdup("null");
596 string
= pa_sprintf_malloc("file:%s", t
->file
);
599 string
= pa_sprintf_malloc("newfile:%s", t
->file
);