spin_trigger_http/
instrument.rs1use anyhow::Result;
2use http::Response;
3use opentelemetry_semantic_conventions::attribute as otel_attribute;
4use tracing::Level;
5
6use crate::Body;
7
8macro_rules! http_span {
10 ($request:tt, $addr:tt) => {
11 tracing::info_span!(
12 "spin_trigger_http.handle_http_request",
13 "otel.kind" = "server",
14 {opentelemetry_semantic_conventions::attribute::HTTP_REQUEST_METHOD} = %$request.method(),
15 {opentelemetry_semantic_conventions::attribute::NETWORK_PEER_ADDRESS} = %$addr.ip(),
16 {opentelemetry_semantic_conventions::attribute::NETWORK_PEER_PORT} = %$addr.port(),
17 {opentelemetry_semantic_conventions::attribute::NETWORK_PROTOCOL_NAME} = "http",
18 {opentelemetry_semantic_conventions::attribute::URL_PATH} = $request.uri().path(),
19 {opentelemetry_semantic_conventions::attribute::URL_QUERY} = $request.uri().query().unwrap_or(""),
20 {opentelemetry_semantic_conventions::attribute::URL_SCHEME} = $request.uri().scheme_str().unwrap_or(""),
21 {opentelemetry_semantic_conventions::attribute::CLIENT_ADDRESS} = $request.headers().get("x-forwarded-for").and_then(|val| val.to_str().ok()),
22 {opentelemetry_semantic_conventions::attribute::ERROR_TYPE} = ::tracing::field::Empty,
24 {opentelemetry_semantic_conventions::attribute::HTTP_RESPONSE_STATUS_CODE} = ::tracing::field::Empty,
25 {opentelemetry_semantic_conventions::attribute::HTTP_ROUTE} = ::tracing::field::Empty,
26 "otel.name" = ::tracing::field::Empty,
27 )
28 };
29}
30
31pub(crate) use http_span;
32
33pub(crate) fn finalize_http_span(
35 response: Result<Response<Body>>,
36 method: String,
37) -> Result<Response<Body>> {
38 let span = tracing::Span::current();
39 match response {
40 Ok(response) => {
41 tracing::info!(
42 "Request finished, sending response with status code {}",
43 response.status()
44 );
45
46 let matched_route = response.extensions().get::<MatchedRoute>();
47 if let Some(MatchedRoute { route }) = matched_route {
49 span.record(otel_attribute::HTTP_ROUTE, route);
50 span.record("otel.name", format!("{method} {route}"));
51 } else {
52 span.record("otel.name", method);
53 }
54
55 span.record(
57 otel_attribute::HTTP_RESPONSE_STATUS_CODE,
58 response.status().as_u16(),
59 );
60
61 Ok(response)
62 }
63 Err(err) => {
64 instrument_error(&err);
65 span.record(otel_attribute::HTTP_RESPONSE_STATUS_CODE, 500);
66 span.record("otel.name", method);
67 Err(err)
68 }
69 }
70}
71
72pub(crate) fn instrument_error(err: &anyhow::Error) {
74 let span = tracing::Span::current();
75 tracing::event!(target:module_path!(), Level::INFO, error = %err);
76 span.record(otel_attribute::ERROR_TYPE, format!("{err:?}"));
77}
78
79#[derive(Clone)]
82pub struct MatchedRoute {
83 pub route: String,
84}
85
86impl MatchedRoute {
87 pub fn set_response_extension(resp: &mut Response<Body>, route: impl Into<String>) {
88 resp.extensions_mut().insert(MatchedRoute {
89 route: route.into(),
90 });
91 }
92
93 pub fn with_response_extension(
94 mut resp: Response<Body>,
95 route: impl Into<String>,
96 ) -> Response<Body> {
97 Self::set_response_extension(&mut resp, route);
98 resp
99 }
100}