From cc402e64b4636867c900187319f378eb463dfc5f Mon Sep 17 00:00:00 2001 From: Ossi Herrala Date: Thu, 29 Dec 2022 23:23:14 +0200 Subject: [PATCH] journald: add support for specifying syslog facility The syslog facility is optional and if it is not specified, it is not included in JournalD message. The SyslogFacility enum contains all the fields specified in syslog(3) for facility and numbers are mapped using libc's definition. I couldn't find why I needed to bitshift (line 466) the libc facility values right to get the proper result, but this worked for me. --- tracing-journald/src/lib.rs | 118 +++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 2 deletions(-) diff --git a/tracing-journald/src/lib.rs b/tracing-journald/src/lib.rs index 2433418ce9..aa8cea64a7 100644 --- a/tracing-journald/src/lib.rs +++ b/tracing-journald/src/lib.rs @@ -85,6 +85,7 @@ pub struct Subscriber { socket: UnixDatagram, field_prefix: Option, syslog_identifier: String, + syslog_facility: Option, } #[cfg(unix)] @@ -109,6 +110,7 @@ impl Subscriber { .map(|n| n.to_string_lossy().into_owned()) // If we fail to get the name of the current executable fall back to an empty string. .unwrap_or_else(String::new), + syslog_facility: None, }; // Check that we can talk to journald, by sending empty payload which journald discards. // However if the socket didn't exist or if none listened we'd get an error here. @@ -131,8 +133,8 @@ impl Subscriber { /// Sets the syslog identifier for this logger. /// - /// The syslog identifier comes from the classic syslog interface (`openlog()` - /// and `syslog()`) and tags log entries with a given identifier. + /// The syslog identifier comes from the classic syslog interface (`openlog(3)` + /// and `syslog(3)`) and tags log entries with a given identifier. /// Systemd exposes it in the `SYSLOG_IDENTIFIER` journal field, and allows /// filtering log messages by syslog identifier with `journalctl -t`. /// Unlike the unit (`journalctl -u`) this field is not trusted, i.e. applications @@ -151,10 +153,39 @@ impl Subscriber { } /// Returns the syslog identifier in use. + /// + /// A syslog facility can be set using [`Subscriber::with_syslog_identifier`]. pub fn syslog_identifier(&self) -> &str { &self.syslog_identifier } + /// Sets the syslog facility for this logger. + /// + /// The syslog facility comes from the classic syslog interface (`openlog(3)` + /// and `syslog(3)`). In syslog, the facility argument is used to specify + /// what type of program is logging the message. This lets the configuration + /// file specify that messages from different facilities will be handled + /// differently. Systemd exposes it in the `SYSLOG_FACILITY` journal field, + /// and allows filtering log messages by syslog facility with `journalctl + /// --facility`. + /// + /// See [Journal Fields](https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html) + /// and [journalctl](https://www.freedesktop.org/software/systemd/man/journalctl.html) + /// for more information. + /// + /// If not set, this field is left out. + pub fn with_syslog_facility(mut self, facility: SyslogFacility) -> Self { + self.syslog_facility = Some(facility); + self + } + + /// Returns the syslog facility in use. + /// + /// A syslog facility can be set using [`Subscriber::with_syslog_facility`]. + pub fn syslog_facility(&self) -> Option { + self.syslog_facility + } + #[cfg(not(unix))] fn send_payload(&self, _opayload: &[u8]) -> io::Result<()> { Err(io::Error::new( @@ -257,6 +288,9 @@ where put_field_length_encoded(&mut buf, "SYSLOG_IDENTIFIER", |buf| { write!(buf, "{}", self.syslog_identifier).unwrap() }); + if let Some(facility) = self.syslog_facility { + put_facility(&mut buf, facility); + } event.record(&mut EventVisitor::new( &mut buf, @@ -268,6 +302,60 @@ where } } +/// The facility argument is used to specify what type of program is logging the +/// message. This lets the configuration file specify that messages from +/// different facilities will be handled differently. +#[derive(Copy, Clone)] +pub enum SyslogFacility { + /// security/authorization messages + Auth, + /// security/authorization messages (private) + Authpriv, + /// clock daemon (cron and at) + Cron, + /// system daemons without separate facility value + Daemon, + /// ftp daemon + Ftp, + /// kernel messages (these can't be generated from user + /// processes) + Kern, + ///reserved for local use + Local0, + ///reserved for local use + Local1, + ///reserved for local use + Local2, + ///reserved for local use + Local3, + ///reserved for local use + Local4, + ///reserved for local use + Local5, + ///reserved for local use + Local6, + ///reserved for local use + Local7, + /// line printer subsystem + Lpr, + /// mail subsystem + Mail, + /// USENET news subsystem + News, + /// messages generated internally by syslogd(8) + Syslog, + /// generic user-level messages + User, + /// UUCP subsystem + Uucp, +} + +impl Default for SyslogFacility { + fn default() -> Self { + Self::User + } +} + struct SpanFields(Vec); struct SpanVisitor<'a> { @@ -353,6 +441,32 @@ fn put_priority(buf: &mut Vec, meta: &Metadata) { ); } +fn put_facility(buf: &mut Vec, facility: SyslogFacility) { + let value = match facility { + SyslogFacility::Auth => libc::LOG_AUTH, + SyslogFacility::Authpriv => libc::LOG_AUTHPRIV, + SyslogFacility::Cron => libc::LOG_CRON, + SyslogFacility::Daemon => libc::LOG_DAEMON, + SyslogFacility::Ftp => libc::LOG_FTP, + SyslogFacility::Kern => libc::LOG_KERN, + SyslogFacility::Local0 => libc::LOG_LOCAL0, + SyslogFacility::Local1 => libc::LOG_LOCAL1, + SyslogFacility::Local2 => libc::LOG_LOCAL2, + SyslogFacility::Local3 => libc::LOG_LOCAL3, + SyslogFacility::Local4 => libc::LOG_LOCAL4, + SyslogFacility::Local5 => libc::LOG_LOCAL5, + SyslogFacility::Local6 => libc::LOG_LOCAL6, + SyslogFacility::Local7 => libc::LOG_LOCAL7, + SyslogFacility::Lpr => libc::LOG_LPR, + SyslogFacility::Mail => libc::LOG_MAIL, + SyslogFacility::News => libc::LOG_NEWS, + SyslogFacility::Syslog => libc::LOG_SYSLOG, + SyslogFacility::User => libc::LOG_USER, + SyslogFacility::Uucp => libc::LOG_UUCP, + }; + writeln!(buf, "SYSLOG_FACILITY={}", value).unwrap(); +} + fn put_metadata(buf: &mut Vec, meta: &Metadata, prefix: Option<&str>) { if let Some(prefix) = prefix { write!(buf, "{}", prefix).unwrap();