Skip to content

Commit

Permalink
JsonFormatter outputs key-values to a new kv field
Browse files Browse the repository at this point in the history
  • Loading branch information
SpriteOvO committed Nov 24, 2024
1 parent e050b52 commit 6e4df18
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 12 deletions.
2 changes: 1 addition & 1 deletion spdlog/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ native = []
libsystemd = ["libsystemd-sys"]
multi-thread = ["crossbeam"]
runtime-pattern = ["spdlog-internal"]
serde_json = ["serde", "dep:serde_json"]
serde_json = ["serde", "dep:serde_json", "value-bag/serde1"]

[dependencies]
arc-swap = "1.5.1"
Expand Down
20 changes: 12 additions & 8 deletions spdlog/src/formatter/full_formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,18 @@ impl FullFormatter {
}
}

fmt_with_time(ctx, record, |mut time: TimeDate| {
dest.write_str("[")?;
dest.write_str(time.full_second_str())?;
dest.write_str(".")?;
write!(dest, "{:03}", time.millisecond())?;
dest.write_str("] [")?;
Ok(())
})?;
fmt_with_time(
ctx,
record,
|mut time: TimeDate| -> Result<(), fmt::Error> {
dest.write_str("[")?;
dest.write_str(time.full_second_str())?;
dest.write_str(".")?;
write!(dest, "{:03}", time.millisecond())?;
dest.write_str("] [")?;
Ok(())
},
)?;

if let Some(logger_name) = record.logger_name() {
dest.write_str(logger_name)?;
Expand Down
64 changes: 61 additions & 3 deletions spdlog/src/formatter/json_formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use std::{
};

use cfg_if::cfg_if;
use serde::{ser::SerializeStruct, Serialize};
use serde::{
ser::{SerializeMap, SerializeStruct},
Serialize, Serializer,
};

use crate::{
formatter::{Formatter, FormatterContext},
Expand All @@ -21,7 +24,7 @@ struct JsonRecord<'a>(&'a Record<'a>);
impl Serialize for JsonRecord<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
S: Serializer,
{
let fields_len =
4 + opt_to_num(self.0.logger_name()) + opt_to_num(self.0.source_location());
Expand All @@ -40,6 +43,11 @@ impl Serialize for JsonRecord<'_> {
.expect("invalid timestamp"),
)?;
record.serialize_field("payload", self.0.payload())?;

if !self.0.key_values().is_empty() {
record.serialize_field("kv", &JsonKV(&self.0))?;
}

if let Some(logger_name) = self.0.logger_name() {
record.serialize_field("logger", logger_name)?;
}
Expand All @@ -52,6 +60,22 @@ impl Serialize for JsonRecord<'_> {
}
}

struct JsonKV<'a>(&'a Record<'a>);

impl<'a> Serialize for JsonKV<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let kv = self.0.key_values();
let mut map = serializer.serialize_map(Some(kv.len()))?;
for (key, value) in kv {
map.serialize_entry(key.as_str(), &value)?;
}
map.end()
}
}

impl<'a> From<&'a Record<'a>> for JsonRecord<'a> {
fn from(value: &'a Record<'a>) -> Self {
JsonRecord(value)
Expand Down Expand Up @@ -126,6 +150,13 @@ impl From<JsonFormatterError> for crate::Error {
/// {"level":"error","timestamp":1722817541459,"payload":"something went wrong","logger":"app-component","tid":3478045}
/// ```
///
/// - If key-values are present:
///
/// ```json
/// {"level":"info","timestamp":1722817541459,"payload":"hello, world!","kv":{"k1":123,"k2":"cool"},"tid":3478045}
/// {"level":"error","timestamp":1722817541459,"payload":"something went wrong","kv":{"k1":123,"k2":"cool"},"tid":3478045}
/// ```
///
/// - If crate feature `source-location` is enabled:
///
/// ```json
Expand Down Expand Up @@ -193,7 +224,7 @@ mod tests {
use chrono::prelude::*;

use super::*;
use crate::{Level, SourceLocation, __EOL};
use crate::{kv, Level, SourceLocation, __EOL};

#[test]
fn should_format_json() {
Expand Down Expand Up @@ -269,4 +300,31 @@ mod tests {
)
);
}

#[test]
fn should_format_json_with_kv() {
let mut dest = StringBuf::new();
let formatter = JsonFormatter::new();
let kvs = [
(kv::Key::__from_static_str("k1"), kv::Value::from(114)),
(kv::Key::__from_static_str("k2"), kv::Value::from("514")),
];
let record = Record::new(Level::Info, "payload", None, None, &kvs);
let mut ctx = FormatterContext::new();
formatter.format(&record, &mut dest, &mut ctx).unwrap();

let local_time: DateTime<Local> = record.time().into();

assert_eq!(ctx.style_range(), None);
assert_eq!(
dest.to_string(),
format!(
r#"{{"level":"info","timestamp":{},"payload":"{}","kv":{{"k1":114,"k2":"514"}},"tid":{}}}{}"#,
local_time.timestamp_millis(),
"payload",
record.tid(),
__EOL
)
);
}
}

0 comments on commit 6e4df18

Please sign in to comment.