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
33 #ifdef HAVE_EXECINFO_H
41 #include <pulse/gccmacro.h>
42 #include <pulse/rtclock.h>
43 #include <pulse/utf8.h>
44 #include <pulse/xmalloc.h>
45 #include <pulse/util.h>
46 #include <pulse/timeval.h>
48 #include <pulsecore/macro.h>
49 #include <pulsecore/core-util.h>
50 #include <pulsecore/once.h>
51 #include <pulsecore/ratelimit.h>
55 #define ENV_LOG_SYSLOG "PULSE_LOG_SYSLOG"
56 #define ENV_LOG_LEVEL "PULSE_LOG"
57 #define ENV_LOG_COLORS "PULSE_LOG_COLORS"
58 #define ENV_LOG_PRINT_TIME "PULSE_LOG_TIME"
59 #define ENV_LOG_PRINT_FILE "PULSE_LOG_FILE"
60 #define ENV_LOG_PRINT_META "PULSE_LOG_META"
61 #define ENV_LOG_PRINT_LEVEL "PULSE_LOG_LEVEL"
62 #define ENV_LOG_BACKTRACE "PULSE_LOG_BACKTRACE"
63 #define ENV_LOG_BACKTRACE_SKIP "PULSE_LOG_BACKTRACE_SKIP"
64 #define ENV_LOG_NO_RATELIMIT "PULSE_LOG_NO_RATE_LIMIT"
66 static char *ident
= NULL
; /* in local charset format */
67 static pa_log_target_t target
= PA_LOG_STDERR
, target_override
;
68 static pa_bool_t target_override_set
= FALSE
;
69 static pa_log_level_t maximum_level
= PA_LOG_ERROR
, maximum_level_override
= PA_LOG_ERROR
;
70 static unsigned show_backtrace
= 0, show_backtrace_override
= 0, skip_backtrace
= 0;
71 static pa_log_flags_t flags
= 0, flags_override
= 0;
72 static pa_bool_t no_rate_limit
= FALSE
;
73 static int log_fd
= -1;
76 static const int level_to_syslog
[] = {
77 [PA_LOG_ERROR
] = LOG_ERR
,
78 [PA_LOG_WARN
] = LOG_WARNING
,
79 [PA_LOG_NOTICE
] = LOG_NOTICE
,
80 [PA_LOG_INFO
] = LOG_INFO
,
81 [PA_LOG_DEBUG
] = LOG_DEBUG
85 static const char level_to_char
[] = {
88 [PA_LOG_NOTICE
] = 'N',
93 void pa_log_set_ident(const char *p
) {
96 if (!(ident
= pa_utf8_to_locale(p
)))
97 ident
= pa_ascii_filter(p
);
100 /* To make valgrind shut up. */
101 static void ident_destructor(void) PA_GCC_DESTRUCTOR
;
102 static void ident_destructor(void) {
103 if (!pa_in_valgrind())
109 void pa_log_set_level(pa_log_level_t l
) {
110 pa_assert(l
< PA_LOG_LEVEL_MAX
);
115 void pa_log_set_target(pa_log_target_t t
) {
116 pa_assert(t
< PA_LOG_TARGET_MAX
);
121 void pa_log_set_flags(pa_log_flags_t _flags
, pa_log_merge_t merge
) {
122 pa_assert(!(_flags
& ~(PA_LOG_COLORS
|PA_LOG_PRINT_TIME
|PA_LOG_PRINT_FILE
|PA_LOG_PRINT_META
|PA_LOG_PRINT_LEVEL
)));
124 if (merge
== PA_LOG_SET
)
126 else if (merge
== PA_LOG_UNSET
)
132 void pa_log_set_fd(int fd
) {
135 else if (log_fd
>= 0) {
141 void pa_log_set_show_backtrace(unsigned nlevels
) {
142 show_backtrace
= nlevels
;
145 void pa_log_set_skip_backtrace(unsigned nlevels
) {
146 skip_backtrace
= nlevels
;
149 #ifdef HAVE_EXECINFO_H
151 static char* get_backtrace(unsigned show_nframes
) {
154 char **symbols
, *e
, *r
;
158 pa_assert(show_nframes
> 0);
160 n_frames
= backtrace(trace
, PA_ELEMENTSOF(trace
));
165 symbols
= backtrace_symbols(trace
, n_frames
);
171 n
= PA_MIN((unsigned) n_frames
, s
+ show_nframes
);
175 for (j
= s
; j
< n
; j
++) {
178 a
+= strlen(pa_path_get_filename(symbols
[j
]));
181 r
= pa_xnew(char, a
);
186 for (j
= s
; j
< n
; j
++) {
194 sym
= pa_path_get_filename(symbols
[j
]);
209 static void init_defaults(void) {
216 if (pa_get_binary_name(binary
, sizeof(binary
)))
217 pa_log_set_ident(binary
);
220 if (getenv(ENV_LOG_SYSLOG
)) {
221 target_override
= PA_LOG_SYSLOG
;
222 target_override_set
= TRUE
;
225 if ((e
= getenv(ENV_LOG_LEVEL
))) {
226 maximum_level_override
= (pa_log_level_t
) atoi(e
);
228 if (maximum_level_override
>= PA_LOG_LEVEL_MAX
)
229 maximum_level_override
= PA_LOG_LEVEL_MAX
-1;
232 if (getenv(ENV_LOG_COLORS
))
233 flags_override
|= PA_LOG_COLORS
;
235 if (getenv(ENV_LOG_PRINT_TIME
))
236 flags_override
|= PA_LOG_PRINT_TIME
;
238 if (getenv(ENV_LOG_PRINT_FILE
))
239 flags_override
|= PA_LOG_PRINT_FILE
;
241 if (getenv(ENV_LOG_PRINT_META
))
242 flags_override
|= PA_LOG_PRINT_META
;
244 if (getenv(ENV_LOG_PRINT_LEVEL
))
245 flags_override
|= PA_LOG_PRINT_LEVEL
;
247 if ((e
= getenv(ENV_LOG_BACKTRACE
))) {
248 show_backtrace_override
= (unsigned) atoi(e
);
250 if (show_backtrace_override
<= 0)
251 show_backtrace_override
= 0;
254 if ((e
= getenv(ENV_LOG_BACKTRACE_SKIP
))) {
255 skip_backtrace
= (unsigned) atoi(e
);
257 if (skip_backtrace
<= 0)
261 if (getenv(ENV_LOG_NO_RATELIMIT
))
262 no_rate_limit
= TRUE
;
267 void pa_log_levelv_meta(
268 pa_log_level_t level
,
276 int saved_errno
= errno
;
278 pa_log_target_t _target
;
279 pa_log_level_t _maximum_level
;
280 unsigned _show_backtrace
;
281 pa_log_flags_t _flags
;
283 /* We don't use dynamic memory allocation here to minimize the hit
285 char text
[16*1024], location
[128], timestamp
[32];
287 pa_assert(level
< PA_LOG_LEVEL_MAX
);
292 _target
= target_override_set
? target_override
: target
;
293 _maximum_level
= PA_MAX(maximum_level
, maximum_level_override
);
294 _show_backtrace
= PA_MAX(show_backtrace
, show_backtrace_override
);
295 _flags
= flags
| flags_override
;
297 if (PA_LIKELY(level
> _maximum_level
)) {
302 pa_vsnprintf(text
, sizeof(text
), format
, ap
);
304 if ((_flags
& PA_LOG_PRINT_META
) && file
&& line
> 0 && func
)
305 pa_snprintf(location
, sizeof(location
), "[%s:%i %s()] ", file
, line
, func
);
306 else if ((_flags
& (PA_LOG_PRINT_META
|PA_LOG_PRINT_FILE
)) && file
)
307 pa_snprintf(location
, sizeof(location
), "%s: ", pa_path_get_filename(file
));
311 if (_flags
& PA_LOG_PRINT_TIME
) {
312 static pa_usec_t start
, last
;
315 u
= pa_rtclock_now();
325 /* This is not thread safe, but this is a debugging tool only
329 pa_snprintf(timestamp
, sizeof(timestamp
), "(%4llu.%03llu|%4llu.%03llu) ",
330 (unsigned long long) (a
/ PA_USEC_PER_SEC
),
331 (unsigned long long) (((a
/ PA_USEC_PER_MSEC
)) % 1000),
332 (unsigned long long) (r
/ PA_USEC_PER_SEC
),
333 (unsigned long long) (((r
/ PA_USEC_PER_MSEC
)) % 1000));
338 #ifdef HAVE_EXECINFO_H
339 if (_show_backtrace
> 0)
340 bt
= get_backtrace(_show_backtrace
);
343 if (!pa_utf8_valid(text
))
344 pa_logl(level
, "Invalid UTF-8 string following below:");
346 for (t
= text
; t
; t
= n
) {
347 if ((n
= strchr(t
, '\n'))) {
352 /* We ignore strings only made out of whitespace */
353 if (t
[strspn(t
, "\t ")] == 0)
358 case PA_LOG_STDERR
: {
359 const char *prefix
= "", *suffix
= "", *grey
= "";
363 /* Yes indeed. Useless, but fun! */
364 if ((_flags
& PA_LOG_COLORS
) && isatty(STDERR_FILENO
)) {
365 if (level
<= PA_LOG_ERROR
)
366 prefix
= "\x1B[1;31m";
367 else if (level
<= PA_LOG_WARN
)
373 if (grey
[0] || prefix
[0])
378 /* We shouldn't be using dynamic allocation here to
379 * minimize the hit in RT threads */
380 if ((local_t
= pa_utf8_to_locale(t
)))
383 if (_flags
& PA_LOG_PRINT_LEVEL
)
384 fprintf(stderr
, "%s%c: %s%s%s%s%s%s\n", timestamp
, level_to_char
[level
], location
, prefix
, t
, grey
, pa_strempty(bt
), suffix
);
386 fprintf(stderr
, "%s%s%s%s%s%s%s\n", timestamp
, location
, prefix
, t
, grey
, pa_strempty(bt
), suffix
);
397 case PA_LOG_SYSLOG
: {
400 openlog(ident
, LOG_PID
, LOG_USER
);
402 if ((local_t
= pa_utf8_to_locale(t
)))
405 syslog(level_to_syslog
[level
], "%s%s%s%s", timestamp
, location
, t
, pa_strempty(bt
));
416 pa_snprintf(metadata
, sizeof(metadata
), "\n%c %s %s", level_to_char
[level
], timestamp
, location
);
418 if ((write(log_fd
, metadata
, strlen(metadata
)) < 0) || (write(log_fd
, t
, strlen(t
)) < 0)) {
421 fprintf(stderr
, "%s\n", "Error writing logs to a file descriptor. Redirect log messages to console.");
422 fprintf(stderr
, "%s %s\n", metadata
, t
);
423 pa_log_set_target(PA_LOG_STDERR
);
439 void pa_log_level_meta(
440 pa_log_level_t level
,
444 const char *format
, ...) {
447 va_start(ap
, format
);
448 pa_log_levelv_meta(level
, file
, line
, func
, format
, ap
);
452 void pa_log_levelv(pa_log_level_t level
, const char *format
, va_list ap
) {
453 pa_log_levelv_meta(level
, NULL
, 0, NULL
, format
, ap
);
456 void pa_log_level(pa_log_level_t level
, const char *format
, ...) {
459 va_start(ap
, format
);
460 pa_log_levelv_meta(level
, NULL
, 0, NULL
, format
, ap
);
464 pa_bool_t
pa_log_ratelimit(pa_log_level_t level
) {
465 /* Not more than 10 messages every 5s */
466 static PA_DEFINE_RATELIMIT(ratelimit
, 5 * PA_USEC_PER_SEC
, 10);
473 return pa_ratelimit_test(&ratelimit
, level
);