use qcs_api_client_common::tracing_configuration::TracingFilter;
use tower_http::classify::GrpcFailureClass;
use tracing::Span;
use tracing_opentelemetry::OpenTelemetrySpanExt;
use urlpattern::UrlPatternMatchInput;
pub(super) fn make_grpc_request_span<B>(request: &http::Request<B>) -> tracing::Span {
let url = request.uri();
let path = url.path();
let mut path_split = path.split('/');
let (_, service, method) = (path_split.next(), path_split.next(), path_split.next());
let service = service.unwrap_or("");
let method = method.unwrap_or("");
let host = url.host().unwrap_or("");
let host_port = url.port().map_or(0u16, |p| p.as_u16());
tracing::span!(
tracing::Level::INFO,
"gRPC request",
rpc.system = "grpc",
rpc.service = %service,
rpc.method = %method,
net.peer.name = %host,
net.peer.port = %host_port,
"message.type" = "sent",
otel.kind = "client",
otel.name = %path,
rpc.grpc.status_code = tracing::field::Empty,
)
}
pub(super) fn should_trace_request<B>(
base_url: &str,
request: &http::Request<B>,
filter: Option<&TracingFilter>,
) -> bool {
let full_request_url = format!("{base_url}{}", request.uri());
let parsed = full_request_url.parse::<::url::Url>();
let url = parsed.ok();
filter
.and_then(|filter| url.map(|url| (filter, url)))
.is_none_or(|(filter, url)| filter.is_enabled(&UrlPatternMatchInput::Url(url)))
}
#[derive(Clone, Debug, Default)]
pub struct OnFailure {
pub(super) inner: tower_http::trace::DefaultOnFailure,
}
impl tower_http::trace::OnFailure<GrpcFailureClass> for OnFailure {
fn on_failure(
&mut self,
failure_classification: GrpcFailureClass,
latency: std::time::Duration,
span: &Span,
) {
match failure_classification {
GrpcFailureClass::Code(code) => {
span.set_attribute("rpc.grpc.status_code", format!("{code}"));
}
GrpcFailureClass::Error(_) => {
span.set_attribute(
"rpc.grpc.status_code",
format!("{}", tonic::Code::Unknown as u8),
);
}
}
self.inner.on_failure(failure_classification, latency, span);
}
}