Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow changing the casing of the level field value #7

Merged
merged 3 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "tracing-ndjson"
version = "0.1.2"
version = "0.1.3"
edition = "2021"
license = "MIT"
authors = ["Cole Mackenzie"]
Expand Down
18 changes: 14 additions & 4 deletions src/formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::{visitor, Error};

const DEFAULT_TIMESTAMP_FORMAT: crate::TimestampFormat = crate::TimestampFormat::Rfc3339;
const DEFAULT_LEVEL_NAME: &str = "level";
const DEFAULT_LEVEL_VALUE_CASING: crate::Casing = crate::Casing::Lowercase;
const DEFAULT_MESSAGE_NAME: &str = "message";
const DEFAULT_TARGET_NAME: &str = "target";
const DEFAULT_TIMESTAMP_NAME: &str = "timestamp";
Expand All @@ -21,6 +22,7 @@ const DEFAULT_FLATTEN_FIELDS: bool = true;
/// This is used to format the event field in the JSON output.
pub struct JsonEventFormatter {
level_name: &'static str,
level_value_casing: crate::Casing,
message_name: &'static str,
target_name: &'static str,
timestamp_name: &'static str,
Expand All @@ -32,6 +34,7 @@ impl Default for JsonEventFormatter {
fn default() -> Self {
Self {
level_name: DEFAULT_LEVEL_NAME,
level_value_casing: DEFAULT_LEVEL_VALUE_CASING,
message_name: DEFAULT_MESSAGE_NAME,
target_name: DEFAULT_TARGET_NAME,
timestamp_name: DEFAULT_TIMESTAMP_NAME,
Expand All @@ -51,6 +54,11 @@ impl JsonEventFormatter {
self
}

pub fn with_level_value_casing(mut self, level_value_casing: crate::Casing) -> Self {
self.level_value_casing = level_value_casing;
self
}

pub fn with_message_name(mut self, message_name: &'static str) -> Self {
self.message_name = message_name;
self
Expand Down Expand Up @@ -93,11 +101,13 @@ where
let mut binding = serde_json::Serializer::new(Vec::new());
let mut serializer = binding.serialize_map(None).map_err(Error::Serde)?;

let level_str = match self.level_value_casing {
crate::Casing::Lowercase => event.metadata().level().to_string().to_lowercase(),
crate::Casing::Uppercase => event.metadata().level().to_string().to_uppercase(),
};

serializer
.serialize_entry(
self.level_name,
&event.metadata().level().to_string().to_lowercase(),
)
.serialize_entry(self.level_name, &level_str)
.map_err(Error::Serde)?;

if matches!(
Expand Down
15 changes: 15 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ impl TimestampFormat {
}
}

pub enum Casing {
Lowercase,
Uppercase,
}

#[derive(Debug, thiserror::Error)]
enum Error {
#[error("fmt error: {0}")]
Expand All @@ -110,6 +115,7 @@ impl From<Error> for std::fmt::Error {
/// This is used to configure the JSON formatter.
/// The default configuration is:
/// * level_name: "level"
/// * level_value_casing: Casing::Lowercase
/// * message_name: "message"
/// * target_name: "target"
/// * timestamp_name: "timestamp"
Expand All @@ -125,6 +131,7 @@ impl From<Error> for std::fmt::Error {
/// .with(
/// tracing_ndjson::Builder::default()
/// .with_level_name("severity")
/// .with_level_value_casing(tracing_ndjson::Casing::Uppercase)
/// .with_message_name("msg")
/// .with_timestamp_name("ts")
/// .with_timestamp_format(tracing_ndjson::TimestampFormat::Unix)
Expand Down Expand Up @@ -166,6 +173,13 @@ impl Builder {
self
}

/// Set the casing for the level field value.
/// The default is Casing::Lowercase.
pub fn with_level_value_casing(mut self, casing: Casing) -> Self {
self.events = self.events.with_level_value_casing(casing);
self
}

/// Set the field name for the message field.
/// The default is "message".
pub fn with_message_name(mut self, message_name: &'static str) -> Self {
Expand Down Expand Up @@ -277,6 +291,7 @@ mod tests {
let subscriber = tracing_subscriber::registry().with(
builder()
.with_level_name("severity")
.with_level_value_casing(Casing::Uppercase)
.with_message_name("msg")
.with_timestamp_name("ts")
.with_timestamp_format(TimestampFormat::Unix)
Expand Down
48 changes: 22 additions & 26 deletions src/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ where
pub fn finish(self) -> Result<(), W::Error> {
self.state
}

/// Serialize a key-value pair, replacing the message field if overwrite_message_name is set.
pub fn serialize_entry<V>(&mut self, key: &str, value: V) -> Result<(), W::Error>
where
V: serde::Serialize,
{
if self.overwrite_message_name.is_some() && key == "message" {
self.serializer
.serialize_entry(self.overwrite_message_name.expect("message"), &value)
} else {
self.serializer.serialize_entry(key, &value)
}
}
}

impl<'a, W> Visit for Visitor<'a, W>
Expand All @@ -32,49 +45,43 @@ where
{
fn record_f64(&mut self, field: &tracing_core::Field, value: f64) {
if self.state.is_ok() {
self.state = self.serializer.serialize_entry(field.name(), &value);
self.state = self.serialize_entry(field.name(), value)
}
}

fn record_i64(&mut self, field: &tracing_core::Field, value: i64) {
if self.state.is_ok() {
self.state = self.serializer.serialize_entry(field.name(), &value);
self.state = self.serialize_entry(field.name(), value)
}
}

fn record_u64(&mut self, field: &tracing_core::Field, value: u64) {
if self.state.is_ok() {
self.state = self.serializer.serialize_entry(field.name(), &value);
self.state = self.serialize_entry(field.name(), value)
}
}

fn record_i128(&mut self, field: &tracing_core::Field, value: i128) {
if self.state.is_ok() {
self.state = self.serializer.serialize_entry(field.name(), &value);
self.state = self.serialize_entry(field.name(), value)
}
}

fn record_u128(&mut self, field: &tracing_core::Field, value: u128) {
if self.state.is_ok() {
self.state = self.serializer.serialize_entry(field.name(), &value);
self.state = self.serialize_entry(field.name(), value)
}
}

fn record_bool(&mut self, field: &tracing_core::Field, value: bool) {
if self.state.is_ok() {
self.state = self.serializer.serialize_entry(field.name(), &value);
self.state = self.serialize_entry(field.name(), value)
}
}

fn record_str(&mut self, field: &tracing_core::Field, value: &str) {
if self.state.is_ok() {
if self.overwrite_message_name.is_some() && field.name() == "message" {
self.state = self
.serializer
.serialize_entry(self.overwrite_message_name.expect("message"), &value);
} else {
self.state = self.serializer.serialize_entry(field.name(), &value);
}
self.state = self.serialize_entry(field.name(), value)
}
}

Expand All @@ -84,24 +91,13 @@ where
value: &(dyn std::error::Error + 'static),
) {
if self.state.is_ok() {
self.state = self
.serializer
.serialize_entry(field.name(), &format_args!("{}", value).to_string());
self.state = self.serialize_entry(field.name(), &value.to_string())
}
}

fn record_debug(&mut self, field: &tracing_core::Field, value: &dyn std::fmt::Debug) {
if self.state.is_ok() {
if self.overwrite_message_name.is_some() && field.name() == "message" {
self.state = self.serializer.serialize_entry(
self.overwrite_message_name.expect("message"),
&format_args!("{:?}", value).to_string(),
);
} else {
self.state = self
.serializer
.serialize_entry(field.name(), &format_args!("{:?}", value).to_string());
}
self.state = self.serialize_entry(field.name(), &format!("{:?}", value))
}
}
}