Skip to content

Commit

Permalink
Consolidate logic in logging to avoid allocations where possible
Browse files Browse the repository at this point in the history
This does two things to try and avoid allocations where possible in
logging.

1. Removing a to_string() call on Priority by adding as_str function.
2. Consolidating the extend calls on the buffer into a single call.

I've implemented the conversion to string as a private member function
to avoid adding to the API surface of Priority.

The second allows extend to take advantage of the size_hint generated by
the iterator over the slices.  Since the length of all of the data is
known, flat_map will provide a correct size_hint for the full set of
data, providing the reserve call within extend the correctly needed
value, and avoiding potentially bumping the capacity multiple times.
  • Loading branch information
dfreese committed Dec 19, 2022
1 parent 8c781c9 commit 5d6f10a
Showing 1 changed file with 50 additions and 10 deletions.
60 changes: 50 additions & 10 deletions src/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ impl std::convert::From<Priority> for u8 {
}
}

impl Priority {
fn as_str(&self) -> &str {
match self {
Priority::Emergency => "0",
Priority::Alert => "1",
Priority::Critical => "2",
Priority::Error => "3",
Priority::Warning => "4",
Priority::Notice => "5",
Priority::Info => "6",
Priority::Debug => "7",
}
}
}

#[inline(always)]
fn is_valid_char(c: char) -> bool {
c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_'
Expand Down Expand Up @@ -103,11 +118,17 @@ fn is_valid_field(input: &str) -> bool {
///
/// See <https://systemd.io/JOURNAL_NATIVE_PROTOCOL/> for details.
fn add_field_and_payload_explicit_length(data: &mut Vec<u8>, field: &str, payload: &str) {
data.extend(field.as_bytes());
data.push(b'\n');
data.extend(&(payload.len() as u64).to_le_bytes());
data.extend(payload.as_bytes());
data.push(b'\n');
data.extend(
[
field.as_bytes(),
b"\n",
&(payload.len() as u64).to_le_bytes(),
payload.as_bytes(),
b"\n",
]
.iter()
.flat_map(|x| x.iter()),
);
}

/// Add a journal `field` and its `payload` to journal fields `data` with appropriate encoding.
Expand All @@ -124,10 +145,11 @@ fn add_field_and_payload(data: &mut Vec<u8>, field: &str, payload: &str) {
add_field_and_payload_explicit_length(data, field, payload);
} else {
// If payload doesn't contain an newline directly write the field name and the payload
data.extend(field.as_bytes());
data.push(b'=');
data.extend(payload.as_bytes());
data.push(b'\n');
data.extend(
[field.as_bytes(), b"=", payload.as_bytes(), b"\n"]
.iter()
.flat_map(|x| x.iter()),
);
}
}
}
Expand All @@ -149,7 +171,7 @@ where
.context("failed to open datagram socket")?;

let mut data = Vec::new();
add_field_and_payload(&mut data, "PRIORITY", &(u8::from(priority)).to_string());
add_field_and_payload(&mut data, "PRIORITY", priority.as_str());
add_field_and_payload(&mut data, "MESSAGE", msg);
for (ref k, ref v) in vars {
if k.as_ref() != "PRIORITY" && k.as_ref() != "MESSAGE" {
Expand Down Expand Up @@ -342,6 +364,24 @@ mod tests {
}
}

#[test]
fn test_priority_as_str_matches_to_string() {
let priorities = [
Priority::Emergency,
Priority::Alert,
Priority::Critical,
Priority::Error,
Priority::Warning,
Priority::Notice,
Priority::Info,
Priority::Debug,
];

for priority in &priorities {
assert_eq!(&(u8::from(priority.clone())).to_string(), priority.as_str());
}
}

#[test]
fn test_journal_print_simple() {
if !ensure_journald_socket() {
Expand Down

0 comments on commit 5d6f10a

Please sign in to comment.