miden_node_utils/tracing/
grpc.rs1use http::header::HeaderName;
2use tower_governor::key_extractor::{KeyExtractor, SmartIpKeyExtractor};
3use tracing::field;
4
5#[track_caller]
13pub fn grpc_trace_fn<T>(request: &http::Request<T>) -> tracing::Span {
14 let mut path_segments = request.uri().path().rsplit('/');
16
17 let method = path_segments.next().unwrap_or_default();
18 let service = path_segments.next().unwrap_or_default();
19
20 let span = tracing::info_span!(
23 "rpc",
24 otel.name = field::Empty,
25 rpc.service = service,
26 rpc.method = method,
27 rpc.system = field::Empty,
28 server.address = field::Empty,
29 server.port = field::Empty,
30 client.address = field::Empty,
31 client.port = field::Empty,
32 network.peer.address = field::Empty,
33 network.peer.port = field::Empty,
34 network.transport = field::Empty,
35 network.type = field::Empty,
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.record("rpc.system", "grpc");
56 if let Some(host) = request.uri().host() {
57 span.record("server.address", host);
58 }
59 if let Some(host_port) = request.uri().port() {
60 span.record("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
67 if let Ok(ip) = SmartIpKeyExtractor.extract(request) {
70 span.record("client.address", field::display(ip));
71 } else if let Some(addr) = remote_addr {
72 span.record("client.address", field::display(addr.ip()));
73 span.record("client.port", addr.port());
74 }
75
76 if let Some(addr) = remote_addr {
77 span.record("network.peer.address", field::display(addr.ip()));
78 span.record("network.peer.port", addr.port());
79 span.record("network.transport", "tcp");
80 match addr.ip() {
81 std::net::IpAddr::V4(_) => span.record("network.type", "ipv4"),
82 std::net::IpAddr::V6(_) => span.record("network.type", "ipv6"),
83 };
84 }
85
86 for header in [
87 http::header::ACCEPT,
88 http::header::ORIGIN,
89 http::header::USER_AGENT,
90 http::header::FORWARDED,
91 HeaderName::from_static("x-forwarded-for"),
92 HeaderName::from_static("x-real-ip"),
93 HeaderName::from_static("x-request-id"),
94 ] {
95 if let Some(value) = request.headers().get(&header) {
96 if let Ok(value) = value.to_str() {
97 tracing_opentelemetry::OpenTelemetrySpanExt::set_attribute(
98 &span,
99 format!("http.request.header.{header}"),
100 value.to_owned(),
101 );
102 }
103 }
104 }
105
106 span
107}
108
109#[derive(Copy, Clone)]
111pub struct OtelInterceptor;
112
113impl tonic::service::Interceptor for OtelInterceptor {
114 fn call(
115 &mut self,
116 mut request: tonic::Request<()>,
117 ) -> Result<tonic::Request<()>, tonic::Status> {
118 use tracing_opentelemetry::OpenTelemetrySpanExt;
119 let ctx = tracing::Span::current().context();
120 opentelemetry::global::get_text_map_propagator(|propagator| {
121 propagator.inject_context(&ctx, &mut MetadataInjector(request.metadata_mut()));
122 });
123
124 Ok(request)
125 }
126}
127
128struct MetadataExtractor<'a>(&'a tonic::metadata::MetadataMap);
129impl opentelemetry::propagation::Extractor for MetadataExtractor<'_> {
130 fn get(&self, key: &str) -> Option<&str> {
133 self.0.get(key).and_then(|metadata| metadata.to_str().ok())
134 }
135
136 fn keys(&self) -> Vec<&str> {
138 self.0
139 .keys()
140 .map(|key| match key {
141 tonic::metadata::KeyRef::Ascii(v) => v.as_str(),
142 tonic::metadata::KeyRef::Binary(v) => v.as_str(),
143 })
144 .collect::<Vec<_>>()
145 }
146}
147
148struct MetadataInjector<'a>(&'a mut tonic::metadata::MetadataMap);
149impl opentelemetry::propagation::Injector for MetadataInjector<'_> {
150 fn set(&mut self, key: &str, value: String) {
153 if let Ok(key) = tonic::metadata::MetadataKey::from_bytes(key.as_bytes())
154 && let Ok(val) = tonic::metadata::MetadataValue::try_from(&value)
155 {
156 self.0.insert(key, val);
157 }
158 }
159}