Skip to content

Commit 83cb2a1

Browse files
Finomnisdavidbarsky
authored andcommitted
journald: allow custom journal fields (#2708)
It's currently not possible to customize how messages will get send to journald. This became apparent in #2425, where first a specific API got designed, but then it was decided that users should not get restricted in only a subset of fields, but should be able to simply choose by themselves what fields get set with what values. So in a sense, this is the successor/rework of #2425. Allow custom fields to be set in tracing-journald. - [x] How should we deal with fields that also get supplied by other options? For example, setting `SYSLOG_IDENTIFIER` here and also setting `.with_syslog_identifier()` will send said field twice, potentially with differing values. Is that a problem? - Answer: No, this is not a problem. Closes #2425
1 parent 2eb55f3 commit 83cb2a1

File tree

2 files changed

+59
-0
lines changed

2 files changed

+59
-0
lines changed

tracing-journald/src/lib.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ pub struct Layer {
8383
socket: UnixDatagram,
8484
field_prefix: Option<String>,
8585
syslog_identifier: String,
86+
additional_fields: Vec<u8>,
8687
}
8788

8889
#[cfg(unix)]
@@ -107,6 +108,7 @@ impl Layer {
107108
.map(|n| n.to_string_lossy().into_owned())
108109
// If we fail to get the name of the current executable fall back to an empty string.
109110
.unwrap_or_else(String::new),
111+
additional_fields: Vec::new(),
110112
};
111113
// Check that we can talk to journald, by sending empty payload which journald discards.
112114
// However if the socket didn't exist or if none listened we'd get an error here.
@@ -148,6 +150,40 @@ impl Layer {
148150
self
149151
}
150152

153+
/// Adds fields that will get be passed to journald with every log entry.
154+
///
155+
/// The input values of this function are interpreted as `(field, value)` pairs.
156+
///
157+
/// This can for example be used to configure the syslog facility.
158+
/// See [Journal Fields](https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html)
159+
/// and [journalctl](https://www.freedesktop.org/software/systemd/man/journalctl.html)
160+
/// for more information.
161+
///
162+
/// Fields specified using this method will be added to the journald
163+
/// message alongside fields generated from the event's fields, its
164+
/// metadata, and the span context. If the name of a field provided using
165+
/// this method is the same as the name of a field generated by the
166+
/// layer, both fields will be sent to journald.
167+
///
168+
/// ```no_run
169+
/// # use tracing_journald::Layer;
170+
/// let layer = Layer::new()
171+
/// .unwrap()
172+
/// .with_custom_fields([("SYSLOG_FACILITY", "17")]);
173+
/// ```
174+
///
175+
pub fn with_custom_fields<T: AsRef<str>, U: AsRef<[u8]>>(
176+
mut self,
177+
fields: impl IntoIterator<Item = (T, U)>,
178+
) -> Self {
179+
for (name, value) in fields {
180+
put_field_length_encoded(&mut self.additional_fields, name.as_ref(), |buf| {
181+
buf.extend_from_slice(value.as_ref())
182+
})
183+
}
184+
self
185+
}
186+
151187
/// Returns the syslog identifier in use.
152188
pub fn syslog_identifier(&self) -> &str {
153189
&self.syslog_identifier
@@ -255,6 +291,7 @@ where
255291
put_field_length_encoded(&mut buf, "SYSLOG_IDENTIFIER", |buf| {
256292
write!(buf, "{}", self.syslog_identifier).unwrap()
257293
});
294+
buf.extend_from_slice(&self.additional_fields);
258295

259296
event.record(&mut EventVisitor::new(
260297
&mut buf,

tracing-journald/tests/journal.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,28 @@ fn simple_metadata() {
238238
});
239239
}
240240

241+
#[test]
242+
fn journal_fields() {
243+
let sub = Layer::new()
244+
.unwrap()
245+
.with_field_prefix(None)
246+
.with_custom_fields([("SYSLOG_FACILITY", "17")])
247+
.with_custom_fields([("ABC", "dEf"), ("XYZ", "123")]);
248+
with_journald_layer(sub, || {
249+
info!(test.name = "journal_fields", "Hello World");
250+
251+
let message = retry_read_one_line_from_journal("journal_fields");
252+
assert_eq!(message["MESSAGE"], "Hello World");
253+
assert_eq!(message["PRIORITY"], "5");
254+
assert_eq!(message["TARGET"], "journal");
255+
assert_eq!(message["SYSLOG_FACILITY"], "17");
256+
assert_eq!(message["ABC"], "dEf");
257+
assert_eq!(message["XYZ"], "123");
258+
assert!(message["CODE_FILE"].as_text().is_some());
259+
assert!(message["CODE_LINE"].as_text().is_some());
260+
});
261+
}
262+
241263
#[test]
242264
fn span_metadata() {
243265
with_journald(|| {

0 commit comments

Comments
 (0)