log: Add support for the systemd journal

The journal is a component of systemd, that captures Syslog messages,
Kernel log messages, initial RAM disk and early boot messages as well
as messages written to STDOUT/STDERR of all services, indexes them and
makes this available to the user.

It can be used in parallel, or in place of a traditional syslog daemon,
such as rsyslog or syslog-ng.

The journal offers a couple of improvements over traditional logging
facilities (e.g. advanced filtering capabilities).

This patch adds support for logging directly to the journal using its
native API.
This commit is contained in:
poljar (Damir Jelić) 2013-12-03 01:09:56 +01:00 committed by Peter Meerwald
parent a8fea5c468
commit d20ee7e7f2
8 changed files with 113 additions and 19 deletions

View file

@ -1160,6 +1160,22 @@ AC_SUBST(HAVE_SYSTEMD)
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$HAVE_SYSTEMD" = x1])
AS_IF([test "x$HAVE_SYSTEMD" = "x1"], AC_DEFINE([HAVE_SYSTEMD], 1, [Have SYSTEMD?]))
#### journal support (optional) ####
AC_ARG_ENABLE([systemd-journal],
AS_HELP_STRING([--disable-systemd-journal],[Disable optional systemd journal support]))
AS_IF([test "x$enable_systemd_journal" != "xno"],
[PKG_CHECK_MODULES(JOURNAL, [ libsystemd-journal ], HAVE_SYSTEMD_JOURNAL=1, HAVE_SYSTEMD_JOURNAL=0)],
HAVE_SYSTEMD_JOURNAL=0)
AS_IF([test "x$enable_systemd_journal" = "xyes" && test "x$HAVE_SYSTEMD_JOURNAL" = "x0"],
[AC_MSG_ERROR([*** Needed systemd journal support not found])])
AC_SUBST(HAVE_SYSTEMD_JOURNAL)
AM_CONDITIONAL([HAVE_SYSTEMD_JOURNAL], [test "x$HAVE_SYSTEMD_JOURNAL" = x1])
AS_IF([test "x$HAVE_SYSTEMD_JOURNAL" = "x1"], AC_DEFINE([HAVE_JOURNAL], 1, [Have JOURNAL?]))
#### Build and Install man pages ####
AC_ARG_ENABLE([manpages],
@ -1405,6 +1421,7 @@ AS_IF([test "x$HAVE_XEN" = "x1"], ENABLE_XEN=yes, ENABLE_XEN=no)
AS_IF([test "x$HAVE_DBUS" = "x1"], ENABLE_DBUS=yes, ENABLE_DBUS=no)
AS_IF([test "x$HAVE_UDEV" = "x1"], ENABLE_UDEV=yes, ENABLE_UDEV=no)
AS_IF([test "x$HAVE_SYSTEMD" = "x1"], ENABLE_SYSTEMD=yes, ENABLE_SYSTEMD=no)
AS_IF([test "x$HAVE_SYSTEMD_JOURNAL" = "x1"], ENABLE_SYSTEMD_JOURNAL=yes, ENABLE_SYSTEMD_JOURNAL=no)
AS_IF([test "x$HAVE_BLUEZ_4" = "x1"], ENABLE_BLUEZ_4=yes, ENABLE_BLUEZ_4=no)
AS_IF([test "x$HAVE_BLUEZ_5" = "x1"], ENABLE_BLUEZ_5=yes, ENABLE_BLUEZ_5=no)
AS_IF([test "x$HAVE_HAL_COMPAT" = "x1"], ENABLE_HAL_COMPAT=yes, ENABLE_HAL_COMPAT=no)
@ -1463,6 +1480,7 @@ echo "
Enable udev: ${ENABLE_UDEV}
Enable HAL->udev compat: ${ENABLE_HAL_COMPAT}
Enable systemd login: ${ENABLE_SYSTEMD}
Enable systemd journal: ${ENABLE_SYSTEMD_JOURNAL}
Enable TCP Wrappers: ${ENABLE_TCPWRAP}
Enable libsamplerate: ${ENABLE_LIBSAMPLERATE}
Enable IPv6: ${ENABLE_IPV6}

View file

@ -297,11 +297,12 @@ USA.
<option>
<p><opt>log-target=</opt> The default log target. Use either
<opt>stderr</opt>, <opt>syslog</opt>, <opt>auto</opt>,
<opt>file:PATH</opt> or <opt>newfile:PATH</opt>. <opt>auto</opt> is
equivalent to <opt>sylog</opt> in case <opt>daemonize</opt> is enabled,
otherwise to <opt>stderr</opt>. If set to <opt>file:PATH</opt>, logging
is directed to the file indicated by PATH. <opt>newfile:PATH</opt> is
<opt>stderr</opt>, <opt>syslog</opt>, <opt>journal</opt> (optional),
<opt>auto</opt>, <opt>file:PATH</opt> or <opt>newfile:PATH</opt>. On traditional
systems <opt>auto</opt> is equivalent to <opt>syslog</opt>. On systemd-enabled
systems, auto is equivalent to <opt>journal</opt>, in case <opt>daemonize</opt>
is enabled, and to <opt>stderr</opt> otherwise. If set to <opt>file:PATH</opt>,
logging is directed to the file indicated by PATH. <opt>newfile:PATH</opt> is
otherwise the same as <opt>file:PATH</opt>, but existing files are never
overwritten. If the specified file already exists, a suffix is added to
the file name to avoid overwriting. Defaults to <opt>auto</opt>. The

View file

@ -217,12 +217,13 @@ USA.
</option>
<option>
<p><opt>--log-target</opt><arg>={auto,syslog,stderr,file:PATH,newfile:PATH}</arg></p>
<p><opt>--log-target</opt><arg>={auto,syslog,journal,stderr,file:PATH,newfile:PATH}</arg></p>
<optdesc><p>Specify the log target. If set to <arg>auto</arg>
(which is the default), then logging is directed to syslog when
<opt>--daemonize</opt> is passed, otherwise to
STDERR. If set to <arg>file:PATH</arg>, logging is directed to
STDERR. If set to <arg>journal</arg> logging is directed to the systemd
journal. If set to <arg>file:PATH</arg>, logging is directed to
the file indicated by PATH. <arg>newfile:PATH</arg> is otherwise
the same as file:PATH, but existing files are never overwritten.
If the specified file already exists, a suffix is added to the

View file

@ -687,6 +687,11 @@ libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS += $(X11_CFLAGS)
libpulsecommon_@PA_MAJORMINOR@_la_LDFLAGS += $(X11_LIBS)
endif
if HAVE_SYSTEMD_JOURNAL
libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS += $(JOURNAL_FLAGS)
libpulsecommon_@PA_MAJORMINOR@_la_LDFLAGS += $(JOURNAL_LIBS)
endif
# proplist-util.h uses these header files, but not the library itself!
libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS += $(GLIB20_CFLAGS) $(GTK30_CFLAGS)

View file

@ -323,7 +323,11 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
case ARG_LOG_TARGET:
if (pa_daemon_conf_set_log_target(conf, optarg) < 0) {
#ifdef HAVE_JOURNAL
pa_log(_("Invalid log target: use either 'syslog', 'journal','stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>'."));
#else
pa_log(_("Invalid log target: use either 'syslog', 'stderr' or 'auto' or a valid file name 'file:<path>', 'newfile:<path>'."));
#endif
goto fail;
}
break;

View file

@ -817,7 +817,11 @@ int main(int argc, char *argv[]) {
#endif
if (!conf->log_target) {
#ifdef HAVE_JOURNAL
pa_log_target target = { .type = PA_LOG_JOURNAL, .file = NULL };
#else
pa_log_target target = { .type = PA_LOG_SYSLOG, .file = NULL };
#endif
pa_log_set_target(&target);
}

View file

@ -40,6 +40,10 @@
#include <syslog.h>
#endif
#ifdef HAVE_JOURNAL
#include <systemd/sd-journal.h>
#endif
#include <pulse/gccmacro.h>
#include <pulse/rtclock.h>
#include <pulse/utf8.h>
@ -90,6 +94,18 @@ static const int level_to_syslog[] = {
};
#endif
/* These are actually equivalent to the syslog ones
* but we don't want to depend on syslog.h */
#ifdef HAVE_JOURNAL
static const int level_to_journal[] = {
[PA_LOG_ERROR] = 3,
[PA_LOG_WARN] = 4,
[PA_LOG_NOTICE] = 5,
[PA_LOG_INFO] = 6,
[PA_LOG_DEBUG] = 7
};
#endif
static const char level_to_char[] = {
[PA_LOG_ERROR] = 'E',
[PA_LOG_WARN] = 'W',
@ -129,6 +145,9 @@ int pa_log_set_target(pa_log_target *t) {
switch (t->type) {
case PA_LOG_STDERR:
case PA_LOG_SYSLOG:
#ifdef HAVE_JOURNAL
case PA_LOG_JOURNAL:
#endif
case PA_LOG_NULL:
break;
case PA_LOG_FILE:
@ -318,6 +337,20 @@ static void init_defaults(void) {
} PA_ONCE_END;
}
#ifdef HAVE_SYSLOG_H
static void log_syslog(pa_log_level_t level, char *t, char *timestamp, char *location, char *bt) {
char *local_t;
openlog(ident, LOG_PID, LOG_USER);
if ((local_t = pa_utf8_to_locale(t)))
t = local_t;
syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
pa_xfree(local_t);
}
#endif
void pa_log_levelv_meta(
pa_log_level_t level,
const char*file,
@ -450,19 +483,35 @@ void pa_log_levelv_meta(
}
#ifdef HAVE_SYSLOG_H
case PA_LOG_SYSLOG: {
char *local_t;
openlog(ident, LOG_PID, LOG_USER);
if ((local_t = pa_utf8_to_locale(t)))
t = local_t;
syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
pa_xfree(local_t);
case PA_LOG_SYSLOG:
log_syslog(level, t, timestamp, location, bt);
break;
#endif
#ifdef HAVE_JOURNAL
case PA_LOG_JOURNAL:
if (sd_journal_send("MESSAGE=%s", t,
"PRIORITY=%i", level_to_journal[level],
"CODE_FILE=%s", file,
"CODE_FUNC=%s", func,
"CODE_LINE=%d", line,
NULL) < 0) {
#ifdef HAVE_SYSLOG_H
pa_log_target new_target = { .type = PA_LOG_SYSLOG, .file = NULL };
syslog(level_to_syslog[PA_LOG_ERROR], "%s%s%s", timestamp, __FILE__,
"Error writing logs to the journal. Redirect log messages to syslog.");
log_syslog(level, t, timestamp, location, bt);
#else
pa_log_target new_target = { .type = PA_LOG_STDERR, .file = NULL };
saved_errno = errno;
fprintf(stderr, "%s\n", "Error writing logs to the journal. Redirect log messages to console.");
fprintf(stderr, "%s %s\n", metadata, t);
#endif
pa_log_set_target(&new_target);
}
break;
}
#endif
case PA_LOG_FILE:
@ -570,6 +619,10 @@ pa_log_target *pa_log_parse_target(const char *string) {
t = pa_log_target_new(PA_LOG_STDERR, NULL);
else if (pa_streq(string, "syslog"))
t = pa_log_target_new(PA_LOG_SYSLOG, NULL);
#ifdef HAVE_JOURNAL
else if (pa_streq(string, "journal"))
t = pa_log_target_new(PA_LOG_JOURNAL, NULL);
#endif
else if (pa_streq(string, "null"))
t = pa_log_target_new(PA_LOG_NULL, NULL);
else if (pa_startswith(string, "file:"))
@ -594,6 +647,11 @@ char *pa_log_target_to_string(const pa_log_target *t) {
case PA_LOG_SYSLOG:
string = pa_xstrdup("syslog");
break;
#ifdef HAVE_JOURNAL
case PA_LOG_JOURNAL:
string = pa_xstrdup("journal");
break;
#endif
case PA_LOG_NULL:
string = pa_xstrdup("null");
break;

View file

@ -35,6 +35,9 @@
typedef enum pa_log_target_type {
PA_LOG_STDERR, /* default */
PA_LOG_SYSLOG,
#ifdef HAVE_JOURNAL
PA_LOG_JOURNAL, /* systemd journal */
#endif
PA_LOG_NULL, /* to /dev/null */
PA_LOG_FILE, /* to a user specified file */
PA_LOG_NEWFILE, /* with an automatic suffix to avoid overwriting anything */