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