1mod args;
48mod grpc;
49mod memory_telemetry;
50mod metrics_server;
51mod prometheus;
52mod shared_reader;
53mod telemetry;
54mod utils;
55
56use opentelemetry_sdk::propagation::TraceContextPropagator;
57
58pub use self::{
59 args::{LogFormat, TelemetryArgs},
60 grpc::{
61 ClientTelemetryLayer, GrpcMakeSpan, GrpcOnEos, GrpcOnFirstBodyChunk, GrpcOnRequest,
62 GrpcOnResponse, ServerTelemetryLayer, TracingExtractorInterceptor,
63 TracingInjectorInterceptor, new_client_telemetry_layer, new_server_telemetry_layer,
64 },
65 telemetry::{Telemetry, TelemetryDropBehavior},
66 utils::to_short_str,
67};
68
69pub mod external {
70 pub use clap;
71 pub use opentelemetry;
72 pub use tower;
73 pub use tower_http;
74 pub use tracing;
75 pub use tracing_opentelemetry;
76
77 #[cfg(feature = "tracy")]
78 pub use tracing_tracy;
79}
80
81pub fn current_trace_id() -> Option<opentelemetry::TraceId> {
94 use opentelemetry::trace::TraceContextExt as _;
95 use tracing_opentelemetry::OpenTelemetrySpanExt as _;
96
97 let cx = tracing::Span::current().context();
98 let span = cx.span();
99 let span_cx = span.span_context();
100
101 (span_cx.is_valid() && span_cx.is_sampled()).then(|| span_cx.trace_id())
102}
103
104pub fn current_trace_headers() -> Option<TraceHeaders> {
108 use opentelemetry::propagation::text_map_propagator::TextMapPropagator as _;
109 use opentelemetry::trace::TraceContextExt as _;
110 use tracing_opentelemetry::OpenTelemetrySpanExt as _;
111
112 let cx = tracing::Span::current().context();
113 let span = cx.span();
114 let span_cx = span.span_context();
115
116 if !span_cx.is_valid() || !span_cx.is_sampled() {
117 return None;
118 }
119
120 let propagator = TraceContextPropagator::new();
121 let mut carrier = TraceHeaders::empty();
122
123 propagator.inject_context(&cx, &mut carrier);
124
125 Some(carrier)
126}
127
128#[derive(Debug, Clone)]
129pub struct TraceHeaders {
130 pub traceparent: String,
131 pub tracestate: Option<String>,
132}
133
134impl TraceHeaders {
135 pub const TRACEPARENT_KEY: &'static str = "traceparent";
136 pub const TRACESTATE_KEY: &'static str = "tracestate";
137
138 fn empty() -> Self {
139 Self {
140 traceparent: String::new(),
141 tracestate: None,
142 }
143 }
144}
145
146impl opentelemetry::propagation::Injector for TraceHeaders {
147 fn set(&mut self, key: &str, value: String) {
148 match key {
149 Self::TRACEPARENT_KEY => self.traceparent = value,
150 Self::TRACESTATE_KEY => {
151 if !value.is_empty() {
152 self.tracestate = Some(value);
153 }
154 }
155 _ => {}
156 }
157 }
158}
159
160impl opentelemetry::propagation::Extractor for TraceHeaders {
161 fn get(&self, key: &str) -> Option<&str> {
162 match key {
163 Self::TRACEPARENT_KEY => Some(self.traceparent.as_str()),
164 Self::TRACESTATE_KEY => self.tracestate.as_deref(),
165 _ => None,
166 }
167 }
168
169 fn keys(&self) -> Vec<&str> {
170 vec![Self::TRACEPARENT_KEY, Self::TRACESTATE_KEY]
171 }
172}
173
174impl From<&TraceHeaders> for opentelemetry::Context {
175 fn from(value: &TraceHeaders) -> Self {
176 use opentelemetry::propagation::text_map_propagator::TextMapPropagator as _;
177 let propagator = TraceContextPropagator::new();
178 propagator.extract(value)
179 }
180}
181
182pub trait EnvFilterExt
187where
188 Self: Sized,
189{
190 fn add_directive_if_absent(
191 self,
192 base: &str,
193 target: &str,
194 default: &str,
195 ) -> anyhow::Result<Self>;
196}
197
198impl EnvFilterExt for tracing_subscriber::EnvFilter {
199 fn add_directive_if_absent(
200 self,
201 base: &str,
202 target: &str,
203 default: &str,
204 ) -> anyhow::Result<Self> {
205 if !base.contains(&format!("{target}=")) {
206 let filter = self.add_directive(format!("{target}={default}").parse()?);
207 Ok(filter)
208 } else {
209 Ok(self)
210 }
211 }
212}