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 pa_bool_t 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 pa_bool_t 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 file_path
, file_path
, file_path
, file_path
, 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()] ", pa_thread_get_name(pa_thread_self()), file
, line
, func
);
367 else if ((_flags
& (PA_LOG_PRINT_META
|PA_LOG_PRINT_FILE
)) && file
)
368 pa_snprintf(location
, sizeof(location
), "[%s] %s: ", pa_thread_get_name(pa_thread_self()), pa_path_get_filename(file
));
372 if (_flags
& PA_LOG_PRINT_TIME
) {
373 static pa_usec_t start
, last
;
376 u
= pa_rtclock_now();
386 /* This is not thread safe, but this is a debugging tool only
390 pa_snprintf(timestamp
, sizeof(timestamp
), "(%4llu.%03llu|%4llu.%03llu) ",
391 (unsigned long long) (a
/ PA_USEC_PER_SEC
),
392 (unsigned long long) (((a
/ PA_USEC_PER_MSEC
)) % 1000),
393 (unsigned long long) (r
/ PA_USEC_PER_SEC
),
394 (unsigned long long) (((r
/ PA_USEC_PER_MSEC
)) % 1000));
399 #ifdef HAVE_EXECINFO_H
400 if (_show_backtrace
> 0)
401 bt
= get_backtrace(_show_backtrace
);
404 if (!pa_utf8_valid(text
))
405 pa_logl(level
, "Invalid UTF-8 string following below:");
407 for (t
= text
; t
; t
= n
) {
408 if ((n
= strchr(t
, '\n'))) {
413 /* We ignore strings only made out of whitespace */
414 if (t
[strspn(t
, "\t ")] == 0)
419 case PA_LOG_STDERR
: {
420 const char *prefix
= "", *suffix
= "", *grey
= "";
424 /* Yes indeed. Useless, but fun! */
425 if ((_flags
& PA_LOG_COLORS
) && isatty(STDERR_FILENO
)) {
426 if (level
<= PA_LOG_ERROR
)
427 prefix
= "\x1B[1;31m";
428 else if (level
<= PA_LOG_WARN
)
434 if (grey
[0] || prefix
[0])
439 /* We shouldn't be using dynamic allocation here to
440 * minimize the hit in RT threads */
441 if ((local_t
= pa_utf8_to_locale(t
)))
444 if (_flags
& PA_LOG_PRINT_LEVEL
)
445 fprintf(stderr
, "%s%c: %s%s%s%s%s%s\n", timestamp
, level_to_char
[level
], location
, prefix
, t
, grey
, pa_strempty(bt
), suffix
);
447 fprintf(stderr
, "%s%s%s%s%s%s%s\n", timestamp
, location
, prefix
, t
, grey
, pa_strempty(bt
), suffix
);
458 case PA_LOG_SYSLOG
: {
461 openlog(ident
, LOG_PID
, LOG_USER
);
463 if ((local_t
= pa_utf8_to_locale(t
)))
466 syslog(level_to_syslog
[level
], "%s%s%s%s", timestamp
, location
, t
, pa_strempty(bt
));
474 case PA_LOG_NEWFILE
: {
478 pa_snprintf(metadata
, sizeof(metadata
), "\n%c %s %s", level_to_char
[level
], timestamp
, location
);
480 if ((write(log_fd
, metadata
, strlen(metadata
)) < 0) || (write(log_fd
, t
, strlen(t
)) < 0)) {
481 pa_log_target new_target
= { .type
= PA_LOG_STDERR
, .file
= NULL
};
484 fprintf(stderr
, "%s\n", "Error writing logs to a file descriptor. Redirect log messages to console.");
485 fprintf(stderr
, "%s %s\n", metadata
, t
);
486 pa_log_set_target(&new_target
);
502 void pa_log_level_meta(
503 pa_log_level_t level
,
507 const char *format
, ...) {
510 va_start(ap
, format
);
511 pa_log_levelv_meta(level
, file
, line
, func
, format
, ap
);
515 void pa_log_levelv(pa_log_level_t level
, const char *format
, va_list ap
) {
516 pa_log_levelv_meta(level
, NULL
, 0, NULL
, format
, ap
);
519 void pa_log_level(pa_log_level_t level
, const char *format
, ...) {
522 va_start(ap
, format
);
523 pa_log_levelv_meta(level
, NULL
, 0, NULL
, format
, ap
);
527 pa_bool_t
pa_log_ratelimit(pa_log_level_t level
) {
528 /* Not more than 10 messages every 5s */
529 static PA_DEFINE_RATELIMIT(ratelimit
, 5 * PA_USEC_PER_SEC
, 10);
536 return pa_ratelimit_test(&ratelimit
, level
);
539 pa_log_target
*pa_log_target_new(pa_log_target_type_t type
, const char *file
) {
540 pa_log_target
*t
= NULL
;
542 t
= pa_xnew(pa_log_target
, 1);
545 t
->file
= pa_xstrdup(file
);
550 void pa_log_target_free(pa_log_target
*t
) {
557 pa_log_target
*pa_log_parse_target(const char *string
) {
558 pa_log_target
*t
= NULL
;
562 if (pa_streq(string
, "stderr"))
563 t
= pa_log_target_new(PA_LOG_STDERR
, NULL
);
564 else if (pa_streq(string
, "syslog"))
565 t
= pa_log_target_new(PA_LOG_SYSLOG
, NULL
);
566 else if (pa_streq(string
, "null"))
567 t
= pa_log_target_new(PA_LOG_NULL
, NULL
);
568 else if (pa_startswith(string
, "file:"))
569 t
= pa_log_target_new(PA_LOG_FILE
, string
+ 5);
570 else if (pa_startswith(string
, "newfile:"))
571 t
= pa_log_target_new(PA_LOG_NEWFILE
, string
+ 8);
573 pa_log(_("Invalid log target."));
578 char *pa_log_target_to_string(const pa_log_target
*t
) {
585 string
= pa_xstrdup("stderr");
588 string
= pa_xstrdup("syslog");
591 string
= pa_xstrdup("null");
594 string
= pa_sprintf_malloc("file:%s", t
->file
);
597 string
= pa_sprintf_malloc("newfile:%s", t
->file
);