systemprompt_logging/services/
json_format.rs1use std::fmt;
18
19use serde_json::{Map, Value};
20use tracing::field::{Field, Visit};
21use tracing::{Event, Subscriber};
22use tracing_subscriber::fmt::format::{Format, Full, Writer};
23use tracing_subscriber::fmt::time::SystemTime;
24use tracing_subscriber::fmt::{FmtContext, FormatEvent, FormatFields, FormattedFields};
25use tracing_subscriber::registry::LookupSpan;
26
27use crate::layer::InstanceLabel;
28use crate::sanitize::{REDACTION_PLACEHOLDER, is_redacted, is_system_sentinel};
29use crate::services::output::OutputState;
30
31#[derive(Debug, Clone, Default)]
33pub struct JsonEventFormat {
34 instance: InstanceLabel,
35}
36
37#[derive(Debug, Clone, Default)]
40pub struct ConsoleEventFormat {
41 text: Format<Full, SystemTime>,
42 json: JsonEventFormat,
43 output: OutputState,
44}
45
46impl ConsoleEventFormat {
47 #[must_use]
48 pub fn with_output(output: OutputState) -> Self {
49 Self {
50 output,
51 ..Self::default()
52 }
53 }
54
55 pub(crate) fn labelled(instance: InstanceLabel, output: OutputState) -> Self {
56 Self {
57 text: Format::default(),
58 json: JsonEventFormat { instance },
59 output,
60 }
61 }
62}
63
64impl<S, N> FormatEvent<S, N> for ConsoleEventFormat
65where
66 S: Subscriber + for<'a> LookupSpan<'a>,
67 N: for<'a> FormatFields<'a> + 'static,
68{
69 fn format_event(
70 &self,
71 ctx: &FmtContext<'_, S, N>,
72 writer: Writer<'_>,
73 event: &Event<'_>,
74 ) -> fmt::Result {
75 if self.output.is_json_console() {
76 self.json.format_event(ctx, writer, event)
77 } else {
78 self.text.format_event(ctx, writer, event)
79 }
80 }
81}
82
83impl<S, N> FormatEvent<S, N> for JsonEventFormat
84where
85 S: Subscriber + for<'a> LookupSpan<'a>,
86 N: for<'a> FormatFields<'a> + 'static,
87{
88 fn format_event(
89 &self,
90 ctx: &FmtContext<'_, S, N>,
91 mut writer: Writer<'_>,
92 event: &Event<'_>,
93 ) -> fmt::Result {
94 let meta = event.metadata();
95 let mut fields = JsonFieldVisitor::default();
96 event.record(&mut fields);
97
98 let mut object = Map::new();
99 object.insert(
100 "timestamp".to_owned(),
101 Value::String(chrono::Utc::now().to_rfc3339()),
102 );
103 object.insert("level".to_owned(), Value::String(meta.level().to_string()));
104 object.insert("target".to_owned(), Value::String(meta.target().to_owned()));
105 object.insert("fields".to_owned(), Value::Object(fields.fields));
106
107 let spans: Vec<Value> = ctx
108 .event_scope()
109 .into_iter()
110 .flat_map(tracing_subscriber::registry::Scope::from_root)
111 .map(|span| {
112 let mut entry = Map::new();
113 entry.insert("name".to_owned(), Value::String(span.name().to_owned()));
114 if let Some(rendered) = span.extensions().get::<FormattedFields<N>>()
115 && !rendered.is_empty()
116 {
117 entry.insert("fields".to_owned(), Value::String(rendered.to_string()));
118 }
119 Value::Object(entry)
120 })
121 .collect();
122 if !spans.is_empty() {
123 object.insert("spans".to_owned(), Value::Array(spans));
124 }
125 if let Some(instance_id) = self.instance.get() {
126 object.insert(
127 "instance".to_owned(),
128 Value::String(instance_id.as_str().to_owned()),
129 );
130 }
131
132 let line = serde_json::to_string(&Value::Object(object)).map_err(|_encode| fmt::Error)?;
133 writeln!(writer, "{line}")
134 }
135}
136
137#[derive(Debug, Default)]
138struct JsonFieldVisitor {
139 fields: Map<String, Value>,
142}
143
144impl JsonFieldVisitor {
145 fn insert(&mut self, field: &Field, value: Value) {
147 let value = if is_redacted(field.name()) {
148 Value::String(REDACTION_PLACEHOLDER.to_owned())
149 } else {
150 value
151 };
152 self.fields.insert(field.name().to_owned(), value);
153 }
154}
155
156impl Visit for JsonFieldVisitor {
157 fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
158 let rendered = format!("{value:?}");
159 if is_system_sentinel(&rendered) {
160 return;
161 }
162 self.insert(field, Value::String(rendered));
163 }
164
165 fn record_str(&mut self, field: &Field, value: &str) {
166 if is_system_sentinel(value) {
167 return;
168 }
169 self.insert(field, Value::String(value.to_owned()));
170 }
171
172 fn record_i64(&mut self, field: &Field, value: i64) {
173 self.insert(field, Value::from(value));
174 }
175
176 fn record_u64(&mut self, field: &Field, value: u64) {
177 self.insert(field, Value::from(value));
178 }
179
180 fn record_f64(&mut self, field: &Field, value: f64) {
181 self.insert(field, Value::from(value));
182 }
183
184 fn record_bool(&mut self, field: &Field, value: bool) {
185 self.insert(field, Value::from(value));
186 }
187}