Skip to main content

sh_layer1/observability/
span.rs

1//! Span utilities for tracing.
2
3use tracing::Span;
4
5/// Guard for a tracing span.
6///
7/// When dropped, the span is automatically closed.
8pub struct SpanGuard {
9    span: Option<Span>,
10}
11
12impl SpanGuard {
13    /// Create a new span guard from a tracing span.
14    pub fn new(span: Span) -> Self {
15        Self { span: Some(span) }
16    }
17
18    /// Create a no-op span guard (for disabled observability).
19    pub fn noop() -> Self {
20        Self { span: None }
21    }
22
23    /// Set an attribute on the span.
24    pub fn set_attribute(&self, key: &str, value: &str) {
25        if let Some(span) = &self.span {
26            span.record(key, value);
27        }
28    }
29
30    /// Add an event to the span.
31    pub fn add_event(&self, name: &str, attributes: &[(&str, &str)]) {
32        if let Some(span) = &self.span {
33            let mut fields = Vec::new();
34            for (k, v) in attributes {
35                fields.push(format!("{}={}", k, v));
36            }
37            span.record("event", format!("{}: {}", name, fields.join(", ")));
38        }
39    }
40
41    /// Get a reference to the underlying span.
42    pub fn as_ref(&self) -> Option<&Span> {
43        self.span.as_ref()
44    }
45}
46
47impl Default for SpanGuard {
48    fn default() -> Self {
49        Self::noop()
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn test_span_guard_noop() {
59        let guard = SpanGuard::noop();
60        guard.set_attribute("key", "value");
61        guard.add_event("test", &[("a", "b")]);
62        // Should not panic
63    }
64
65    #[test]
66    fn test_span_guard_default() {
67        let guard = SpanGuard::default();
68        assert!(guard.as_ref().is_none());
69    }
70
71    #[test]
72    fn test_span_guard_with_span() {
73        let span = tracing::info_span!("test_span");
74        let guard = SpanGuard::new(span);
75
76        guard.set_attribute("key", "value");
77        guard.add_event("event", &[("status", "ok")]);
78
79        assert!(guard.as_ref().is_some());
80    }
81}