rama_http/layer/trace/
make_span.rs1use crate::Request;
2use crate::header::USER_AGENT;
3use crate::opentelemetry::version_as_protocol_version;
4use rama_core::telemetry::tracing::{self, Level, Span};
5use rama_net::Protocol;
6use rama_utils::str::arcstr::{ArcStr, arcstr};
7
8use super::DEFAULT_MESSAGE_LEVEL;
9
10pub trait MakeSpan<B>: Send + Sync + 'static {
16 fn make_span(&self, request: &Request<B>) -> Span;
18}
19
20impl<B> MakeSpan<B> for Span {
21 fn make_span(&self, _request: &Request<B>) -> Span {
22 self.clone()
23 }
24}
25
26impl<F, B> MakeSpan<B> for F
27where
28 F: Fn(&Request<B>) -> Span + Send + Sync + 'static,
29{
30 fn make_span(&self, request: &Request<B>) -> Span {
31 self(request)
32 }
33}
34
35#[derive(Debug, Clone)]
40pub struct DefaultMakeSpan {
41 level: Level,
42 include_headers: bool,
43 otel_name: ArcStr,
44}
45
46impl DefaultMakeSpan {
47 #[must_use]
49 pub const fn new() -> Self {
50 Self {
51 level: DEFAULT_MESSAGE_LEVEL,
52 include_headers: false,
53 otel_name: arcstr!("request"),
54 }
55 }
56
57 rama_utils::macros::generate_set_and_with! {
58 pub fn level(mut self, level: Level) -> Self {
64 self.level = level;
65 self
66 }
67 }
68
69 rama_utils::macros::generate_set_and_with! {
70 pub fn include_headers(mut self, include_headers: bool) -> Self {
75 self.include_headers = include_headers;
76 self
77 }
78 }
79
80 rama_utils::macros::generate_set_and_with! {
81 pub fn name(mut self, otel_name: ArcStr) -> Self {
83 self.otel_name = otel_name;
84 self
85 }
86 }
87}
88
89impl Default for DefaultMakeSpan {
90 fn default() -> Self {
91 Self::new()
92 }
93}
94
95impl<B> MakeSpan<B> for DefaultMakeSpan {
96 fn make_span(&self, request: &Request<B>) -> Span {
97 let full_uri = request.request_uri();
98
99 let protocol = full_uri.scheme().unwrap_or(&Protocol::HTTP);
101
102 let (found_domain, found_port, found_scheme) = if let Some(host) = full_uri.host() {
104 (
105 Some(host),
106 full_uri.port_u16().or_else(|| protocol.default_port()),
107 Some(protocol.as_str()),
108 )
109 } else {
110 tracing::debug!("no authority could be resolved for request");
111 (None, None, None)
112 };
113
114 let found_domain_cow_str = found_domain.as_ref().map(|d| d.to_str());
115 let found_domain_str = found_domain_cow_str.as_deref();
116
117 let url_path = request.uri().path_or_root();
118 let url_query = request.uri().query_or_empty();
119
120 macro_rules! make_span {
124 ($level:expr) => {
125 if self.include_headers {
126 tracing::span!(
127 $level,
128 "request",
129 otel.name = self.otel_name.as_str(),
130 http.request.method = %request.method(),
131 url.full = %request.request_uri(),
132 url.domain = found_domain_str,
133 url.port = found_port,
134 url.path = %url_path.as_ref(),
135 url.query = %url_query.as_ref(),
136 url.scheme = found_scheme,
137 network.protocol.name = "http",
138 network.protocol.version = version_as_protocol_version(request.version()),
139 user_agent.original = %request.headers().get(USER_AGENT).and_then(|v| v.to_str().ok()).unwrap_or_default(),
140 headers = ?request.headers(),
141 )
142 } else {
143 tracing::span!(
144 $level,
145 "request",
146 otel.name = self.otel_name.as_str(),
147 http.request.method = %request.method(),
148 url.full = %request.request_uri(),
149 url.domain = found_domain_str,
150 url.port = found_port,
151 url.path = %url_path.as_ref(),
152 url.query = %url_query.as_ref(),
153 url.scheme = found_scheme,
154 network.protocol.name = "http",
155 network.protocol.version = version_as_protocol_version(request.version()),
156 user_agent.original = %request.headers().get(USER_AGENT).and_then(|v| v.to_str().ok()).unwrap_or_default(),
157 )
158 }
159 }
160 }
161
162 match self.level {
163 Level::ERROR => make_span!(Level::ERROR),
164 Level::WARN => make_span!(Level::WARN),
165 Level::INFO => make_span!(Level::INFO),
166 Level::DEBUG => make_span!(Level::DEBUG),
167 Level::TRACE => make_span!(Level::TRACE),
168 }
169 }
170}