Fix indent
[platform/upstream/pulseaudio.git] / src / pulsecore / log.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2004-2006 Lennart Poettering
5   Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
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.
11
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.
16
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
20   USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <sys/stat.h>
34
35 #ifdef HAVE_EXECINFO_H
36 #include <execinfo.h>
37 #endif
38
39 #ifdef HAVE_SYSLOG_H
40 #include <syslog.h>
41 #endif
42
43 #ifdef HAVE_SYSTEMD_JOURNAL
44 #include <systemd/sd-journal.h>
45 #endif
46
47 #include <pulse/gccmacro.h>
48 #include <pulse/rtclock.h>
49 #include <pulse/utf8.h>
50 #include <pulse/xmalloc.h>
51 #include <pulse/util.h>
52 #include <pulse/timeval.h>
53
54 #include <pulsecore/macro.h>
55 #include <pulsecore/core-util.h>
56 #include <pulsecore/core-error.h>
57 #include <pulsecore/once.h>
58 #include <pulsecore/ratelimit.h>
59 #include <pulsecore/thread.h>
60 #include <pulsecore/i18n.h>
61
62 #include "log.h"
63
64 #ifdef USE_DLOG
65 #include <dlog.h>
66 #define DLOG_TAG        "PULSEAUDIO"
67
68 #define COLOR_BLACK     30
69 #define COLOR_RED       31
70 #define COLOR_GREEN     32
71 #define COLOR_BLUE      34
72 #define COLOR_MAGENTA   35
73 #define COLOR_CYAN      36
74 #define COLOR_WHITE     97
75 #define COLOR_B_GRAY    100
76 #define COLOR_B_RED     101
77 #define COLOR_B_GREEN   102
78 #define COLOR_B_YELLOW  103
79 #define COLOR_B_BLUE    104
80 #define COLOR_B_MAGENTA 105
81 #define COLOR_B_CYAN    106
82 #define COLOR_REVERSE   7
83
84 #endif
85 #define ENV_LOG_SYSLOG "PULSE_LOG_SYSLOG"
86 #define ENV_LOG_LEVEL "PULSE_LOG"
87 #define ENV_LOG_COLORS "PULSE_LOG_COLORS"
88 #define ENV_LOG_PRINT_TIME "PULSE_LOG_TIME"
89 #define ENV_LOG_PRINT_FILE "PULSE_LOG_FILE"
90 #define ENV_LOG_PRINT_META "PULSE_LOG_META"
91 #define ENV_LOG_PRINT_LEVEL "PULSE_LOG_LEVEL"
92 #define ENV_LOG_BACKTRACE "PULSE_LOG_BACKTRACE"
93 #define ENV_LOG_BACKTRACE_SKIP "PULSE_LOG_BACKTRACE_SKIP"
94 #define ENV_LOG_NO_RATELIMIT "PULSE_LOG_NO_RATE_LIMIT"
95 #define LOG_MAX_SUFFIX_NUMBER 99
96
97 static char *ident = NULL; /* in local charset format */
98 static pa_log_target target = { PA_LOG_STDERR, NULL };
99 static pa_log_target_type_t target_override;
100 static bool target_override_set = false;
101 static pa_log_level_t maximum_level = PA_LOG_ERROR, maximum_level_override = PA_LOG_ERROR;
102 static unsigned show_backtrace = 0, show_backtrace_override = 0, skip_backtrace = 0;
103 static pa_log_flags_t flags = 0, flags_override = 0;
104 static bool no_rate_limit = false;
105 static int log_fd = -1;
106 static int write_type = 0;
107
108 #ifdef HAVE_SYSLOG_H
109 static const int level_to_syslog[] = {
110     [PA_LOG_ERROR] = LOG_ERR,
111     [PA_LOG_WARN] = LOG_WARNING,
112     [PA_LOG_NOTICE] = LOG_NOTICE,
113     [PA_LOG_INFO] = LOG_INFO,
114     [PA_LOG_DEBUG] = LOG_DEBUG
115 };
116 #endif
117
118 /* These are actually equivalent to the syslog ones
119  * but we don't want to depend on syslog.h */
120 #ifdef HAVE_SYSTEMD_JOURNAL
121 static const int level_to_journal[] = {
122     [PA_LOG_ERROR]  = 3,
123     [PA_LOG_WARN]   = 4,
124     [PA_LOG_NOTICE] = 5,
125     [PA_LOG_INFO]   = 6,
126     [PA_LOG_DEBUG]  = 7
127 };
128 #endif
129
130 static const char level_to_char[] = {
131     [PA_LOG_ERROR] = 'E',
132     [PA_LOG_WARN] = 'W',
133     [PA_LOG_NOTICE] = 'N',
134     [PA_LOG_INFO] = 'I',
135     [PA_LOG_DEBUG] = 'D'
136 };
137
138 void pa_log_set_ident(const char *p) {
139     pa_xfree(ident);
140
141     if (!(ident = pa_utf8_to_locale(p)))
142         ident = pa_ascii_filter(p);
143 }
144
145 /* To make valgrind shut up. */
146 static void ident_destructor(void) PA_GCC_DESTRUCTOR;
147 static void ident_destructor(void) {
148     if (!pa_in_valgrind())
149         return;
150
151     pa_xfree(ident);
152 }
153
154 void pa_log_set_level(pa_log_level_t l) {
155     pa_assert(l < PA_LOG_LEVEL_MAX);
156
157     maximum_level = l;
158 }
159
160 int pa_log_set_target(pa_log_target *t) {
161     int fd = -1;
162     int old_fd;
163
164     pa_assert(t);
165
166     switch (t->type) {
167         case PA_LOG_STDERR:
168         case PA_LOG_SYSLOG:
169 #ifdef HAVE_SYSTEMD_JOURNAL
170         case PA_LOG_JOURNAL:
171 #endif
172 #ifdef USE_DLOG
173         case PA_LOG_DLOG:
174         case PA_LOG_DLOG_COLOR:
175 #endif
176         case PA_LOG_NULL:
177             break;
178         case PA_LOG_FILE:
179             if ((fd = pa_open_cloexec(t->file, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
180                 pa_log(_("Failed to open target file '%s'."), t->file);
181                 return -1;
182             }
183             break;
184         case PA_LOG_NEWFILE: {
185             char *file_path;
186             char *p;
187             unsigned version;
188
189             file_path = pa_sprintf_malloc("%s.xx", t->file);
190             p = file_path + strlen(t->file);
191
192             for (version = 0; version <= LOG_MAX_SUFFIX_NUMBER; version++) {
193                 memset(p, 0, 3); /* Overwrite the ".xx" part in file_path with zero bytes. */
194
195                 if (version > 0)
196                     pa_snprintf(p, 4, ".%u", version); /* Why 4? ".xx" + termitating zero byte. */
197
198                 if ((fd = pa_open_cloexec(file_path, O_WRONLY | O_TRUNC | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) >= 0)
199                     break;
200             }
201
202             if (version > LOG_MAX_SUFFIX_NUMBER) {
203                 pa_log(_("Tried to open target file '%s', '%s.1', '%s.2' ... '%s.%d', but all failed."),
204                         t->file, t->file, t->file, t->file, LOG_MAX_SUFFIX_NUMBER);
205                 pa_xfree(file_path);
206                 return -1;
207             } else
208                 pa_log_debug("Opened target file %s\n", file_path);
209
210             pa_xfree(file_path);
211             break;
212         }
213     }
214
215     target.type = t->type;
216     pa_xfree(target.file);
217     target.file = pa_xstrdup(t->file);
218
219     old_fd = log_fd;
220     log_fd = fd;
221
222     if (old_fd >= 0)
223         pa_close(old_fd);
224
225     return 0;
226 }
227
228 void pa_log_set_flags(pa_log_flags_t _flags, pa_log_merge_t merge) {
229     pa_assert(!(_flags & ~(PA_LOG_COLORS|PA_LOG_PRINT_TIME|PA_LOG_PRINT_FILE|PA_LOG_PRINT_META|PA_LOG_PRINT_LEVEL)));
230
231     if (merge == PA_LOG_SET)
232         flags |= _flags;
233     else if (merge == PA_LOG_UNSET)
234         flags &= ~_flags;
235     else
236         flags = _flags;
237 }
238
239 void pa_log_set_show_backtrace(unsigned nlevels) {
240     show_backtrace = nlevels;
241 }
242
243 void pa_log_set_skip_backtrace(unsigned nlevels) {
244     skip_backtrace = nlevels;
245 }
246
247 #ifdef HAVE_EXECINFO_H
248
249 static char* get_backtrace(unsigned show_nframes) {
250     void* trace[32];
251     int n_frames;
252     char **symbols, *e, *r;
253     unsigned j, n, s;
254     size_t a;
255
256     pa_assert(show_nframes > 0);
257
258     n_frames = backtrace(trace, PA_ELEMENTSOF(trace));
259
260     if (n_frames <= 0)
261         return NULL;
262
263     symbols = backtrace_symbols(trace, n_frames);
264
265     if (!symbols)
266         return NULL;
267
268     s = skip_backtrace;
269     n = PA_MIN((unsigned) n_frames, s + show_nframes);
270
271     a = 4;
272
273     for (j = s; j < n; j++) {
274         if (j > s)
275             a += 2;
276         a += strlen(pa_path_get_filename(symbols[j]));
277     }
278
279     r = pa_xnew(char, a);
280
281     strcpy(r, " (");
282     e = r + 2;
283
284     for (j = s; j < n; j++) {
285         const char *sym;
286
287         if (j > s) {
288             strcpy(e, "<<");
289             e += 2;
290         }
291
292         sym = pa_path_get_filename(symbols[j]);
293
294         strcpy(e, sym);
295         e += strlen(sym);
296     }
297
298     strcpy(e, ")");
299
300     free(symbols);
301
302     return r;
303 }
304
305 #endif
306
307 static void init_defaults(void) {
308     PA_ONCE_BEGIN {
309
310         const char *e;
311
312         if (!ident) {
313             char binary[256];
314             if (pa_get_binary_name(binary, sizeof(binary)))
315                 pa_log_set_ident(binary);
316         }
317
318         if (getenv(ENV_LOG_SYSLOG)) {
319             target_override = PA_LOG_SYSLOG;
320             target_override_set = true;
321         }
322
323         if ((e = getenv(ENV_LOG_LEVEL))) {
324             maximum_level_override = (pa_log_level_t) atoi(e);
325
326             if (maximum_level_override >= PA_LOG_LEVEL_MAX)
327                 maximum_level_override = PA_LOG_LEVEL_MAX-1;
328         }
329
330         if (getenv(ENV_LOG_COLORS))
331             flags_override |= PA_LOG_COLORS;
332
333         if (getenv(ENV_LOG_PRINT_TIME))
334             flags_override |= PA_LOG_PRINT_TIME;
335
336         if (getenv(ENV_LOG_PRINT_FILE))
337             flags_override |= PA_LOG_PRINT_FILE;
338
339         if (getenv(ENV_LOG_PRINT_META))
340             flags_override |= PA_LOG_PRINT_META;
341
342         if (getenv(ENV_LOG_PRINT_LEVEL))
343             flags_override |= PA_LOG_PRINT_LEVEL;
344
345         if ((e = getenv(ENV_LOG_BACKTRACE))) {
346             show_backtrace_override = (unsigned) atoi(e);
347
348             if (show_backtrace_override <= 0)
349                 show_backtrace_override = 0;
350         }
351
352         if ((e = getenv(ENV_LOG_BACKTRACE_SKIP))) {
353             skip_backtrace = (unsigned) atoi(e);
354
355             if (skip_backtrace <= 0)
356                 skip_backtrace = 0;
357         }
358
359         if (getenv(ENV_LOG_NO_RATELIMIT))
360             no_rate_limit = true;
361
362     } PA_ONCE_END;
363 }
364
365 #ifdef HAVE_SYSLOG_H
366 static void log_syslog(pa_log_level_t level, char *t, char *timestamp, char *location, char *bt) {
367     char *local_t;
368
369     openlog(ident, LOG_PID, LOG_USER);
370
371     if ((local_t = pa_utf8_to_locale(t)))
372         t = local_t;
373
374     syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
375     pa_xfree(local_t);
376 }
377 #endif
378
379 void pa_log_levelv_meta(
380         pa_log_level_t level,
381         const char*file,
382         int line,
383         const char *func,
384         const char *format,
385         va_list ap) {
386
387     char *t, *n;
388     int saved_errno = errno;
389     char *bt = NULL;
390     pa_log_target_type_t _target;
391     pa_log_level_t _maximum_level;
392     unsigned _show_backtrace;
393     pa_log_flags_t _flags;
394
395     /* We don't use dynamic memory allocation here to minimize the hit
396      * in RT threads */
397     char text[16*1024], location[128], timestamp[32];
398
399     pa_assert(level < PA_LOG_LEVEL_MAX);
400     pa_assert(format);
401
402     init_defaults();
403
404     _target = target_override_set ? target_override : target.type;
405     _maximum_level = PA_MAX(maximum_level, maximum_level_override);
406     _show_backtrace = PA_MAX(show_backtrace, show_backtrace_override);
407     _flags = flags | flags_override;
408
409     if (PA_LIKELY(level > _maximum_level)) {
410         errno = saved_errno;
411         return;
412     }
413
414     pa_vsnprintf(text, sizeof(text), format, ap);
415
416     if ((_flags & PA_LOG_PRINT_META) && file && line > 0 && func)
417         pa_snprintf(location, sizeof(location), "[%s][%s:%i %s()] ",
418                     pa_strnull(pa_thread_get_name(pa_thread_self())), file, line, func);
419     else if ((_flags & (PA_LOG_PRINT_META|PA_LOG_PRINT_FILE)) && file)
420         pa_snprintf(location, sizeof(location), "[%s] %s: ",
421                     pa_strnull(pa_thread_get_name(pa_thread_self())), pa_path_get_filename(file));
422     else
423         location[0] = 0;
424
425     if (_flags & PA_LOG_PRINT_TIME) {
426         static pa_usec_t start, last;
427         pa_usec_t u, a, r;
428
429         u = pa_rtclock_now();
430
431         PA_ONCE_BEGIN {
432             start = u;
433             last = u;
434         } PA_ONCE_END;
435
436         r = u - last;
437         a = u - start;
438
439         /* This is not thread safe, but this is a debugging tool only
440          * anyway. */
441         last = u;
442
443         pa_snprintf(timestamp, sizeof(timestamp), "(%4llu.%03llu|%4llu.%03llu) ",
444                     (unsigned long long) (a / PA_USEC_PER_SEC),
445                     (unsigned long long) (((a / PA_USEC_PER_MSEC)) % 1000),
446                     (unsigned long long) (r / PA_USEC_PER_SEC),
447                     (unsigned long long) (((r / PA_USEC_PER_MSEC)) % 1000));
448
449     } else
450         timestamp[0] = 0;
451
452 #ifdef HAVE_EXECINFO_H
453     if (_show_backtrace > 0)
454         bt = get_backtrace(_show_backtrace);
455 #endif
456
457     if (!pa_utf8_valid(text))
458         pa_logl(level, "Invalid UTF-8 string following below:");
459
460     for (t = text; t; t = n) {
461         if ((n = strchr(t, '\n'))) {
462             *n = 0;
463             n++;
464         }
465
466         /* We ignore strings only made out of whitespace */
467         if (t[strspn(t, "\t ")] == 0)
468             continue;
469
470         switch (_target) {
471
472             case PA_LOG_STDERR: {
473                 const char *prefix = "", *suffix = "", *grey = "";
474                 char *local_t;
475
476 #ifndef OS_IS_WIN32
477                 /* Yes indeed. Useless, but fun! */
478                 if ((_flags & PA_LOG_COLORS) && isatty(STDERR_FILENO)) {
479                     if (level <= PA_LOG_ERROR)
480                         prefix = "\x1B[1;31m";
481                     else if (level <= PA_LOG_WARN)
482                         prefix = "\x1B[1m";
483
484                     if (bt)
485                         grey = "\x1B[2m";
486
487                     if (grey[0] || prefix[0])
488                         suffix = "\x1B[0m";
489                 }
490 #endif
491
492                 /* We shouldn't be using dynamic allocation here to
493                  * minimize the hit in RT threads */
494                 if ((local_t = pa_utf8_to_locale(t)))
495                     t = local_t;
496
497                 if (_flags & PA_LOG_PRINT_LEVEL)
498                     fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, t, grey, pa_strempty(bt), suffix);
499                 else
500                     fprintf(stderr, "%s%s%s%s%s%s%s\n", timestamp, location, prefix, t, grey, pa_strempty(bt), suffix);
501 #ifdef OS_IS_WIN32
502                 fflush(stderr);
503 #endif
504
505                 pa_xfree(local_t);
506
507                 break;
508             }
509
510 #ifdef HAVE_SYSLOG_H
511             case PA_LOG_SYSLOG:
512                 log_syslog(level, t, timestamp, location, bt);
513                 break;
514 #endif
515
516 #ifdef HAVE_SYSTEMD_JOURNAL
517             case PA_LOG_JOURNAL:
518                 if (sd_journal_send("MESSAGE=%s", t,
519                                 "PRIORITY=%i", level_to_journal[level],
520                                 "CODE_FILE=%s", file,
521                                 "CODE_FUNC=%s", func,
522                                 "CODE_LINE=%d", line,
523                                 NULL) < 0) {
524 #ifdef HAVE_SYSLOG_H
525                     pa_log_target new_target = { .type = PA_LOG_SYSLOG, .file = NULL };
526
527                     syslog(level_to_syslog[PA_LOG_ERROR], "%s%s%s", timestamp, __FILE__,
528                            "Error writing logs to the journal. Redirect log messages to syslog.");
529                     log_syslog(level, t, timestamp, location, bt);
530 #else
531                     pa_log_target new_target = { .type = PA_LOG_STDERR, .file = NULL };
532
533                     saved_errno = errno;
534                     fprintf(stderr, "%s\n", "Error writing logs to the journal. Redirect log messages to console.");
535                     fprintf(stderr, "%s %s\n", metadata, t);
536 #endif
537                     pa_log_set_target(&new_target);
538                 }
539                 break;
540 #endif
541
542             case PA_LOG_FILE:
543             case PA_LOG_NEWFILE: {
544                 char *local_t;
545
546                 if ((local_t = pa_utf8_to_locale(t)))
547                     t = local_t;
548
549                 if (log_fd >= 0) {
550                     char metadata[256];
551
552                     if (_flags & PA_LOG_PRINT_LEVEL)
553                         pa_snprintf(metadata, sizeof(metadata), "%s%c: %s", timestamp, level_to_char[level], location);
554                     else
555                         pa_snprintf(metadata, sizeof(metadata), "%s%s", timestamp, location);
556
557                     if ((pa_write(log_fd, metadata, strlen(metadata), &write_type) < 0)
558                             || (pa_write(log_fd, t, strlen(t), &write_type) < 0)
559                             || (bt && pa_write(log_fd, bt, strlen(bt), &write_type) < 0)
560                             || (pa_write(log_fd, "\n", 1, &write_type) < 0)) {
561                         pa_log_target new_target = { .type = PA_LOG_STDERR, .file = NULL };
562                         saved_errno = errno;
563                         fprintf(stderr, "%s\n", "Error writing logs to a file descriptor. Redirect log messages to console.");
564                         fprintf(stderr, "%s %s\n", metadata, t);
565                         pa_log_set_target(&new_target);
566                     }
567                 }
568
569                 pa_xfree(local_t);
570
571                 break;
572             }
573
574 #ifdef USE_DLOG
575             case PA_LOG_DLOG: {
576                 char *local_t;
577
578                 openlog(ident, LOG_PID, LOG_USER);
579
580                 if ((local_t = pa_utf8_to_locale(t)))
581                     t = local_t;
582
583                 switch (level)
584                 {
585                                         case PA_LOG_DEBUG:
586                                                 SLOG (LOG_DEBUG, DLOG_TAG, "%s%s%s%s",  timestamp, location, t, pa_strempty(bt));
587                                                 break;
588                                         case PA_LOG_INFO:
589                                         case PA_LOG_NOTICE: /* no notice category in dlog, use info instead */
590                                                 SLOG (LOG_INFO, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
591                                                 break;
592                                         case PA_LOG_WARN:
593                                                 SLOG (LOG_WARN, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
594                                                 break;
595                                         case PA_LOG_ERROR:
596                                                 SLOG (LOG_ERROR, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
597                                                 break;
598                                         default:
599                                                 SLOG (LOG_DEBUG, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
600                                                 break;
601                 }
602
603                 pa_xfree(local_t);
604
605                 break;
606             }
607             case PA_LOG_DLOG_COLOR: {
608                                 char *local_t;
609
610                                 openlog(ident, LOG_PID, LOG_USER);
611
612                                 if ((local_t = pa_utf8_to_locale(t)))
613                                         t = local_t;
614
615                                 switch (level)
616                                 {
617                                         case PA_LOG_DEBUG:
618                                                 SLOG (LOG_DEBUG, DLOG_TAG, "\033[%dm%s%s%s%s\033[0m", COLOR_GREEN, timestamp, location, t, pa_strempty(bt));
619                                                 break;
620                                         case PA_LOG_INFO:
621                                         case PA_LOG_NOTICE: /* no notice category in dlog, use info instead */
622                                                 SLOG (LOG_INFO, DLOG_TAG, "\033[%dm%s%s%s%s\033[0m", COLOR_BLUE, timestamp, location, t, pa_strempty(bt));
623                                                 break;
624                                         case PA_LOG_WARN:
625                                                 SLOG (LOG_WARN, DLOG_TAG, "\033[%dm%s%s%s%s\033[0m", COLOR_MAGENTA, timestamp, location, t, pa_strempty(bt));
626                                                 break;
627                                         case PA_LOG_ERROR:
628                                                 SLOG (LOG_ERROR, DLOG_TAG, "\033[%dm%s%s%s%s\033[0m", COLOR_RED, timestamp, location, t, pa_strempty(bt));
629                                                 break;
630                                         default:
631                                                 SLOG (LOG_DEBUG, DLOG_TAG, "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
632                                                 break;
633                                 }
634
635                                 pa_xfree(local_t);
636
637                                 break;
638                         }
639
640 #endif
641             case PA_LOG_NULL:
642             default:
643                 break;
644         }
645     }
646
647     pa_xfree(bt);
648     errno = saved_errno;
649 }
650
651 void pa_log_level_meta(
652         pa_log_level_t level,
653         const char*file,
654         int line,
655         const char *func,
656         const char *format, ...) {
657
658     va_list ap;
659     va_start(ap, format);
660     pa_log_levelv_meta(level, file, line, func, format, ap);
661     va_end(ap);
662 }
663
664 void pa_log_levelv(pa_log_level_t level, const char *format, va_list ap) {
665     pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
666 }
667
668 void pa_log_level(pa_log_level_t level, const char *format, ...) {
669     va_list ap;
670
671     va_start(ap, format);
672     pa_log_levelv_meta(level, NULL, 0, NULL, format, ap);
673     va_end(ap);
674 }
675
676 bool pa_log_ratelimit(pa_log_level_t level) {
677     /* Not more than 10 messages every 5s */
678     static PA_DEFINE_RATELIMIT(ratelimit, 5 * PA_USEC_PER_SEC, 10);
679
680     init_defaults();
681
682     if (no_rate_limit)
683         return true;
684
685     return pa_ratelimit_test(&ratelimit, level);
686 }
687
688 pa_log_target *pa_log_target_new(pa_log_target_type_t type, const char *file) {
689     pa_log_target *t = NULL;
690
691     t = pa_xnew(pa_log_target, 1);
692
693     t->type = type;
694     t->file = pa_xstrdup(file);
695
696     return t;
697 }
698
699 void pa_log_target_free(pa_log_target *t) {
700     pa_assert(t);
701
702     pa_xfree(t->file);
703     pa_xfree(t);
704 }
705
706 pa_log_target *pa_log_parse_target(const char *string) {
707     pa_log_target *t = NULL;
708
709     pa_assert(string);
710
711     if (pa_streq(string, "stderr"))
712         t = pa_log_target_new(PA_LOG_STDERR, NULL);
713     else if (pa_streq(string, "syslog"))
714         t = pa_log_target_new(PA_LOG_SYSLOG, NULL);
715 #ifdef HAVE_SYSTEMD_JOURNAL
716     else if (pa_streq(string, "journal"))
717         t = pa_log_target_new(PA_LOG_JOURNAL, NULL);
718 #endif
719     else if (pa_streq(string, "null"))
720         t = pa_log_target_new(PA_LOG_NULL, NULL);
721     else if (pa_startswith(string, "file:"))
722         t = pa_log_target_new(PA_LOG_FILE, string + 5);
723     else if (pa_startswith(string, "newfile:"))
724         t = pa_log_target_new(PA_LOG_NEWFILE, string + 8);
725 #ifdef USE_DLOG
726     else if (pa_streq(string, "dlog"))
727         t = pa_log_target_new(PA_LOG_DLOG, NULL);
728     else if (pa_streq(string, "dlog-color"))
729         t = pa_log_target_new(PA_LOG_DLOG_COLOR, NULL);
730 #endif
731     else
732         pa_log(_("Invalid log target."));
733
734     return t;
735 }
736
737 char *pa_log_target_to_string(const pa_log_target *t) {
738     char *string = NULL;
739
740     pa_assert(t);
741
742     switch (t->type) {
743         case PA_LOG_STDERR:
744             string = pa_xstrdup("stderr");
745             break;
746         case PA_LOG_SYSLOG:
747             string = pa_xstrdup("syslog");
748             break;
749 #ifdef HAVE_SYSTEMD_JOURNAL
750         case PA_LOG_JOURNAL:
751             string = pa_xstrdup("journal");
752             break;
753 #endif
754 #ifdef USE_DLOG
755         case PA_LOG_DLOG:
756             string = pa_xstrdup("dlog");
757             break;
758         case PA_LOG_DLOG_COLOR:
759             string = pa_xstrdup("dlog-color");
760             break;
761 #endif
762         case PA_LOG_NULL:
763             string = pa_xstrdup("null");
764             break;
765         case PA_LOG_FILE:
766             string = pa_sprintf_malloc("file:%s", t->file);
767             break;
768         case PA_LOG_NEWFILE:
769             string = pa_sprintf_malloc("newfile:%s", t->file);
770             break;
771     }
772
773     return string;
774 }