miden_node_utils/tracing/
grpc.rs1use http::header::HeaderName;
2use tracing::field;
3
4use crate::tracing::OpenTelemetrySpanExt;
5
6#[track_caller]
14pub fn grpc_trace_fn<T>(request: &http::Request<T>) -> tracing::Span {
15 let mut path_segments = request.uri().path().rsplit('/');
17
18 let method = path_segments.next().unwrap_or_default();
19 let service = path_segments.next().unwrap_or_default();
20
21 let span = match method {
24 "SyncState" | "SyncNullifiers" => tracing::debug_span!(
25 "rpc",
26 otel.name = field::Empty,
27 rpc.service = service,
28 rpc.method = method
29 ),
30 _ => tracing::info_span!(
31 "rpc",
32 otel.name = field::Empty,
33 rpc.service = service,
34 rpc.method = method
35 ),
36 };
37
38 let otel_name = format!("{service}/{method}");
40 span.record("otel.name", otel_name);
41
42 let otel_ctx = opentelemetry::global::get_text_map_propagator(|propagator| {
44 propagator.extract(&MetadataExtractor(&tonic::metadata::MetadataMap::from_headers(
45 request.headers().clone(),
46 )))
47 });
48 let _ = tracing_opentelemetry::OpenTelemetrySpanExt::set_parent(&span, otel_ctx);
49
50 span.set_attribute("rpc.system", "grpc");
56 if let Some(host) = request.uri().host() {
57 span.set_attribute("server.address", host);
58 }
59 if let Some(host_port) = request.uri().port() {
60 span.set_attribute("server.port", host_port.as_u16());
61 }
62 let remote_addr = request
63 .extensions()
64 .get::<tonic::transport::server::TcpConnectInfo>()
65 .and_then(tonic::transport::server::TcpConnectInfo::remote_addr);
66 if let Some(addr) = remote_addr {
67 span.set_attribute("client.address", addr.ip());
68 span.set_attribute("client.port", addr.port());
69 span.set_attribute("network.peer.address", addr.ip());
70 span.set_attribute("network.peer.port", addr.port());
71 span.set_attribute("network.transport", "tcp");
72 match addr.ip() {
73 std::net::IpAddr::V4(_) => span.set_attribute("network.type", "ipv4"),
74 std::net::IpAddr::V6(_) => span.set_attribute("network.type", "ipv6"),
75 }
76 }
77
78 for header in [
79 http::header::ACCEPT,
80 http::header::ORIGIN,
81 http::header::USER_AGENT,
82 http::header::FORWARDED,
83 HeaderName::from_static("x-forwarded-for"),
84 HeaderName::from_static("x-real-ip"),
85 ] {
86 if let Some(value) = request.headers().get(&header) {
87 if let Ok(value) = value.to_str() {
88 span.set_attribute(format!("http.request.header.{header}"), value);
89 }
90 }
91 }
92
93 span
94}
95
96#[derive(Copy, Clone)]
98pub struct OtelInterceptor;
99
100impl tonic::service::Interceptor for OtelInterceptor {
101 fn call(
102 &mut self,
103 mut request: tonic::Request<()>,
104 ) -> Result<tonic::Request<()>, tonic::Status> {
105 use tracing_opentelemetry::OpenTelemetrySpanExt;
106 let ctx = tracing::Span::current().context();
107 opentelemetry::global::get_text_map_propagator(|propagator| {
108 propagator.inject_context(&ctx, &mut MetadataInjector(request.metadata_mut()));
109 });
110
111 Ok(request)
112 }
113}
114
115struct MetadataExtractor<'a>(&'a tonic::metadata::MetadataMap);
116impl opentelemetry::propagation::Extractor for MetadataExtractor<'_> {
117 fn get(&self, key: &str) -> Option<&str> {
120 self.0.get(key).and_then(|metadata| metadata.to_str().ok())
121 }
122
123 fn keys(&self) -> Vec<&str> {
125 self.0
126 .keys()
127 .map(|key| match key {
128 tonic::metadata::KeyRef::Ascii(v) => v.as_str(),
129 tonic::metadata::KeyRef::Binary(v) => v.as_str(),
130 })
131 .collect::<Vec<_>>()
132 }
133}
134
135struct MetadataInjector<'a>(&'a mut tonic::metadata::MetadataMap);
136impl opentelemetry::propagation::Injector for MetadataInjector<'_> {
137 fn set(&mut self, key: &str, value: String) {
140 if let Ok(key) = tonic::metadata::MetadataKey::from_bytes(key.as_bytes())
141 && let Ok(val) = tonic::metadata::MetadataValue::try_from(&value)
142 {
143 self.0.insert(key, val);
144 }
145 }
146}