diff --git a/tracing-journald/src/lib.rs b/tracing-journald/src/lib.rs index 2433418ce9..7be8fa422a 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. @@ -155,6 +157,31 @@ impl Subscriber { &self.syslog_identifier } + /// Sets the syslog facility for this logger. + /// + /// The syslog facility comes from the classic syslog interface (`openlog()` + /// and `syslog()`). 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. + 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 +284,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 +298,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 +437,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, + }; + put_field_wellformed(buf, "SYSLOG_FACILITY", format!("{}", value >> 3).as_bytes()); +} + fn put_metadata(buf: &mut Vec, meta: &Metadata, prefix: Option<&str>) { if let Some(prefix) = prefix { write!(buf, "{}", prefix).unwrap();