diff --git a/tracing-actix-web/CHANGELOG.md b/tracing-actix-web/CHANGELOG.md new file mode 100644 index 0000000000..1f2344f373 --- /dev/null +++ b/tracing-actix-web/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## Unreleased + +- Support Opentelemetry 0.31 diff --git a/tracing-actix-web/Cargo.toml b/tracing-actix-web/Cargo.toml index e587c5087b..d32ee21a4b 100644 --- a/tracing-actix-web/Cargo.toml +++ b/tracing-actix-web/Cargo.toml @@ -15,12 +15,11 @@ readme = "README.md" description = "Structured logging middleware for actix-web." keywords = ["http", "actix-web", "tracing", "logging"] categories = ["asynchronous", "web-programming"] - -repository.workspace = true -homepage.workspace = true -license.workspace = true -edition.workspace = true -rust-version.workspace = true +repository = "https://github.com/actix/actix-extras" +homepage = "https://actix.rs" +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.82" [features] default = ["emit_event_on_error"] @@ -96,6 +95,10 @@ opentelemetry_0_30 = [ "opentelemetry_0_30_pkg", "tracing-opentelemetry_0_31_pkg", ] +opentelemetry_0_31 = [ + "opentelemetry_0_31_pkg", + "tracing-opentelemetry_0_32_pkg", +] emit_event_on_error = [] uuid_v7 = ["uuid/v7"] @@ -123,6 +126,7 @@ opentelemetry_0_27_pkg = { package = "opentelemetry", version = "0.27", optional opentelemetry_0_28_pkg = { package = "opentelemetry", version = "0.28", optional = true } opentelemetry_0_29_pkg = { package = "opentelemetry", version = "0.29", optional = true } opentelemetry_0_30_pkg = { package = "opentelemetry", version = "0.30", optional = true } +opentelemetry_0_31_pkg = { package = "opentelemetry", version = "0.31", optional = true } tracing-opentelemetry_0_12_pkg = { package = "tracing-opentelemetry", version = "0.12", optional = true } tracing-opentelemetry_0_13_pkg = { package = "tracing-opentelemetry", version = "0.13", optional = true } tracing-opentelemetry_0_14_pkg = { package = "tracing-opentelemetry", version = "0.14", optional = true } @@ -141,6 +145,7 @@ tracing-opentelemetry_0_28_pkg = { package = "tracing-opentelemetry", version = tracing-opentelemetry_0_29_pkg = { package = "tracing-opentelemetry", version = "0.29", optional = true } tracing-opentelemetry_0_30_pkg = { package = "tracing-opentelemetry", version = "0.30", optional = true } tracing-opentelemetry_0_31_pkg = { package = "tracing-opentelemetry", version = "0.31", optional = true } +tracing-opentelemetry_0_32_pkg = { package = "tracing-opentelemetry", version = "0.32", optional = true } [dev-dependencies] actix-web = { version = "4", default-features = false, features = ["macros"] } diff --git a/tracing-actix-web/README.md b/tracing-actix-web/README.md index b2da8cbe48..98b7b539ef 100644 --- a/tracing-actix-web/README.md +++ b/tracing-actix-web/README.md @@ -64,6 +64,7 @@ actix-web = "4" - `opentelemetry_0_28`: same as above but using `opentelemetry` 0.28; - `opentelemetry_0_29`: same as above but using `opentelemetry` 0.29; - `opentelemetry_0_30`: same as above but using `opentelemetry` 0.30; +- `opentelemetry_0_31`: same as above but using `opentelemetry` 0.31; - `emit_event_on_error`: emit a [`tracing`] event when request processing fails with an error (enabled by default). - `uuid_v7`: use the UUID v7 implementation inside [`RequestId`] instead of UUID v4 (disabled by default). ## Quickstart diff --git a/tracing-actix-web/examples/custom-root-span/Cargo.toml b/tracing-actix-web/examples/custom-root-span/Cargo.toml index 99df674a5e..e0630d021a 100644 --- a/tracing-actix-web/examples/custom-root-span/Cargo.toml +++ b/tracing-actix-web/examples/custom-root-span/Cargo.toml @@ -8,12 +8,12 @@ edition = "2021" [dependencies] actix-web = "4" -opentelemetry = "0.30" -opentelemetry-otlp = { version = "0.30", features = ["grpc-tonic"] } -opentelemetry_sdk = { version = "0.30", features = ["rt-tokio-current-thread"] } -opentelemetry-semantic-conventions = "0.30" -tracing-opentelemetry = "0.31" +opentelemetry = "0.31" +opentelemetry-otlp = { version = "0.31", features = ["grpc-tonic"] } +opentelemetry_sdk = { version = "0.31", features = ["rt-tokio-current-thread"] } +opentelemetry-semantic-conventions = "0.31" +tracing-opentelemetry = "0.32" tracing = "0.1" -tracing-actix-web = { path = "../..", features = ["opentelemetry_0_30"] } +tracing-actix-web = { path = "../..", features = ["opentelemetry_0_31"] } tracing-bunyan-formatter = "0.3" tracing-subscriber = { version = "0.3", features = ["registry", "env-filter"] } diff --git a/tracing-actix-web/examples/custom-root-span/src/main.rs b/tracing-actix-web/examples/custom-root-span/src/main.rs index 9b16bfa958..5161641219 100644 --- a/tracing-actix-web/examples/custom-root-span/src/main.rs +++ b/tracing-actix-web/examples/custom-root-span/src/main.rs @@ -1,13 +1,14 @@ -use actix_web::body::MessageBody; -use actix_web::dev::{ServiceRequest, ServiceResponse}; -use actix_web::{web, App, Error, HttpServer}; -use opentelemetry::trace::TracerProvider; -use opentelemetry::{global, KeyValue}; +use std::{io, sync::LazyLock}; + +use actix_web::{ + body::MessageBody, + dev::{ServiceRequest, ServiceResponse}, + web, App, Error, HttpServer, +}; +use opentelemetry::{global, trace::TracerProvider, KeyValue}; use opentelemetry_otlp::WithExportConfig; use opentelemetry_sdk::{propagation::TraceContextPropagator, Resource}; use opentelemetry_semantic_conventions::resource; -use std::io; -use std::sync::LazyLock; use tracing::Span; use tracing_actix_web::{DefaultRootSpanBuilder, RootSpan, RootSpanBuilder, TracingLogger}; use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer}; diff --git a/tracing-actix-web/examples/opentelemetry/Cargo.toml b/tracing-actix-web/examples/opentelemetry/Cargo.toml index e81ab03e09..94ac4e9b78 100644 --- a/tracing-actix-web/examples/opentelemetry/Cargo.toml +++ b/tracing-actix-web/examples/opentelemetry/Cargo.toml @@ -8,12 +8,12 @@ license = "MIT/Apache-2.0" [dependencies] actix-web = "4" -opentelemetry = "0.30" -opentelemetry-otlp = { version = "0.30", features = ["grpc-tonic"] } -opentelemetry_sdk = { version = "0.30", features = ["rt-tokio-current-thread"] } -opentelemetry-semantic-conventions = "0.30" +opentelemetry = "0.31" +opentelemetry-otlp = { version = "0.31", features = ["grpc-tonic"] } +opentelemetry_sdk = { version = "0.31", features = ["rt-tokio-current-thread"] } +opentelemetry-semantic-conventions = "0.31" tracing = "0.1" -tracing-actix-web = { path = "../..", features = ["opentelemetry_0_30"] } +tracing-actix-web = { path = "../..", features = ["opentelemetry_0_31"] } tracing-bunyan-formatter = "0.3" -tracing-opentelemetry = "0.31" +tracing-opentelemetry = "0.32" tracing-subscriber = { version = "0.3", features = ["registry", "env-filter"] } diff --git a/tracing-actix-web/examples/opentelemetry/src/main.rs b/tracing-actix-web/examples/opentelemetry/src/main.rs index 757c1b441f..496a2bdb81 100644 --- a/tracing-actix-web/examples/opentelemetry/src/main.rs +++ b/tracing-actix-web/examples/opentelemetry/src/main.rs @@ -1,11 +1,10 @@ +use std::{io, sync::LazyLock}; + use actix_web::{web, App, HttpServer}; -use opentelemetry::trace::TracerProvider; -use opentelemetry::{global, KeyValue}; +use opentelemetry::{global, trace::TracerProvider, KeyValue}; use opentelemetry_otlp::WithExportConfig; use opentelemetry_sdk::{propagation::TraceContextPropagator, Resource}; use opentelemetry_semantic_conventions::resource; -use std::io; -use std::sync::LazyLock; use tracing_actix_web::TracingLogger; use tracing_bunyan_formatter::{BunyanFormattingLayer, JsonStorageLayer}; use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Registry}; diff --git a/tracing-actix-web/examples/request-id-response-header/src/main.rs b/tracing-actix-web/examples/request-id-response-header/src/main.rs index 5f40550a18..6faae54bf6 100644 --- a/tracing-actix-web/examples/request-id-response-header/src/main.rs +++ b/tracing-actix-web/examples/request-id-response-header/src/main.rs @@ -1,9 +1,10 @@ +use std::io; + use actix_web::{ dev::Service, http::header::{HeaderName, HeaderValue}, web, App, HttpMessage, HttpServer, }; -use std::io; use tracing_actix_web::{RequestId, TracingLogger}; async fn hello() -> &'static str { diff --git a/tracing-actix-web/src/lib.rs b/tracing-actix-web/src/lib.rs index 7543d6d9ad..69a54897e0 100644 --- a/tracing-actix-web/src/lib.rs +++ b/tracing-actix-web/src/lib.rs @@ -37,6 +37,7 @@ //! - `opentelemetry_0_28`: same as above but using `opentelemetry` 0.28; //! - `opentelemetry_0_29`: same as above but using `opentelemetry` 0.29; //! - `opentelemetry_0_30`: same as above but using `opentelemetry` 0.30; +//! - `opentelemetry_0_31`: same as above but using `opentelemetry` 0.31; //! - `emit_event_on_error`: emit a [`tracing`] event when request processing fails with an error (enabled by default). //! - `uuid_v7`: use the UUID v7 implementation inside [`RequestId`] instead of UUID v4 (disabled by default). //! @@ -64,20 +65,20 @@ //! [`TracingLogger`] is built on top of [`tracing`], a modern instrumentation framework with //! [a vibrant ecosystem](https://github.com/tokio-rs/tracing#related-crates). //! -//! `tracing-actix-web`'s documentation provides a crash course in how to use [`tracing`] to instrument an `actix-web` application. +//! `tracing-actix-web`'s documentation provides a crash course in how to use [`tracing`] to instrument an `actix-web` application. //! If you want to learn more check out ["Are we observable yet?"](https://www.lpalmieri.com/posts/2020-09-27-zero-to-production-4-are-we-observable-yet/) - //! it provides an in-depth introduction to the crate and the problems it solves within the bigger picture of [observability](https://docs.honeycomb.io/learning-about-observability/). //! //! ## The root span //! -//! [`tracing::Span`] is the key abstraction in [`tracing`]: it represents a unit of work in your system. +//! [`tracing::Span`] is the key abstraction in [`tracing`]: it represents a unit of work in your system. //! A [`tracing::Span`] has a beginning and an end. It can include one or more **child spans** to represent sub-unit //! of works within a larger task. //! -//! When your application receives a request, [`TracingLogger`] creates a new span - we call it the **[root span]**. +//! When your application receives a request, [`TracingLogger`] creates a new span - we call it the **[root span]**. //! All the spans created _while_ processing the request will be children of the root span. //! -//! [`tracing`] empowers us to attach structured properties to a span as a collection of key-value pairs. +//! [`tracing`] empowers us to attach structured properties to a span as a collection of key-value pairs. //! Those properties can then be queried in a variety of tools (e.g. ElasticSearch, Honeycomb, DataDog) to //! understand what is happening in your system. //! @@ -103,11 +104,11 @@ //! let another_way = TracingLogger::::new(); //! ``` //! -//! We are delegating the construction of the root span to [`DefaultRootSpanBuilder`]. +//! We are delegating the construction of the root span to [`DefaultRootSpanBuilder`]. //! [`DefaultRootSpanBuilder`] captures, out of the box, several dimensions that are usually relevant when looking at an HTTP //! API: method, version, route, etc. - check out its documentation for an extensive list. //! -//! You can customise the root span by providing your own implementation of the [`RootSpanBuilder`] trait. +//! You can customise the root span by providing your own implementation of the [`RootSpanBuilder`] trait. //! Let's imagine, for example, that our system cares about a client identifier embedded inside an authorization header. //! We could add a `client_id` property to the root span using a custom builder, `DomainRootSpanBuilder`: //! @@ -132,7 +133,7 @@ //! let custom_middleware = TracingLogger::::new(); //! ``` //! -//! There is an issue, though: `client_id` is the _only_ property we are capturing. +//! There is an issue, though: `client_id` is the _only_ property we are capturing. //! With `DomainRootSpanBuilder`, as it is, we do not get any of that useful HTTP-related information provided by //! [`DefaultRootSpanBuilder`]. //! @@ -164,7 +165,7 @@ //! [`root_span!`] is a macro provided by `tracing-actix-web`: it creates a new span by combining all the HTTP properties tracked //! by [`DefaultRootSpanBuilder`] with the custom ones you specify when calling it (e.g. `client_id` in our example). //! -//! We need to use a macro because `tracing` requires all the properties attached to a span to be declared upfront, when the span is created. +//! We need to use a macro because `tracing` requires all the properties attached to a span to be declared upfront, when the span is created. //! You cannot add new ones afterwards. This makes it extremely fast, but it pushes us to reach for macros when we need some level of //! composition. //! @@ -200,7 +201,7 @@ //! //! ## The [`RootSpan`] extractor //! -//! It often happens that not all information about a task is known upfront, encoded in the incoming request. +//! It often happens that not all information about a task is known upfront, encoded in the incoming request. //! You can use the [`RootSpan`] extractor to grab the root span in your handlers and attach more information //! to your root span as it becomes available: //! @@ -272,10 +273,10 @@ //! To fulfill a request you often have to perform additional I/O operations - e.g. calls to other REST or gRPC APIs, database queries, etc. //! **Distributed tracing** is the standard approach to **trace** a single request across the entirety of your stack. //! -//! `tracing-actix-web` provides support for distributed tracing by supporting the [OpenTelemetry standard](https://opentelemetry.io/). +//! `tracing-actix-web` provides support for distributed tracing by supporting the [OpenTelemetry standard](https://opentelemetry.io/). //! `tracing-actix-web` follows [OpenTelemetry's semantic convention](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/overview.md#spancontext) //! for field names. -//! Furthermore, it provides an `opentelemetry_0_17` feature flag to automatically performs trace propagation: it tries to extract the OpenTelemetry context out of the headers of incoming requests and, when it finds one, it sets it as the remote context for the current root span. The context is then propagated to your downstream dependencies if your HTTP or gRPC clients are OpenTelemetry-aware - e.g. using [`reqwest-middleware` and `reqwest-tracing`](https://github.com/TrueLayer/reqwest-middleware) if you are using `reqwest` as your HTTP client. +//! Furthermore, it provides an `opentelemetry_0_17` feature flag to automatically performs trace propagation: it tries to extract the OpenTelemetry context out of the headers of incoming requests and, when it finds one, it sets it as the remote context for the current root span. The context is then propagated to your downstream dependencies if your HTTP or gRPC clients are OpenTelemetry-aware - e.g. using [`reqwest-middleware` and `reqwest-tracing`](https://github.com/TrueLayer/reqwest-middleware) if you are using `reqwest` as your HTTP client. //! You can then find all logs for the same request across all the services it touched by looking for the `trace_id`, automatically logged by `tracing-actix-web`. //! //! If you add [`tracing-opentelemetry::OpenTelemetryLayer`](https://docs.rs/tracing-opentelemetry/0.17.0/tracing_opentelemetry/struct.OpenTelemetryLayer.html) @@ -319,6 +320,7 @@ mutually_exclusive_features::none_or_one_of!( "opentelemetry_0_28", "opentelemetry_0_29", "opentelemetry_0_30", + "opentelemetry_0_31", ); #[cfg(any( @@ -340,5 +342,6 @@ mutually_exclusive_features::none_or_one_of!( feature = "opentelemetry_0_28", feature = "opentelemetry_0_29", feature = "opentelemetry_0_30", + feature = "opentelemetry_0_31", ))] mod otel; diff --git a/tracing-actix-web/src/middleware.rs b/tracing-actix-web/src/middleware.rs index 9bd706eff3..7d7b74b453 100644 --- a/tracing-actix-web/src/middleware.rs +++ b/tracing-actix-web/src/middleware.rs @@ -1,14 +1,19 @@ -use crate::{DefaultRootSpanBuilder, RequestId, RootSpan, RootSpanBuilder}; -use actix_web::body::{BodySize, MessageBody}; -use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform}; -use actix_web::http::StatusCode; -use actix_web::web::Bytes; -use actix_web::{Error, HttpMessage, ResponseError}; -use std::future::{ready, Future, Ready}; -use std::pin::Pin; -use std::task::{Context, Poll}; +use std::{ + future::{ready, Future, Ready}, + pin::Pin, + task::{Context, Poll}, +}; + +use actix_web::{ + body::{BodySize, MessageBody}, + dev::{Service, ServiceRequest, ServiceResponse, Transform}, + web::Bytes, + Error, HttpMessage, +}; use tracing::Span; +use crate::{DefaultRootSpanBuilder, RequestId, RootSpan, RootSpanBuilder}; + /// `TracingLogger` is a middleware to capture structured diagnostic when processing an HTTP request. /// Check the crate-level documentation for an in-depth introduction. /// @@ -16,7 +21,7 @@ use tracing::Span; /// /// # Usage /// -/// Register `TracingLogger` as a middleware for your application using `.wrap` on `App`. +/// Register `TracingLogger` as a middleware for your application using `.wrap` on `App`. /// In this example we add a [`tracing::Subscriber`] to output structured logs to the console. /// /// ```rust @@ -235,6 +240,7 @@ where } } +#[cfg(feature = "emit_event_on_error")] fn emit_event_on_error(outcome: &Result, actix_web::Error>) { match outcome { Ok(response) => { @@ -250,7 +256,11 @@ fn emit_event_on_error(outcome: &Result, actix_we } } -fn emit_error_event(response_error: &dyn ResponseError, status_code: StatusCode) { +#[cfg(feature = "emit_event_on_error")] +fn emit_error_event( + response_error: &dyn actix_web::ResponseError, + status_code: actix_web::http::StatusCode, +) { let error_msg_prefix = "Error encountered while processing the incoming HTTP request"; if status_code.is_client_error() { tracing::warn!("{}: {:?}", error_msg_prefix, response_error); diff --git a/tracing-actix-web/src/otel.rs b/tracing-actix-web/src/otel.rs index d82572dbec..54500a5736 100644 --- a/tracing-actix-web/src/otel.rs +++ b/tracing-actix-web/src/otel.rs @@ -1,5 +1,5 @@ use actix_web::dev::ServiceRequest; - +use opentelemetry::propagation::Extractor; #[cfg(feature = "opentelemetry_0_13")] use opentelemetry_0_13_pkg as opentelemetry; #[cfg(feature = "opentelemetry_0_14")] @@ -36,7 +36,8 @@ use opentelemetry_0_28_pkg as opentelemetry; use opentelemetry_0_29_pkg as opentelemetry; #[cfg(feature = "opentelemetry_0_30")] use opentelemetry_0_30_pkg as opentelemetry; - +#[cfg(feature = "opentelemetry_0_31")] +use opentelemetry_0_31_pkg as opentelemetry; #[cfg(feature = "opentelemetry_0_13")] use tracing_opentelemetry_0_12_pkg as tracing_opentelemetry; #[cfg(feature = "opentelemetry_0_14")] @@ -73,8 +74,8 @@ use tracing_opentelemetry_0_29_pkg as tracing_opentelemetry; use tracing_opentelemetry_0_30_pkg as tracing_opentelemetry; #[cfg(feature = "opentelemetry_0_30")] use tracing_opentelemetry_0_31_pkg as tracing_opentelemetry; - -use opentelemetry::propagation::Extractor; +#[cfg(feature = "opentelemetry_0_31")] +use tracing_opentelemetry_0_32_pkg as tracing_opentelemetry; pub(crate) struct RequestHeaderCarrier<'a> { headers: &'a actix_web::http::header::HeaderMap, @@ -103,7 +104,7 @@ pub(crate) fn set_otel_parent(req: &ServiceRequest, span: &tracing::Span) { let parent_context = opentelemetry::global::get_text_map_propagator(|propagator| { propagator.extract(&RequestHeaderCarrier::new(req.headers())) }); - span.set_parent(parent_context); + let _ = span.set_parent(parent_context); // If we have a remote parent span, this will be the parent's trace identifier. // If not, it will be the newly generated trace identifier with this request as root span. #[cfg(not(any( @@ -121,6 +122,7 @@ pub(crate) fn set_otel_parent(req: &ServiceRequest, span: &tracing::Span) { feature = "opentelemetry_0_28", feature = "opentelemetry_0_29", feature = "opentelemetry_0_30", + feature = "opentelemetry_0_31", )))] let trace_id = span.context().span().span_context().trace_id().to_hex(); @@ -139,6 +141,7 @@ pub(crate) fn set_otel_parent(req: &ServiceRequest, span: &tracing::Span) { feature = "opentelemetry_0_28", feature = "opentelemetry_0_29", feature = "opentelemetry_0_30", + feature = "opentelemetry_0_31", ))] let trace_id = { let id = span.context().span().span_context().trace_id(); diff --git a/tracing-actix-web/src/request_id.rs b/tracing-actix-web/src/request_id.rs index f59a3c2fe6..2c96f8da1d 100644 --- a/tracing-actix-web/src/request_id.rs +++ b/tracing-actix-web/src/request_id.rs @@ -1,6 +1,6 @@ -use actix_web::{dev::Payload, HttpMessage}; -use actix_web::{FromRequest, HttpRequest, ResponseError}; use std::future::{ready, Ready}; + +use actix_web::{dev::Payload, FromRequest, HttpMessage, HttpRequest, ResponseError}; use uuid::Uuid; /// A unique identifier generated for each incoming request. diff --git a/tracing-actix-web/src/root_span.rs b/tracing-actix-web/src/root_span.rs index 624a442121..366326c680 100644 --- a/tracing-actix-web/src/root_span.rs +++ b/tracing-actix-web/src/root_span.rs @@ -1,6 +1,6 @@ -use actix_web::{dev::Payload, HttpMessage}; -use actix_web::{FromRequest, HttpRequest, ResponseError}; use std::future::{ready, Ready}; + +use actix_web::{dev::Payload, FromRequest, HttpMessage, HttpRequest, ResponseError}; use tracing::Span; #[derive(Clone)] diff --git a/tracing-actix-web/src/root_span_builder.rs b/tracing-actix-web/src/root_span_builder.rs index fba60f70f0..619ff4267e 100644 --- a/tracing-actix-web/src/root_span_builder.rs +++ b/tracing-actix-web/src/root_span_builder.rs @@ -1,10 +1,13 @@ -use crate::root_span; -use actix_web::body::MessageBody; -use actix_web::dev::{ServiceRequest, ServiceResponse}; -use actix_web::http::StatusCode; -use actix_web::{Error, ResponseError}; +use actix_web::{ + body::MessageBody, + dev::{ServiceRequest, ServiceResponse}, + http::StatusCode, + Error, ResponseError, +}; use tracing::Span; +use crate::root_span; + /// `RootSpanBuilder` allows you to customise the root span attached by /// [`TracingLogger`] to incoming requests. /// diff --git a/tracing-actix-web/src/root_span_macro.rs b/tracing-actix-web/src/root_span_macro.rs index 53469d510c..68a6872f87 100644 --- a/tracing-actix-web/src/root_span_macro.rs +++ b/tracing-actix-web/src/root_span_macro.rs @@ -142,13 +142,16 @@ pub mod private { //! in the code generated by the `root_span` macro. //! Items in this module are not part of the public interface of `tracing-actix-web` - they are considered //! implementation details and will change without notice in patch, minor and major releases. - use crate::RequestId; - use actix_web::dev::ServiceRequest; - use actix_web::http::{Method, Version}; use std::borrow::Cow; + use actix_web::{ + dev::ServiceRequest, + http::{Method, Version}, + }; pub use tracing; + use crate::RequestId; + #[doc(hidden)] // We need to allow unused variables because the function // body is empty if the user of the library chose not to activate @@ -174,6 +177,7 @@ pub mod private { feature = "opentelemetry_0_28", feature = "opentelemetry_0_29", feature = "opentelemetry_0_30", + feature = "opentelemetry_0_31", ))] crate::otel::set_otel_parent(req, span); }