tracing_logstash/
logstash.rs

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
17/// Display options for the logstash output format
18///
19/// # Example
20/// ```
21/// # use tracing_subscriber::prelude::*;
22/// #
23/// let logger = tracing_logstash::Layer::default().event_format(
24///    tracing_logstash::logstash::LogstashFormat::default()
25///         .with_timestamp(false),
26/// );
27/// #
28/// # let collector = tracing_subscriber::Registry::default().with(logger);
29/// ```
30pub 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
45/// Converts a `Level` to a numeric value.
46const 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    /// Add dynamically generated fields to every event
117    ///
118    /// # Example
119    /// ```
120    /// # use tracing_subscriber::prelude::*;
121    /// # use tracing_logstash::logstash::{LogFieldReceiver, LogFieldContributor};
122    /// #
123    /// struct DynamicFields;
124    /// impl LogFieldContributor for DynamicFields {
125    ///     fn add_fields<F>(&self, serializer: &mut F)
126    ///     where
127    ///         F: LogFieldReceiver,
128    ///     {
129    ///         serializer.add_field("string_field", "fnord");
130    ///         serializer.add_field("number_field", &42);
131    ///    }
132    /// }
133    ///
134    /// let logger = tracing_logstash::Layer::default().event_format(
135    ///     tracing_logstash::logstash::LogstashFormat::default()
136    ///         .with_field_contributor(DynamicFields),
137    /// );
138    /// #
139    /// # let collector = tracing_subscriber::Registry::default().with(logger);
140    /// ```
141    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    /// Add a constant field to every event.
159    ///
160    /// # Example
161    /// ```
162    /// # use tracing_subscriber::prelude::*;
163    /// #
164    /// let logger = tracing_logstash::Layer::default().event_format(
165    ///     tracing_logstash::logstash::LogstashFormat::default().with_constants(vec![
166    ///         ("service.name", "tracing-logstash".to_owned()),
167    ///     ]),
168    /// );
169    /// #
170    /// # let collector = tracing_subscriber::Registry::default().with(logger);
171    /// ```
172    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(&timestamp).unwrap();
483        assert_eq!(serialized, "\"2020-01-01T00:00:00Z\"");
484    }
485}