Match stable Semantic Conventions for HTTP Spans

The stable [Semantic Conventions for HTTP
Spans](https://opentelemetry.io/docs/specs/semconv/http/http-spans/) use
different keys than the ones used in this crate. This changes them to
match the keys as defined by the stable specification.
This commit is contained in:
Christopher Serr 2024-02-02 00:14:49 +01:00 committed by Ethan Brierley
parent 94a38211f7
commit 3422e7338c
4 changed files with 62 additions and 38 deletions

View file

@ -9,11 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Breaking changes ### Breaking changes
- Upgraded `reqwest-middleware` to `0.3.0`. - Upgraded `reqwest-middleware` to `0.3.0`.
- Removed support for `opentelemetry` 0.13 to 0.19 - Removed support for `opentelemetry` 0.13 to 0.19
- The keys emitted by the crate now match the stable Semantic Conventions for HTTP Spans.
### Changed ### Changed
- The keys emitted by the crate now match the stable Semantic Conventions for HTTP Spans. - The keys emitted by the crate now match the stable Semantic Conventions for HTTP Spans.
- Opentelemetry features are now additive. - Opentelemetry features are now additive.
### Deprecated
- The old keys are now deprecated.
## [0.4.8] - 2024-03-11 ## [0.4.8] - 2024-03-11
### Added ### Added

View file

@ -91,12 +91,14 @@ mod middleware;
mod otel; mod otel;
mod reqwest_otel_span_builder; mod reqwest_otel_span_builder;
pub use middleware::TracingMiddleware; pub use middleware::TracingMiddleware;
#[allow(deprecated)]
pub use reqwest_otel_span_builder::{ pub use reqwest_otel_span_builder::{
default_on_request_end, default_on_request_failure, default_on_request_success, default_on_request_end, default_on_request_failure, default_on_request_success,
default_span_name, DefaultSpanBackend, DisableOtelPropagation, OtelName, OtelPathNames, default_span_name, DefaultSpanBackend, DisableOtelPropagation, OtelName, OtelPathNames,
ReqwestOtelSpanBackend, SpanBackendWithUrl, ERROR_CAUSE_CHAIN, ERROR_MESSAGE, HTTP_HOST, ReqwestOtelSpanBackend, SpanBackendWithUrl, ERROR_CAUSE_CHAIN, ERROR_MESSAGE, HTTP_HOST,
HTTP_METHOD, HTTP_SCHEME, HTTP_STATUS_CODE, HTTP_URL, HTTP_USER_AGENT, NET_HOST_PORT, HTTP_METHOD, HTTP_REQUEST_METHOD, HTTP_RESPONSE_STATUS_CODE, HTTP_SCHEME, HTTP_STATUS_CODE,
OTEL_KIND, OTEL_NAME, OTEL_STATUS_CODE, HTTP_URL, HTTP_USER_AGENT, NET_HOST_PORT, OTEL_KIND, OTEL_NAME, OTEL_STATUS_CODE,
SERVER_ADDRESS, SERVER_PORT, URL_FULL, URL_SCHEME, USER_AGENT_ORIGINAL,
}; };
#[doc(hidden)] #[doc(hidden)]

View file

@ -2,36 +2,57 @@ use std::borrow::Cow;
use http::Extensions; use http::Extensions;
use matchit::Router; use matchit::Router;
use reqwest::header::{HeaderMap, HeaderValue}; use reqwest::{header::HeaderValue, Request, Response, StatusCode as RequestStatusCode, Url};
use reqwest::{Request, Response, StatusCode as RequestStatusCode, Url};
use reqwest_middleware::{Error, Result}; use reqwest_middleware::{Error, Result};
use tracing::{warn, Span}; use tracing::{warn, Span};
use crate::reqwest_otel_span; use crate::reqwest_otel_span;
/// The `http.method` field added to the span by [`reqwest_otel_span`] /// The `http.request.method` field added to the span by [`reqwest_otel_span`]
pub const HTTP_METHOD: &str = "http.method"; pub const HTTP_REQUEST_METHOD: &str = "http.request.method";
/// The `http.scheme` field added to the span by [`reqwest_otel_span`] /// The `url.scheme` field added to the span by [`reqwest_otel_span`]
pub const HTTP_SCHEME: &str = "http.scheme"; pub const URL_SCHEME: &str = "url.scheme";
/// The `http.host` field added to the span by [`reqwest_otel_span`] /// The `server.address` field added to the span by [`reqwest_otel_span`]
pub const HTTP_HOST: &str = "http.host"; pub const SERVER_ADDRESS: &str = "server.address";
/// The `http.url` field added to the span by [`reqwest_otel_span`] /// The `server.port` field added to the span by [`reqwest_otel_span`]
pub const HTTP_URL: &str = "http.url"; pub const SERVER_PORT: &str = "server.port";
/// The `host.port` field added to the span by [`reqwest_otel_span`] /// The `url.full` field added to the span by [`reqwest_otel_span`]
pub const NET_HOST_PORT: &str = "net.host.port"; pub const URL_FULL: &str = "url.full";
/// The `user_agent.original` field added to the span by [`reqwest_otel_span`]
pub const USER_AGENT_ORIGINAL: &str = "user_agent.original";
/// The `otel.kind` field added to the span by [`reqwest_otel_span`] /// The `otel.kind` field added to the span by [`reqwest_otel_span`]
pub const OTEL_KIND: &str = "otel.kind"; pub const OTEL_KIND: &str = "otel.kind";
/// The `otel.name` field added to the span by [`reqwest_otel_span`] /// The `otel.name` field added to the span by [`reqwest_otel_span`]
pub const OTEL_NAME: &str = "otel.name"; pub const OTEL_NAME: &str = "otel.name";
/// The `otel.status_code` field added to the span by [`reqwest_otel_span`] /// The `otel.status_code` field added to the span by [`reqwest_otel_span`]
pub const OTEL_STATUS_CODE: &str = "otel.status_code"; pub const OTEL_STATUS_CODE: &str = "otel.status_code";
/// The `http.response.status_code` field added to the span by [`reqwest_otel_span`]
pub const HTTP_RESPONSE_STATUS_CODE: &str = "http.response.status_code";
/// The `error.message` field added to the span by [`reqwest_otel_span`] /// The `error.message` field added to the span by [`reqwest_otel_span`]
pub const ERROR_MESSAGE: &str = "error.message"; pub const ERROR_MESSAGE: &str = "error.message";
/// The `error.cause_chain` field added to the span by [`reqwest_otel_span`] /// The `error.cause_chain` field added to the span by [`reqwest_otel_span`]
pub const ERROR_CAUSE_CHAIN: &str = "error.cause_chain"; pub const ERROR_CAUSE_CHAIN: &str = "error.cause_chain";
/// The `http.status_code` field added to the span by [`reqwest_otel_span`]
/// The deprecated `http.method` field added to the span by [`reqwest_otel_span`]
#[deprecated]
pub const HTTP_METHOD: &str = "http.method";
/// The deprecated `http.scheme` field added to the span by [`reqwest_otel_span`]
#[deprecated]
pub const HTTP_SCHEME: &str = "http.scheme";
/// The deprecated `http.host` field added to the span by [`reqwest_otel_span`]
#[deprecated]
pub const HTTP_HOST: &str = "http.host";
/// The deprecated `http.url` field added to the span by [`reqwest_otel_span`]
#[deprecated]
pub const HTTP_URL: &str = "http.url";
/// The deprecated `host.port` field added to the span by [`reqwest_otel_span`]
#[deprecated]
pub const NET_HOST_PORT: &str = "net.host.port";
/// The deprecated `http.status_code` field added to the span by [`reqwest_otel_span`]
#[deprecated]
pub const HTTP_STATUS_CODE: &str = "http.status_code"; pub const HTTP_STATUS_CODE: &str = "http.status_code";
/// The `http.user_agent` added to the span by [`reqwest_otel_span`] /// The deprecated `http.user_agent` added to the span by [`reqwest_otel_span`]
#[deprecated]
pub const HTTP_USER_AGENT: &str = "http.user_agent"; pub const HTTP_USER_AGENT: &str = "http.user_agent";
/// [`ReqwestOtelSpanBackend`] allows you to customise the span attached by /// [`ReqwestOtelSpanBackend`] allows you to customise the span attached by
@ -61,12 +82,10 @@ pub fn default_on_request_end(span: &Span, outcome: &Result<Response>) {
#[inline] #[inline]
pub fn default_on_request_success(span: &Span, response: &Response) { pub fn default_on_request_success(span: &Span, response: &Response) {
let span_status = get_span_status(response.status()); let span_status = get_span_status(response.status());
let user_agent = get_header_value("user_agent", response.headers());
if let Some(span_status) = span_status { if let Some(span_status) = span_status {
span.record(OTEL_STATUS_CODE, span_status); span.record(OTEL_STATUS_CODE, span_status);
} }
span.record(HTTP_STATUS_CODE, response.status().as_u16()); span.record(HTTP_RESPONSE_STATUS_CODE, response.status().as_u16());
span.record(HTTP_USER_AGENT, user_agent.as_str());
} }
/// Populates default failure fields for a given [`reqwest_otel_span!`] span. /// Populates default failure fields for a given [`reqwest_otel_span!`] span.
@ -79,7 +98,7 @@ pub fn default_on_request_failure(span: &Span, e: &Error) {
span.record(ERROR_CAUSE_CHAIN, error_cause_chain.as_str()); span.record(ERROR_CAUSE_CHAIN, error_cause_chain.as_str());
if let Error::Reqwest(e) = e { if let Error::Reqwest(e) = e {
if let Some(status) = e.status() { if let Some(status) = e.status() {
span.record(HTTP_STATUS_CODE, status.as_u16()); span.record(HTTP_RESPONSE_STATUS_CODE, status.as_u16());
} }
} }
} }
@ -122,11 +141,6 @@ impl ReqwestOtelSpanBackend for DefaultSpanBackend {
} }
} }
fn get_header_value(key: &str, headers: &HeaderMap) -> String {
let header_default = &HeaderValue::from_static("");
format!("{:?}", headers.get(key).unwrap_or(header_default)).replace('"', "")
}
/// Similar to [`DefaultSpanBackend`] but also adds the `http.url` attribute to request spans. /// Similar to [`DefaultSpanBackend`] but also adds the `http.url` attribute to request spans.
/// ///
/// [`TracingMiddleware`]: crate::middleware::TracingMiddleware /// [`TracingMiddleware`]: crate::middleware::TracingMiddleware

View file

@ -3,15 +3,16 @@
/// It empowers you to add custom properties to the span on top of the default properties provided by the macro /// It empowers you to add custom properties to the span on top of the default properties provided by the macro
/// ///
/// Default Fields: /// Default Fields:
/// - http.method /// - http.request.method
/// - http.scheme /// - url.scheme
/// - http.host /// - server.address
/// - net.host /// - server.port
/// - url.full
/// - otel.kind /// - otel.kind
/// - otel.name /// - otel.name
/// - otel.status_code /// - otel.status_code
/// - http.user_agent /// - user_agent.original
/// - http.status_code /// - http.response.status_code
/// - error.message /// - error.message
/// - error.cause_chain /// - error.cause_chain
/// ///
@ -122,23 +123,26 @@ macro_rules! reqwest_otel_span {
let url = $request.url(); let url = $request.url();
let scheme = url.scheme(); let scheme = url.scheme();
let host = url.host_str().unwrap_or(""); let host = url.host_str().unwrap_or("");
let host_port = url.port().unwrap_or(0) as i64; let host_port = url.port_or_known_default().unwrap_or(0) as i64;
let otel_name = $name.to_string(); let otel_name = $name.to_string();
let header_default = &HeaderValue::from_static("");
let user_agent = format!("{:?}", $request.headers().get("user_agent").unwrap_or(header_default)).replace('"', "");
macro_rules! request_span { macro_rules! request_span {
($lvl:expr) => { ($lvl:expr) => {
$crate::reqwest_otel_span_macro::private::span!( $crate::reqwest_otel_span_macro::private::span!(
$lvl, $lvl,
"HTTP request", "HTTP request",
http.method = %method, http.request.method = %method,
http.scheme = %scheme, url.scheme = %scheme,
http.host = %host, server.address = %host,
net.host.port = %host_port, server.port = %host_port,
url.full = %url,
user_agent.original = %user_agent,
otel.kind = "client", otel.kind = "client",
otel.name = %otel_name, otel.name = %otel_name,
otel.status_code = tracing::field::Empty, otel.status_code = tracing::field::Empty,
http.user_agent = tracing::field::Empty, http.response.status_code = tracing::field::Empty,
http.status_code = tracing::field::Empty,
error.message = tracing::field::Empty, error.message = tracing::field::Empty,
error.cause_chain = tracing::field::Empty, error.cause_chain = tracing::field::Empty,
$($field)* $($field)*