Skip to content

Commit fec14d2

Browse files
swsnrhawkw
authored andcommitted
journald: write literal string values to journal (#1714)
See #1710: Do not write strings in Debug representation. ## Motivation As discussed in #1710 writing strings literally makes tracing-journald behave like other journal clients, and allows 3rd party journal readers to extract the original value from the journal without having to "un"-parse the Debug representation of Rust strings. Fixes #1710.
1 parent d1eccec commit fec14d2

File tree

2 files changed

+63
-17
lines changed

2 files changed

+63
-17
lines changed

tracing-journald/src/lib.rs

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,29 @@ struct SpanVisitor<'a> {
188188
prefix: Option<&'a str>,
189189
}
190190

191-
impl Visit for SpanVisitor<'_> {
192-
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
191+
impl SpanVisitor<'_> {
192+
fn put_span_prefix(&mut self) {
193193
write!(self.buf, "S{}", self.depth).unwrap();
194194
if let Some(prefix) = self.prefix {
195195
self.buf.extend_from_slice(prefix.as_bytes());
196196
}
197197
self.buf.push(b'_');
198-
put_debug(self.buf, field.name(), value);
198+
}
199+
}
200+
201+
impl Visit for SpanVisitor<'_> {
202+
fn record_str(&mut self, field: &Field, value: &str) {
203+
self.put_span_prefix();
204+
put_field_length_encoded(self.buf, field.name(), |buf| {
205+
buf.extend_from_slice(value.as_bytes())
206+
});
207+
}
208+
209+
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
210+
self.put_span_prefix();
211+
put_field_length_encoded(self.buf, field.name(), |buf| {
212+
write!(buf, "{:?}", value).unwrap()
213+
});
199214
}
200215
}
201216

@@ -210,23 +225,37 @@ impl<'a> EventVisitor<'a> {
210225
fn new(buf: &'a mut Vec<u8>, prefix: Option<&'a str>) -> Self {
211226
Self { buf, prefix }
212227
}
213-
}
214228

215-
impl Visit for EventVisitor<'_> {
216-
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
229+
fn put_prefix(&mut self, field: &Field) {
217230
if let Some(prefix) = self.prefix {
218231
if field.name() != "message" {
232+
// message maps to the standard MESSAGE field so don't prefix it
219233
self.buf.extend_from_slice(prefix.as_bytes());
220234
self.buf.push(b'_');
221235
}
222236
}
223-
put_debug(self.buf, field.name(), value);
237+
}
238+
}
239+
240+
impl Visit for EventVisitor<'_> {
241+
fn record_str(&mut self, field: &Field, value: &str) {
242+
self.put_prefix(field);
243+
put_field_length_encoded(self.buf, field.name(), |buf| {
244+
buf.extend_from_slice(value.as_bytes())
245+
});
246+
}
247+
248+
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
249+
self.put_prefix(field);
250+
put_field_length_encoded(self.buf, field.name(), |buf| {
251+
write!(buf, "{:?}", value).unwrap()
252+
});
224253
}
225254
}
226255

227256
fn put_metadata(buf: &mut Vec<u8>, meta: &Metadata, span: Option<usize>) {
228257
if span.is_none() {
229-
put_field(
258+
put_field_wellformed(
230259
buf,
231260
"PRIORITY",
232261
match *meta.level() {
@@ -241,12 +270,12 @@ fn put_metadata(buf: &mut Vec<u8>, meta: &Metadata, span: Option<usize>) {
241270
if let Some(n) = span {
242271
write!(buf, "S{}_", n).unwrap();
243272
}
244-
put_field(buf, "TARGET", meta.target().as_bytes());
273+
put_field_wellformed(buf, "TARGET", meta.target().as_bytes());
245274
if let Some(file) = meta.file() {
246275
if let Some(n) = span {
247276
write!(buf, "S{}_", n).unwrap();
248277
}
249-
put_field(buf, "CODE_FILE", file.as_bytes());
278+
put_field_wellformed(buf, "CODE_FILE", file.as_bytes());
250279
}
251280
if let Some(line) = meta.line() {
252281
if let Some(n) = span {
@@ -257,12 +286,21 @@ fn put_metadata(buf: &mut Vec<u8>, meta: &Metadata, span: Option<usize>) {
257286
}
258287
}
259288

260-
fn put_debug(buf: &mut Vec<u8>, name: &str, value: &dyn fmt::Debug) {
289+
/// Append a sanitized and length-encoded field into `buf`.
290+
///
291+
/// Unlike `put_field_wellformed` this function handles arbitrary field names and values.
292+
///
293+
/// `name` denotes the field name. It gets sanitized before being appended to `buf`.
294+
///
295+
/// `write_value` is invoked with `buf` as argument to append the value data to `buf`. It must
296+
/// not delete from `buf`, but may append arbitrary data. This function then determines the length
297+
/// of the data written and adds it in the appropriate place in `buf`.
298+
fn put_field_length_encoded(buf: &mut Vec<u8>, name: &str, write_value: impl FnOnce(&mut Vec<u8>)) {
261299
sanitize_name(name, buf);
262300
buf.push(b'\n');
263301
buf.extend_from_slice(&[0; 8]); // Length tag, to be populated
264302
let start = buf.len();
265-
write!(buf, "{:?}", value).unwrap();
303+
write_value(buf);
266304
let end = buf.len();
267305
buf[start - 8..start].copy_from_slice(&((end - start) as u64).to_le_bytes());
268306
buf.push(b'\n');
@@ -279,14 +317,23 @@ fn sanitize_name(name: &str, buf: &mut Vec<u8>) {
279317
);
280318
}
281319

282-
/// Append arbitrary data with a well-formed name
283-
fn put_field(buf: &mut Vec<u8>, name: &str, value: &[u8]) {
320+
/// Append arbitrary data with a well-formed name and value.
321+
///
322+
/// `value` must not contain an internal newline, because this function writes
323+
/// `value` in the new-line separated format.
324+
///
325+
/// For a "newline-safe" variant, see `put_field_length_encoded`.
326+
fn put_field_wellformed(buf: &mut Vec<u8>, name: &str, value: &[u8]) {
284327
buf.extend_from_slice(name.as_bytes());
285328
buf.push(b'\n');
286329
put_value(buf, value);
287330
}
288331

289-
/// Write the value portion of a key-value pair
332+
/// Write the value portion of a key-value pair, in newline separated format.
333+
///
334+
/// `value` must not contain an internal newline.
335+
///
336+
/// For a "newline-safe" variant, see `put_field_length_encoded`.
290337
fn put_value(buf: &mut Vec<u8>, value: &[u8]) {
291338
buf.extend_from_slice(&(value.len() as u64).to_le_bytes());
292339
buf.extend_from_slice(value);

tracing-journald/tests/journal.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,7 @@ fn read_from_journal(test_name: &str) -> Vec<HashMap<String, Field>> {
8888
.args(&["--user", "--output=json"])
8989
// Filter by the PID of the current test process
9090
.arg(format!("_PID={}", std::process::id()))
91-
// tracing-journald logs strings in their debug representation
92-
.arg(format!("TEST_NAME={:?}", test_name))
91+
.arg(format!("TEST_NAME={}", test_name))
9392
.output()
9493
.unwrap()
9594
.stdout,

0 commit comments

Comments
 (0)