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}