1use crate::fields::{FieldConfig, FieldSpec};
2use crate::format::{
3 write_extension_fields, DefaultSpanFormat, FormatEvent, FormatSpan, SerializableSpanList,
4};
5use crate::span_recorder::DefaultSpanRecorder;
6use crate::{DisplayLevelFilter, LoggerName};
7use serde::ser::{Error, SerializeMap};
8use serde::{Serialize, Serializer};
9use std::collections::HashSet;
10use std::fmt::Write as _;
11use std::sync::Arc;
12use tracing_core::field::{Field, Visit};
13use tracing_core::{Event, Level, Metadata, Subscriber};
14use tracing_subscriber::layer::Context;
15use tracing_subscriber::registry::LookupSpan;
16
17pub struct LogstashFormat<FC = (), SF = DefaultSpanFormat> {
31 display_version: bool,
32 display_timestamp: bool,
33 display_logger_name: Option<LoggerName>,
34 display_thread_name: bool,
35 display_level: bool,
36 display_level_value: bool,
37 display_span_list: Option<DisplayLevelFilter>,
38 display_stack_trace: Option<(DisplayLevelFilter, DisplayLevelFilter)>,
39 span_format: SF,
40 span_fields: Arc<FieldConfig>,
41 constants: Vec<(&'static str, String)>,
42 field_contributor: FC,
43}
44
45const fn level_value(level: &Level) -> u64 {
47 match *level {
48 Level::ERROR => 3,
49 Level::WARN => 4,
50 Level::INFO => 5,
51 Level::TRACE => 6,
52 Level::DEBUG => 7,
53 }
54}
55
56impl<FC, SF> LogstashFormat<FC, SF> {
57 pub fn with_timestamp(self, display_timestamp: bool) -> Self {
58 Self {
59 display_timestamp,
60 ..self
61 }
62 }
63 pub fn with_version(self, display_version: bool) -> Self {
64 Self {
65 display_version,
66 ..self
67 }
68 }
69 pub fn with_logger_name(self, display_logger_name: Option<LoggerName>) -> Self {
70 Self {
71 display_logger_name,
72 ..self
73 }
74 }
75 pub fn with_thread_name(self, display_thread_name: bool) -> Self {
76 Self {
77 display_thread_name,
78 ..self
79 }
80 }
81 pub fn with_level(self, display_level: bool) -> Self {
82 Self {
83 display_level,
84 ..self
85 }
86 }
87 pub fn with_level_value(self, display_level_value: bool) -> Self {
88 Self {
89 display_level_value,
90 ..self
91 }
92 }
93 pub fn with_span_list(self, display_span_list: Option<DisplayLevelFilter>) -> Self {
94 Self {
95 display_span_list,
96 ..self
97 }
98 }
99 pub fn with_stack_trace(
100 self,
101 display_stack_trace: Option<(DisplayLevelFilter, DisplayLevelFilter)>,
102 ) -> Self {
103 Self {
104 display_stack_trace,
105 ..self
106 }
107 }
108
109 pub fn with_span_fields(self, span_fields: Vec<FieldSpec>) -> Self {
110 Self {
111 span_fields: Arc::new(FieldConfig::new(span_fields)),
112 ..self
113 }
114 }
115
116 pub fn with_field_contributor<FC2>(self, field_contributor: FC2) -> LogstashFormat<FC2, SF> {
142 LogstashFormat {
143 display_version: self.display_version,
144 display_timestamp: self.display_timestamp,
145 display_logger_name: self.display_logger_name,
146 display_thread_name: self.display_thread_name,
147 display_level: self.display_level,
148 display_stack_trace: self.display_stack_trace,
149 display_level_value: self.display_level_value,
150 display_span_list: self.display_span_list,
151 span_format: self.span_format,
152 span_fields: self.span_fields,
153 constants: self.constants,
154 field_contributor,
155 }
156 }
157
158 pub fn with_constants(self, constants: Vec<(&'static str, String)>) -> Self {
173 Self { constants, ..self }
174 }
175
176 pub fn span_format<FS2>(self, span_format: FS2) -> LogstashFormat<FC, FS2> {
177 LogstashFormat {
178 display_version: self.display_version,
179 display_timestamp: self.display_timestamp,
180 display_logger_name: self.display_logger_name,
181 display_thread_name: self.display_thread_name,
182 display_level: self.display_level,
183 display_stack_trace: self.display_stack_trace,
184 display_level_value: self.display_level_value,
185 display_span_list: self.display_span_list,
186 span_format,
187 span_fields: self.span_fields,
188 constants: self.constants,
189 field_contributor: self.field_contributor,
190 }
191 }
192}
193
194impl Default for LogstashFormat {
195 fn default() -> Self {
196 Self {
197 display_version: true,
198 display_timestamp: true,
199 display_logger_name: Some(LoggerName::Event),
200 display_thread_name: true,
201 display_level: true,
202 display_level_value: true,
203 display_stack_trace: None,
204 display_span_list: None,
205 span_format: Default::default(),
206 span_fields: Default::default(),
207 constants: Default::default(),
208 field_contributor: (),
209 }
210 }
211}
212
213fn format_stack_trace<SS>(
214 event: &Event<'_>,
215 ctx: &Context<'_, SS>,
216 event_filter: DisplayLevelFilter,
217 span_filter: DisplayLevelFilter,
218) -> Option<String>
219where
220 SS: Subscriber + for<'a> LookupSpan<'a>,
221{
222 fn append_line(stack_trace: &mut String, metadata: &Metadata<'_>) {
223 writeln!(
224 stack_trace,
225 " at {}({}:{})",
226 metadata.target(),
227 metadata.file().unwrap_or("<unknown>"),
228 metadata.line().unwrap_or(0)
229 )
230 .unwrap();
231 }
232
233 let event_metadata = event.metadata();
234 if !event_filter.is_enabled(event, event_metadata.level()) {
235 return None;
236 }
237
238 let mut stack_trace = String::new();
239 if let Some(scope) = ctx.event_scope(event) {
240 for span in scope.from_root() {
241 let span_metadata = span.metadata();
242 if span_filter.is_enabled(event, span_metadata.level()) {
243 append_line(&mut stack_trace, span_metadata);
244 }
245 }
246 }
247
248 append_line(&mut stack_trace, event_metadata);
249 if !stack_trace.is_empty() {
250 stack_trace.truncate(stack_trace.len() - 1);
251 }
252
253 Some(stack_trace)
254}
255
256struct SerializeSpanName<'c, SS>(&'c Event<'c>, &'c Context<'c, SS>);
257
258impl<'c, SS> Serialize for SerializeSpanName<'c, SS>
259where
260 SS: Subscriber + for<'a> LookupSpan<'a>,
261{
262 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
263 where
264 S: Serializer,
265 {
266 if let Some(span_metadata) = self.1.current_span().metadata() {
267 let name = format!("{}::{}", span_metadata.target(), span_metadata.name());
268 serializer.serialize_str(&name)
269 } else {
270 serializer.serialize_str(self.0.metadata().target())
271 }
272 }
273}
274
275pub trait LogFieldContributor {
276 fn add_fields<F>(&self, serializer: &mut F)
277 where
278 F: LogFieldReceiver;
279}
280
281impl LogFieldContributor for () {
282 #[inline(always)]
283 fn add_fields<F>(&self, _serializer: &mut F)
284 where
285 F: LogFieldReceiver,
286 {
287 }
288}
289
290impl<DFN, FS> FormatEvent for LogstashFormat<DFN, FS>
291where
292 FS: FormatSpan,
293 DFN: LogFieldContributor,
294{
295 type R = DefaultSpanRecorder;
296
297 fn span_recorder(&self) -> Self::R {
298 DefaultSpanRecorder::from_config(self.span_fields.clone())
299 }
300
301 fn format_event<S: Serializer, SS: Subscriber + for<'a> LookupSpan<'a>>(
302 &self,
303 serializer: S,
304 event: &Event<'_>,
305 ctx: Context<'_, SS>,
306 ) -> Result<S::Ok, S::Error> {
307 let event_metadata = event.metadata();
308 let event_level = event_metadata.level();
309
310 let mut s = serializer.serialize_map(None)?;
311
312 let mut seen = HashSet::new();
313
314 let mut field_visitor = SerializingFieldVisitor {
315 serializer: &mut s,
316 field_name_filter: |name| seen.insert(name),
317 status: None,
318 };
319
320 if self.display_version {
321 field_visitor.add_field("@version", "1");
322 }
323
324 if self.display_timestamp {
325 field_visitor.add_field("@timestamp", &LogTimestamp::default());
326 }
327
328 if self.display_thread_name {
329 let thread = std::thread::current();
330 if let Some(name) = thread.name() {
331 field_visitor.add_field("thread_name", name);
332 }
333 }
334
335 if let Some(l) = self.display_logger_name {
336 match l {
337 LoggerName::Event => {
338 field_visitor.add_field("logger_name", event_metadata.target())
339 }
340 LoggerName::Span => {
341 field_visitor.add_field("logger_name", &SerializeSpanName(event, &ctx))
342 }
343 };
344 }
345
346 if self.display_level {
347 field_visitor.add_field("level", event_level.as_str());
348 }
349
350 if self.display_level_value {
351 field_visitor.add_field("level_value", &level_value(event_level));
352 }
353
354 if let Some((event_filter, span_filter)) = self.display_stack_trace {
355 if let Some(stack_trace) = format_stack_trace(event, &ctx, event_filter, span_filter) {
356 field_visitor.add_field("stack_trace", &stack_trace);
357 }
358 }
359
360 for (key, value) in &self.constants {
361 field_visitor.add_field(key, value);
362 }
363
364 self.field_contributor.add_fields(&mut field_visitor);
365
366 if let Some(filter) = self.display_span_list {
367 field_visitor.add_field(
368 "spans",
369 &SerializableSpanList(&self.span_format, event, &ctx, filter),
370 );
371 }
372
373 event.record(&mut field_visitor);
374 if let Some(e) = field_visitor.status {
375 return Err(e);
376 }
377
378 if let Some(scope) = ctx.event_scope(event) {
379 for span in scope {
380 if let Some(span_fields) = span.extensions().get::<DefaultSpanRecorder>() {
381 write_extension_fields(&mut seen, &mut s, span_fields)?;
382 }
383 }
384 }
385 s.end()
386 }
387}
388
389pub trait LogFieldReceiver {
390 fn add_field<V: ?Sized + Serialize>(&mut self, field: &'static str, value: &V);
391}
392
393pub struct SerializingFieldVisitor<'a, F, S, E> {
394 field_name_filter: F,
395 serializer: &'a mut S,
396 status: Option<E>,
397}
398
399impl<'a, S: SerializeMap, F: FnMut(&'static str) -> bool>
400 SerializingFieldVisitor<'a, F, S, S::Error>
401{
402 #[inline]
403 fn record_field<V: ?Sized + Serialize>(&mut self, field: &Field, value: &V) {
404 self.add_field(field.name(), value)
405 }
406}
407
408impl<'a, S: SerializeMap, F: FnMut(&'static str) -> bool> LogFieldReceiver
409 for SerializingFieldVisitor<'a, F, S, S::Error>
410{
411 fn add_field<V: ?Sized + Serialize>(&mut self, field: &'static str, value: &V) {
412 if self.status.is_none() && (self.field_name_filter)(field) {
413 if let Err(e) = self.serializer.serialize_entry(field, &value) {
414 self.status = Some(e)
415 }
416 }
417 }
418}
419
420impl<'a, F: FnMut(&'static str) -> bool, S: SerializeMap> Visit
421 for SerializingFieldVisitor<'a, F, S, S::Error>
422{
423 fn record_f64(&mut self, field: &Field, value: f64) {
424 self.record_field(field, &value);
425 }
426
427 fn record_i64(&mut self, field: &Field, value: i64) {
428 self.record_field(field, &value);
429 }
430
431 fn record_u64(&mut self, field: &Field, value: u64) {
432 self.record_field(field, &value);
433 }
434
435 fn record_bool(&mut self, field: &Field, value: bool) {
436 self.record_field(field, &value);
437 }
438
439 fn record_str(&mut self, field: &Field, value: &str) {
440 self.record_field(field, value);
441 }
442
443 fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
444 self.record_field(field, &format!("{}", value));
445 }
446
447 fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
448 self.record_field(field, &format!("{:?}", value));
449 }
450}
451
452struct LogTimestamp(time::OffsetDateTime);
453
454impl Default for LogTimestamp {
455 fn default() -> Self {
456 Self(time::OffsetDateTime::now_utc())
457 }
458}
459
460impl Serialize for LogTimestamp {
461 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
462 where
463 S: Serializer,
464 {
465 match self
466 .0
467 .format(&time::format_description::well_known::Rfc3339)
468 {
469 Ok(s) => serializer.serialize_str(&s),
470 Err(e) => Err(S::Error::custom(e)),
471 }
472 }
473}
474
475#[cfg(test)]
476mod test {
477 use time::macros::datetime;
478
479 #[test]
480 fn test_serialize_log_timestamp() {
481 let timestamp = super::LogTimestamp(datetime!(2020-01-01 00:00:00 +00:00));
482 let serialized = serde_json::to_string(×tamp).unwrap();
483 assert_eq!(serialized, "\"2020-01-01T00:00:00Z\"");
484 }
485}