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 #include <pulse/gccmacro.h>
44 #include <pulse/rtclock.h>
45 #include <pulse/utf8.h>
46 #include <pulse/xmalloc.h>
47 #include <pulse/util.h>
48 #include <pulse/timeval.h>
50 #include <pulsecore/macro.h>
51 #include <pulsecore/core-util.h>
52 #include <pulsecore/core-error.h>
53 #include <pulsecore/once.h>
54 #include <pulsecore/ratelimit.h>
55 #include <pulsecore/thread.h>
56 #include <pulsecore/i18n.h>
60 #define ENV_LOG_SYSLOG "PULSE_LOG_SYSLOG"
61 #define ENV_LOG_LEVEL "PULSE_LOG"
62 #define ENV_LOG_COLORS "PULSE_LOG_COLORS"
63 #define ENV_LOG_PRINT_TIME "PULSE_LOG_TIME"
64 #define ENV_LOG_PRINT_FILE "PULSE_LOG_FILE"
65 #define ENV_LOG_PRINT_META "PULSE_LOG_META"
66 #define ENV_LOG_PRINT_LEVEL "PULSE_LOG_LEVEL"
67 #define ENV_LOG_BACKTRACE "PULSE_LOG_BACKTRACE"
68 #define ENV_LOG_BACKTRACE_SKIP "PULSE_LOG_BACKTRACE_SKIP"
69 #define ENV_LOG_NO_RATELIMIT "PULSE_LOG_NO_RATE_LIMIT"
70 #define LOG_MAX_SUFFIX_NUMBER 99
72 static char *ident
= NULL
; /* in local charset format */
73 static pa_log_target target
= { PA_LOG_STDERR
, NULL
};
74 static pa_log_target_type_t target_override
;
75 static bool target_override_set
= false;
76 static pa_log_level_t maximum_level
= PA_LOG_ERROR
, maximum_level_override
= PA_LOG_ERROR
;
77 static unsigned show_backtrace
= 0, show_backtrace_override
= 0, skip_backtrace
= 0;
78 static pa_log_flags_t flags
= 0, flags_override
= 0;
79 static bool no_rate_limit
= false;
80 static int log_fd
= -1;
81 static int write_type
= 0;
84 static const int level_to_syslog
[] = {
85 [PA_LOG_ERROR
] = LOG_ERR
,
86 [PA_LOG_WARN
] = LOG_WARNING
,
87 [PA_LOG_NOTICE
] = LOG_NOTICE
,
88 [PA_LOG_INFO
] = LOG_INFO
,
89 [PA_LOG_DEBUG
] = LOG_DEBUG
93 static const char level_to_char
[] = {
96 [PA_LOG_NOTICE
] = 'N',
101 void pa_log_set_ident(const char *p
) {
104 if (!(ident
= pa_utf8_to_locale(p
)))
105 ident
= pa_ascii_filter(p
);
108 /* To make valgrind shut up. */
109 static void ident_destructor(void) PA_GCC_DESTRUCTOR
;
110 static void ident_destructor(void) {
111 if (!pa_in_valgrind())
117 void pa_log_set_level(pa_log_level_t l
) {
118 pa_assert(l
< PA_LOG_LEVEL_MAX
);
123 int pa_log_set_target(pa_log_target
*t
) {
135 if ((fd
= pa_open_cloexec(t
->file
, O_WRONLY
| O_TRUNC
| O_CREAT
, S_IRUSR
| S_IWUSR
)) < 0) {
136 pa_log(_("Failed to open target file '%s'."), t
->file
);
140 case PA_LOG_NEWFILE
: {
145 file_path
= pa_sprintf_malloc("%s.xx", t
->file
);
146 p
= file_path
+ strlen(t
->file
);
148 for (version
= 0; version
<= LOG_MAX_SUFFIX_NUMBER
; version
++) {
149 memset(p
, 0, 3); /* Overwrite the ".xx" part in file_path with zero bytes. */
152 pa_snprintf(p
, 4, ".%u", version
); /* Why 4? ".xx" + termitating zero byte. */
154 if ((fd
= pa_open_cloexec(file_path
, O_WRONLY
| O_TRUNC
| O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
)) >= 0)
158 if (version
> LOG_MAX_SUFFIX_NUMBER
) {
159 pa_log(_("Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."),
160 t
->file
, t
->file
, t
->file
, t
->file
, LOG_MAX_SUFFIX_NUMBER
);
164 pa_log_debug("Opened target file %s\n", file_path
);
171 target
.type
= t
->type
;
172 pa_xfree(target
.file
);
173 target
.file
= pa_xstrdup(t
->file
);
184 void pa_log_set_flags(pa_log_flags_t _flags
, pa_log_merge_t merge
) {
185 pa_assert(!(_flags
& ~(PA_LOG_COLORS
|PA_LOG_PRINT_TIME
|PA_LOG_PRINT_FILE
|PA_LOG_PRINT_META
|PA_LOG_PRINT_LEVEL
)));
187 if (merge
== PA_LOG_SET
)
189 else if (merge
== PA_LOG_UNSET
)
195 void pa_log_set_show_backtrace(unsigned nlevels
) {
196 show_backtrace
= nlevels
;
199 void pa_log_set_skip_backtrace(unsigned nlevels
) {
200 skip_backtrace
= nlevels
;
203 #ifdef HAVE_EXECINFO_H
205 static char* get_backtrace(unsigned show_nframes
) {
208 char **symbols
, *e
, *r
;
212 pa_assert(show_nframes
> 0);
214 n_frames
= backtrace(trace
, PA_ELEMENTSOF(trace
));
219 symbols
= backtrace_symbols(trace
, n_frames
);
225 n
= PA_MIN((unsigned) n_frames
, s
+ show_nframes
);
229 for (j
= s
; j
< n
; j
++) {
232 a
+= strlen(pa_path_get_filename(symbols
[j
]));
235 r
= pa_xnew(char, a
);
240 for (j
= s
; j
< n
; j
++) {
248 sym
= pa_path_get_filename(symbols
[j
]);
263 static void init_defaults(void) {
270 if (pa_get_binary_name(binary
, sizeof(binary
)))
271 pa_log_set_ident(binary
);
274 if (getenv(ENV_LOG_SYSLOG
)) {
275 target_override
= PA_LOG_SYSLOG
;
276 target_override_set
= true;
279 if ((e
= getenv(ENV_LOG_LEVEL
))) {
280 maximum_level_override
= (pa_log_level_t
) atoi(e
);
282 if (maximum_level_override
>= PA_LOG_LEVEL_MAX
)
283 maximum_level_override
= PA_LOG_LEVEL_MAX
-1;
286 if (getenv(ENV_LOG_COLORS
))
287 flags_override
|= PA_LOG_COLORS
;
289 if (getenv(ENV_LOG_PRINT_TIME
))
290 flags_override
|= PA_LOG_PRINT_TIME
;
292 if (getenv(ENV_LOG_PRINT_FILE
))
293 flags_override
|= PA_LOG_PRINT_FILE
;
295 if (getenv(ENV_LOG_PRINT_META
))
296 flags_override
|= PA_LOG_PRINT_META
;
298 if (getenv(ENV_LOG_PRINT_LEVEL
))
299 flags_override
|= PA_LOG_PRINT_LEVEL
;
301 if ((e
= getenv(ENV_LOG_BACKTRACE
))) {
302 show_backtrace_override
= (unsigned) atoi(e
);
304 if (show_backtrace_override
<= 0)
305 show_backtrace_override
= 0;
308 if ((e
= getenv(ENV_LOG_BACKTRACE_SKIP
))) {
309 skip_backtrace
= (unsigned) atoi(e
);
311 if (skip_backtrace
<= 0)
315 if (getenv(ENV_LOG_NO_RATELIMIT
))
316 no_rate_limit
= true;
321 void pa_log_levelv_meta(
322 pa_log_level_t level
,
330 int saved_errno
= errno
;
332 pa_log_target_type_t _target
;
333 pa_log_level_t _maximum_level
;
334 unsigned _show_backtrace
;
335 pa_log_flags_t _flags
;
337 /* We don't use dynamic memory allocation here to minimize the hit
339 char text
[16*1024], location
[128], timestamp
[32];
341 pa_assert(level
< PA_LOG_LEVEL_MAX
);
346 _target
= target_override_set
? target_override
: target
.type
;
347 _maximum_level
= PA_MAX(maximum_level
, maximum_level_override
);
348 _show_backtrace
= PA_MAX(show_backtrace
, show_backtrace_override
);
349 _flags
= flags
| flags_override
;
351 if (PA_LIKELY(level
> _maximum_level
)) {
356 pa_vsnprintf(text
, sizeof(text
), format
, ap
);
358 if ((_flags
& PA_LOG_PRINT_META
) && file
&& line
> 0 && func
)
359 pa_snprintf(location
, sizeof(location
), "[%s][%s:%i %s()] ",
360 pa_strnull(pa_thread_get_name(pa_thread_self())), file
, line
, func
);
361 else if ((_flags
& (PA_LOG_PRINT_META
|PA_LOG_PRINT_FILE
)) && file
)
362 pa_snprintf(location
, sizeof(location
), "[%s] %s: ",
363 pa_strnull(pa_thread_get_name(pa_thread_self())), pa_path_get_filename(file
));
367 if (_flags
& PA_LOG_PRINT_TIME
) {
368 static pa_usec_t start
, last
;
371 u
= pa_rtclock_now();
381 /* This is not thread safe, but this is a debugging tool only
385 pa_snprintf(timestamp
, sizeof(timestamp
), "(%4llu.%03llu|%4llu.%03llu) ",
386 (unsigned long long) (a
/ PA_USEC_PER_SEC
),
387 (unsigned long long) (((a
/ PA_USEC_PER_MSEC
)) % 1000),
388 (unsigned long long) (r
/ PA_USEC_PER_SEC
),
389 (unsigned long long) (((r
/ PA_USEC_PER_MSEC
)) % 1000));
394 #ifdef HAVE_EXECINFO_H
395 if (_show_backtrace
> 0)
396 bt
= get_backtrace(_show_backtrace
);
399 if (!pa_utf8_valid(text
))
400 pa_logl(level
, "Invalid UTF-8 string following below:");
402 for (t
= text
; t
; t
= n
) {
403 if ((n
= strchr(t
, '\n'))) {
408 /* We ignore strings only made out of whitespace */
409 if (t
[strspn(t
, "\t ")] == 0)
414 case PA_LOG_STDERR
: {
415 const char *prefix
= "", *suffix
= "", *grey
= "";
419 /* Yes indeed. Useless, but fun! */
420 if ((_flags
& PA_LOG_COLORS
) && isatty(STDERR_FILENO
)) {
421 if (level
<= PA_LOG_ERROR
)
422 prefix
= "\x1B[1;31m";
423 else if (level
<= PA_LOG_WARN
)
429 if (grey
[0] || prefix
[0])
434 /* We shouldn't be using dynamic allocation here to
435 * minimize the hit in RT threads */
436 if ((local_t
= pa_utf8_to_locale(t
)))
439 if (_flags
& PA_LOG_PRINT_LEVEL
)
440 fprintf(stderr
, "%s%c: %s%s%s%s%s%s\n", timestamp
, level_to_char
[level
], location
, prefix
, t
, grey
, pa_strempty(bt
), suffix
);
442 fprintf(stderr
, "%s%s%s%s%s%s%s\n", timestamp
, location
, prefix
, t
, grey
, pa_strempty(bt
), suffix
);
453 case PA_LOG_SYSLOG
: {
456 openlog(ident
, LOG_PID
, LOG_USER
);
458 if ((local_t
= pa_utf8_to_locale(t
)))
461 syslog(level_to_syslog
[level
], "%s%s%s%s", timestamp
, location
, t
, pa_strempty(bt
));
469 case PA_LOG_NEWFILE
: {
472 if ((local_t
= pa_utf8_to_locale(t
)))
478 if (_flags
& PA_LOG_PRINT_LEVEL
)
479 pa_snprintf(metadata
, sizeof(metadata
), "%s%c: %s", timestamp
, level_to_char
[level
], location
);
481 pa_snprintf(metadata
, sizeof(metadata
), "%s%s", timestamp
, location
);
483 if ((pa_write(log_fd
, metadata
, strlen(metadata
), &write_type
) < 0)
484 || (pa_write(log_fd
, t
, strlen(t
), &write_type
) < 0)
485 || (bt
&& pa_write(log_fd
, bt
, strlen(bt
), &write_type
) < 0)
486 || (pa_write(log_fd
, "\n", 1, &write_type
) < 0)) {
487 pa_log_target new_target
= { .type
= PA_LOG_STDERR
, .file
= NULL
};
489 fprintf(stderr
, "%s\n", "Error writing logs to a file descriptor. Redirect log messages to console.");
490 fprintf(stderr
, "%s %s\n", metadata
, t
);
491 pa_log_set_target(&new_target
);
509 void pa_log_level_meta(
510 pa_log_level_t level
,
514 const char *format
, ...) {
517 va_start(ap
, format
);
518 pa_log_levelv_meta(level
, file
, line
, func
, format
, ap
);
522 void pa_log_levelv(pa_log_level_t level
, const char *format
, va_list ap
) {
523 pa_log_levelv_meta(level
, NULL
, 0, NULL
, format
, ap
);
526 void pa_log_level(pa_log_level_t level
, const char *format
, ...) {
529 va_start(ap
, format
);
530 pa_log_levelv_meta(level
, NULL
, 0, NULL
, format
, ap
);
534 bool pa_log_ratelimit(pa_log_level_t level
) {
535 /* Not more than 10 messages every 5s */
536 static PA_DEFINE_RATELIMIT(ratelimit
, 5 * PA_USEC_PER_SEC
, 10);
543 return pa_ratelimit_test(&ratelimit
, level
);
546 pa_log_target
*pa_log_target_new(pa_log_target_type_t type
, const char *file
) {
547 pa_log_target
*t
= NULL
;
549 t
= pa_xnew(pa_log_target
, 1);
552 t
->file
= pa_xstrdup(file
);
557 void pa_log_target_free(pa_log_target
*t
) {
564 pa_log_target
*pa_log_parse_target(const char *string
) {
565 pa_log_target
*t
= NULL
;
569 if (pa_streq(string
, "stderr"))
570 t
= pa_log_target_new(PA_LOG_STDERR
, NULL
);
571 else if (pa_streq(string
, "syslog"))
572 t
= pa_log_target_new(PA_LOG_SYSLOG
, NULL
);
573 else if (pa_streq(string
, "null"))
574 t
= pa_log_target_new(PA_LOG_NULL
, NULL
);
575 else if (pa_startswith(string
, "file:"))
576 t
= pa_log_target_new(PA_LOG_FILE
, string
+ 5);
577 else if (pa_startswith(string
, "newfile:"))
578 t
= pa_log_target_new(PA_LOG_NEWFILE
, string
+ 8);
580 pa_log(_("Invalid log target."));
585 char *pa_log_target_to_string(const pa_log_target
*t
) {
592 string
= pa_xstrdup("stderr");
595 string
= pa_xstrdup("syslog");
598 string
= pa_xstrdup("null");
601 string
= pa_sprintf_malloc("file:%s", t
->file
);
604 string
= pa_sprintf_malloc("newfile:%s", t
->file
);