logforth_diagnostic_fastrace/
lib.rs

1// Copyright 2024 FastLabs Developers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! A diagnostic that enriches log records with trace context provided by the Fastrace library.
16
17#![cfg_attr(docsrs, feature(doc_cfg))]
18
19use fastrace::collector::SpanContext;
20use logforth_core::Diagnostic;
21use logforth_core::Error;
22use logforth_core::kv::Key;
23use logforth_core::kv::Value;
24use logforth_core::kv::Visitor;
25
26/// A diagnostic that enriches log records with trace context provided by the Fastrace library.
27///
28/// Output format:
29///
30/// ```text
31/// 2025-01-10T15:22:37.868815+08:00 ERROR fastrace: fastrace.rs:39 Hello syslog error! trace_id=37f9c45f918cbb477089afb0d7162e7e
32/// 2025-01-10T15:22:37.868890+08:00  WARN fastrace: fastrace.rs:40 Hello syslog warn! trace_id=37f9c45f918cbb477089afb0d7162e7e
33/// 2025-01-10T15:22:37.868921+08:00  INFO fastrace: fastrace.rs:41 Hello syslog info! trace_id=37f9c45f918cbb477089afb0d7162e7e
34/// 2025-01-10T15:22:37.868949+08:00 DEBUG fastrace: fastrace.rs:42 Hello syslog debug! trace_id=37f9c45f918cbb477089afb0d7162e7e
35/// 2025-01-10T15:22:37.868976+08:00 TRACE fastrace: fastrace.rs:43 Hello syslog trace! trace_id=37f9c45f918cbb477089afb0d7162e7e
36/// ```
37///
38/// ## Example
39///
40/// ```
41/// use logforth_diagnostic_fastrace::FastraceDiagnostic;
42///
43/// let diagnostic = FastraceDiagnostic::default();
44/// ```
45#[derive(Default, Debug, Clone, Copy)]
46#[non_exhaustive]
47pub struct FastraceDiagnostic {}
48
49impl Diagnostic for FastraceDiagnostic {
50    fn visit(&self, visitor: &mut dyn Visitor) -> Result<(), Error> {
51        if let Some(span) = SpanContext::current_local_parent() {
52            // NOTE: TraceId and SpanId should be represented as hex strings.
53            let trace_id = span.trace_id.to_string();
54            let span_id = span.span_id.to_string();
55
56            visitor.visit(Key::new("trace_id"), Value::from_str(&trace_id))?;
57            visitor.visit(Key::new("span_id"), Value::from_str(&span_id))?;
58            visitor.visit(Key::new("sampled"), Value::from_bool(span.sampled))?;
59        }
60
61        Ok(())
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use std::collections::BTreeMap;
68
69    use fastrace::Span;
70    use logforth_core::kv::ValueOwned;
71
72    use super::*;
73
74    #[test]
75    fn key_values() {
76        struct Collector(BTreeMap<String, ValueOwned>);
77
78        impl Visitor for Collector {
79            fn visit(&mut self, key: Key<'_>, value: Value<'_>) -> Result<(), Error> {
80                self.0.insert(key.to_string(), value.to_owned());
81                Ok(())
82            }
83        }
84
85        let diagnostic = FastraceDiagnostic::default();
86
87        let mut map = {
88            let span = Span::root("test", SpanContext::random());
89            let _guard = span.set_local_parent();
90
91            let mut collector = Collector(BTreeMap::new());
92            diagnostic.visit(&mut collector).unwrap();
93            collector.0
94        };
95
96        let trace_id = map.remove("trace_id").unwrap();
97        assert_eq!(32, trace_id.to_string().len());
98        let span_id = map.remove("span_id").unwrap();
99        assert_eq!(16, span_id.to_string().len());
100        let sampled = map.remove("sampled").unwrap();
101        assert!(sampled.by_ref().to_bool().unwrap());
102
103        assert!(map.is_empty());
104    }
105}