miden_node_utils/tracing/
span_ext.rs

1use core::time::Duration;
2use std::net::IpAddr;
3
4use miden_objects::{Digest, batch::BatchId, block::BlockNumber};
5use opentelemetry::{Key, Value, trace::Status};
6
7use crate::ErrorReport;
8
9/// Utility functions for converting types into [`opentelemetry::Value`].
10pub trait ToValue {
11    fn to_value(&self) -> Value;
12}
13
14impl ToValue for Duration {
15    fn to_value(&self) -> Value {
16        self.as_secs_f64().into()
17    }
18}
19
20impl ToValue for Digest {
21    fn to_value(&self) -> Value {
22        self.to_hex().into()
23    }
24}
25
26impl ToValue for BlockNumber {
27    fn to_value(&self) -> Value {
28        i64::from(self.as_u32()).into()
29    }
30}
31
32impl ToValue for BatchId {
33    fn to_value(&self) -> Value {
34        self.to_hex().into()
35    }
36}
37
38impl ToValue for usize {
39    fn to_value(&self) -> Value {
40        i64::try_from(*self).unwrap_or(i64::MAX).into()
41    }
42}
43
44/// Generates `impl ToValue` blocks for types that are `ToString`.
45macro_rules! impl_to_string_to_value {
46    ($($t:ty),*) => {
47        $(
48            impl ToValue for $t {
49                fn to_value(&self) -> Value {
50                    self.to_string().into()
51                }
52            }
53        )*
54    };
55}
56impl_to_string_to_value!(IpAddr, &str);
57
58/// Generates `impl ToValue` blocks for integer types.
59macro_rules! impl_int_to_value {
60    ($($t:ty),*) => {
61        $(
62            impl ToValue for $t {
63                fn to_value(&self) -> Value {
64                    i64::from(*self).into()
65                }
66            }
67        )*
68    };
69}
70impl_int_to_value!(u16, u32);
71
72/// Generates `impl ToValue` blocks for types that are `Into<Value>`.
73macro_rules! impl_to_value {
74    ($($t:ty),*) => {
75        $(
76            impl ToValue for $t {
77                fn to_value(&self) -> Value {
78                    (*self).into()
79                }
80            }
81        )*
82    };
83}
84impl_to_value!(f64, i64);
85
86/// Utility functions based on [`tracing_opentelemetry::OpenTelemetrySpanExt`].
87///
88/// This is a sealed trait. It and cannot be implemented outside of this module.
89pub trait OpenTelemetrySpanExt: private::Sealed {
90    fn set_attribute(&self, key: impl Into<Key>, value: impl ToValue);
91    fn set_error(&self, err: &dyn std::error::Error);
92}
93
94impl<S> OpenTelemetrySpanExt for S
95where
96    S: tracing_opentelemetry::OpenTelemetrySpanExt,
97{
98    /// Sets an attribute on `Span`.
99    ///
100    /// Implementations for `ToValue` should be added to this crate (miden-node-utils).
101    fn set_attribute(&self, key: impl Into<Key>, value: impl ToValue) {
102        tracing_opentelemetry::OpenTelemetrySpanExt::set_attribute(self, key, value.to_value());
103    }
104
105    /// Sets a status on `Span` based on an error.
106    fn set_error(&self, err: &dyn std::error::Error) {
107        use std::fmt::Write as _;
108        // Include the main error and then append causation report.
109        let mut report = err.to_string();
110
111        std::iter::successors(err.source(), |child| child.source()).for_each(|source| {
112            let _ = write!(&mut report, "\nCaused by: {source}");
113        });
114
115        tracing_opentelemetry::OpenTelemetrySpanExt::set_status(
116            self,
117            Status::Error { description: err.as_report().into() },
118        );
119    }
120}
121
122mod private {
123    pub trait Sealed {}
124    impl<S> Sealed for S where S: tracing_opentelemetry::OpenTelemetrySpanExt {}
125}