use tracing::field;
use crate::tracing::OpenTelemetrySpanExt;
#[track_caller]
pub fn grpc_trace_fn<T>(request: &http::Request<T>) -> tracing::Span {
let mut path_segments = request.uri().path().rsplit('/');
let method = path_segments.next().unwrap_or_default();
let service = path_segments.next().unwrap_or_default();
let span = match method {
"SyncState" | "SyncNullifiers" => tracing::debug_span!(
"rpc",
otel.name = field::Empty,
rpc.service = service,
rpc.method = method
),
_ => tracing::info_span!(
"rpc",
otel.name = field::Empty,
rpc.service = service,
rpc.method = method
),
};
let otel_name = format!("{service}/{method}");
span.record("otel.name", otel_name);
let otel_ctx = opentelemetry::global::get_text_map_propagator(|propagator| {
propagator.extract(&MetadataExtractor(&tonic::metadata::MetadataMap::from_headers(
request.headers().clone(),
)))
});
let _ = tracing_opentelemetry::OpenTelemetrySpanExt::set_parent(&span, otel_ctx);
span.set_attribute("rpc.system", "grpc");
if let Some(host) = request.uri().host() {
span.set_attribute("server.address", host);
}
if let Some(host_port) = request.uri().port() {
span.set_attribute("server.port", host_port.as_u16());
}
let remote_addr = request
.extensions()
.get::<tonic::transport::server::TcpConnectInfo>()
.and_then(tonic::transport::server::TcpConnectInfo::remote_addr);
if let Some(addr) = remote_addr {
span.set_attribute("client.address", addr.ip());
span.set_attribute("client.port", addr.port());
span.set_attribute("network.peer.address", addr.ip());
span.set_attribute("network.peer.port", addr.port());
span.set_attribute("network.transport", "tcp");
match addr.ip() {
std::net::IpAddr::V4(_) => span.set_attribute("network.type", "ipv4"),
std::net::IpAddr::V6(_) => span.set_attribute("network.type", "ipv6"),
}
}
span
}
#[derive(Copy, Clone)]
pub struct OtelInterceptor;
impl tonic::service::Interceptor for OtelInterceptor {
fn call(
&mut self,
mut request: tonic::Request<()>,
) -> Result<tonic::Request<()>, tonic::Status> {
use tracing_opentelemetry::OpenTelemetrySpanExt;
let ctx = tracing::Span::current().context();
opentelemetry::global::get_text_map_propagator(|propagator| {
propagator.inject_context(&ctx, &mut MetadataInjector(request.metadata_mut()));
});
Ok(request)
}
}
struct MetadataExtractor<'a>(&'a tonic::metadata::MetadataMap);
impl opentelemetry::propagation::Extractor for MetadataExtractor<'_> {
fn get(&self, key: &str) -> Option<&str> {
self.0.get(key).and_then(|metadata| metadata.to_str().ok())
}
fn keys(&self) -> Vec<&str> {
self.0
.keys()
.map(|key| match key {
tonic::metadata::KeyRef::Ascii(v) => v.as_str(),
tonic::metadata::KeyRef::Binary(v) => v.as_str(),
})
.collect::<Vec<_>>()
}
}
struct MetadataInjector<'a>(&'a mut tonic::metadata::MetadataMap);
impl opentelemetry::propagation::Injector for MetadataInjector<'_> {
fn set(&mut self, key: &str, value: String) {
if let Ok(key) = tonic::metadata::MetadataKey::from_bytes(key.as_bytes())
&& let Ok(val) = tonic::metadata::MetadataValue::try_from(&value)
{
self.0.insert(key, val);
}
}
}