diff --git a/tracing-journald/src/lib.rs b/tracing-journald/src/lib.rs index 237a0de647..4ad5a58d5b 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, + additional_fields: Vec, } #[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), + additional_fields: Vec::new(), }; // 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. @@ -150,6 +152,40 @@ impl Subscriber { self } + /// Adds fields that will get be passed to journald with every log entry. + /// + /// The input values of this function are interpreted as `(field, value)` pairs. + /// + /// This can for example be used to configure the syslog 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. + /// + /// Fields specified using this method will be added to the journald + /// message alongside fields generated from the event's fields, its + /// metadata, and the span context. If the name of a field provided using + /// this method is the same as the name of a field generated by the + /// subscriber, both fields will be sent to journald. + /// + /// ```no_run + /// # use tracing_journald::Subscriber; + /// let sub = Subscriber::new() + /// .unwrap() + /// .with_custom_fields([("SYSLOG_FACILITY", "17")]); + /// ``` + /// + pub fn with_custom_fields, U: AsRef<[u8]>>( + mut self, + fields: impl IntoIterator, + ) -> Self { + for (name, value) in fields { + put_field_length_encoded(&mut self.additional_fields, name.as_ref(), |buf| { + buf.extend_from_slice(value.as_ref()) + }) + } + self + } + /// Returns the syslog identifier in use. pub fn syslog_identifier(&self) -> &str { &self.syslog_identifier @@ -257,6 +293,7 @@ where put_field_length_encoded(&mut buf, "SYSLOG_IDENTIFIER", |buf| { write!(buf, "{}", self.syslog_identifier).unwrap() }); + buf.extend_from_slice(&self.additional_fields); event.record(&mut EventVisitor::new( &mut buf, diff --git a/tracing-journald/tests/journal.rs b/tracing-journald/tests/journal.rs index 5eab5f79cd..24a7fc7611 100644 --- a/tracing-journald/tests/journal.rs +++ b/tracing-journald/tests/journal.rs @@ -237,6 +237,28 @@ fn simple_metadata() { }); } +#[test] +fn journal_fields() { + let sub = Subscriber::new() + .unwrap() + .with_field_prefix(None) + .with_custom_fields([("SYSLOG_FACILITY", "17")]) + .with_custom_fields([("ABC", "dEf"), ("XYZ", "123")]); + with_journald_subscriber(sub, || { + info!(test.name = "journal_fields", "Hello World"); + + let message = retry_read_one_line_from_journal("journal_fields"); + assert_eq!(message["MESSAGE"], "Hello World"); + assert_eq!(message["PRIORITY"], "5"); + assert_eq!(message["TARGET"], "journal"); + assert_eq!(message["SYSLOG_FACILITY"], "17"); + assert_eq!(message["ABC"], "dEf"); + assert_eq!(message["XYZ"], "123"); + assert!(message["CODE_FILE"].as_text().is_some()); + assert!(message["CODE_LINE"].as_text().is_some()); + }); +} + #[test] fn span_metadata() { with_journald(|| {