diff --git a/Cargo.toml b/Cargo.toml index 4ee8b3573..65d1bd5e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,8 @@ categories = ["asynchronous", "web-programming"] [features] default = ["opentelemetry_0_13", "emit_event_on_error"] -opentelemetry_0_13 = ["opentelemetry", "tracing-opentelemetry"] +opentelemetry_0_13 = ["opentelemetry_0_13_pkg", "tracing-opentelemetry_0_12_pkg"] +opentelemetry_0_14 = ["opentelemetry_0_14_pkg", "tracing-opentelemetry_0_13_pkg"] emit_event_on_error = [] [dependencies] @@ -31,8 +32,10 @@ tracing = "0.1.19" tracing-futures = "0.2.4" futures = "0.3.5" uuid = { version = "0.8.1", features = ["v4"] } -opentelemetry = { version = "0.13", optional = true } -tracing-opentelemetry = { version = "0.12", optional = true } +opentelemetry_0_13_pkg = { package = "opentelemetry", version = "0.13", optional = true } +opentelemetry_0_14_pkg = { package = "opentelemetry", version = "0.14", 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 } [dev-dependencies] tracing-subscriber = { version = "0.2.12", features = ["registry", "env-filter"] } diff --git a/README.md b/README.md index be53f1913..ea780cc9d 100644 --- a/README.md +++ b/README.md @@ -40,12 +40,11 @@ tracing = "0.1" actix-web = "4.0.0-beta.7" ``` -`tracing-actix-web` exposes two feature flags: +`tracing-actix-web` exposes three feature flags: -- `opentelemetry_0_13`: attach [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-rust)'s context to the root span; -- `emit_event_on_error`: emit a [`tracing`] event when request processing fails with an error. - -They are both enabled by default. +- `opentelemetry_0_13`: attach [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-rust)'s context to the root span using OTEL 0.13 (enabled by default). +- `opentelemetry_0_14`: same as above but using OTEL 0.14. +- `emit_event_on_error`: emit a [`tracing`] event when request processing fails with an error (enabled by default). `tracing-actix-web` will release `0.4.0`, going out of beta, as soon as `actix-web` releases a stable `4.0.0`. @@ -242,7 +241,7 @@ async fn index(request_id: RequestId) -> String { `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, if you have not disabled the `opentelemetry_0_13` feature flag, `tracing-actix-web` automatically +Furthermore, if you have not disabled the `opentelemetry_0_13` or `opentelemetry_0_14` feature flags, `tracing-actix-web` automatically performs trace propagation according to the OpenTelemetry standard. 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. diff --git a/examples/opentelemetry/Cargo.toml b/examples/opentelemetry/Cargo.toml index 8ac0b3cca..c7ad9f97c 100644 --- a/examples/opentelemetry/Cargo.toml +++ b/examples/opentelemetry/Cargo.toml @@ -9,9 +9,9 @@ license = "MIT/Apache-2.0" [dependencies] actix-web = "=4.0.0-beta.7" tracing = "0.1.19" -opentelemetry = { version = "0.13", features = ["rt-tokio-current-thread"] } -opentelemetry-jaeger = { version = "0.12", features = ["tokio"] } -tracing-opentelemetry = { version = "0.12" } +opentelemetry = { version = "0.14", features = ["rt-tokio-current-thread"] } +opentelemetry-jaeger = { version = "0.13", features = ["tokio"] } +tracing-opentelemetry = { version = "0.13" } tracing-subscriber = { version = "0.2.12", features = ["registry", "env-filter"] } tracing-bunyan-formatter = "0.1.6" -tracing-actix-web = { path = "../.." } +tracing-actix-web = { path = "../..", features = ["opentelemetry_0_14"] } diff --git a/src/lib.rs b/src/lib.rs index fa8d572f5..15e552a38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,12 +13,11 @@ //! actix-web = "4.0.0-beta.7" //! ``` //! -//! `tracing-actix-web` exposes two feature flags: +//! `tracing-actix-web` exposes three feature flags: //! -//! - `opentelemetry_0_13`: attach [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-rust)'s context to the root span; -//! - `emit_event_on_error`: emit a [`tracing`] event when request processing fails with an error. -//! -//! They are both enabled by default. +//! - `opentelemetry_0_13`: attach [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-rust)'s context to the root span using OTEL 0.13 (enabled by default). +//! - `opentelemetry_0_14`: same as above but using OTEL 0.14. +//! - `emit_event_on_error`: emit a [`tracing`] event when request processing fails with an error (enabled by default). //! //! `tracing-actix-web` will release `0.4.0`, going out of beta, as soon as `actix-web` releases a stable `4.0.0`. //! @@ -215,7 +214,7 @@ //! //! `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, if you have not disabled the `opentelemetry_0_13` feature flag, `tracing-actix-web` automatically +//! Furthermore, if you have not disabled the `opentelemetry_0_13` or `opentelemetry_0_14` feature flags, `tracing-actix-web` automatically //! performs trace propagation according to the OpenTelemetry standard. //! 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. @@ -245,4 +244,7 @@ pub use root_span_builder::{DefaultRootSpanBuilder, RootSpanBuilder}; pub mod root_span_macro; #[cfg(feature = "opentelemetry_0_13")] -mod otel; +mod otel_0_13; + +#[cfg(feature = "opentelemetry_0_14")] +mod otel_0_14; diff --git a/src/otel.rs b/src/otel.rs deleted file mode 100644 index 8c9a67142..000000000 --- a/src/otel.rs +++ /dev/null @@ -1,21 +0,0 @@ -use opentelemetry::propagation::Extractor; - -pub(crate) struct RequestHeaderCarrier<'a> { - headers: &'a actix_web::http::HeaderMap, -} - -impl<'a> RequestHeaderCarrier<'a> { - pub(crate) fn new(headers: &'a actix_web::http::HeaderMap) -> Self { - RequestHeaderCarrier { headers } - } -} - -impl<'a> Extractor for RequestHeaderCarrier<'a> { - fn get(&self, key: &str) -> Option<&str> { - self.headers.get(key).and_then(|v| v.to_str().ok()) - } - - fn keys(&self) -> Vec<&str> { - self.headers.keys().map(|header| header.as_str()).collect() - } -} diff --git a/src/otel_0_13.rs b/src/otel_0_13.rs new file mode 100644 index 000000000..37fbf03c8 --- /dev/null +++ b/src/otel_0_13.rs @@ -0,0 +1,36 @@ +use actix_web::dev::ServiceRequest; +use opentelemetry_0_13_pkg::propagation::Extractor; + +pub(crate) struct RequestHeaderCarrier<'a> { + headers: &'a actix_web::http::HeaderMap, +} + +impl<'a> RequestHeaderCarrier<'a> { + pub(crate) fn new(headers: &'a actix_web::http::HeaderMap) -> Self { + RequestHeaderCarrier { headers } + } +} + +impl<'a> Extractor for RequestHeaderCarrier<'a> { + fn get(&self, key: &str) -> Option<&str> { + self.headers.get(key).and_then(|v| v.to_str().ok()) + } + + fn keys(&self) -> Vec<&str> { + self.headers.keys().map(|header| header.as_str()).collect() + } +} + +pub(crate) fn set_otel_parent(req: &ServiceRequest, span: &tracing::Span) { + use opentelemetry_0_13_pkg::trace::TraceContextExt as _; + use tracing_opentelemetry_0_12_pkg::OpenTelemetrySpanExt as _; + + let parent_context = opentelemetry_0_13_pkg::global::get_text_map_propagator(|propagator| { + propagator.extract(&crate::otel_0_13::RequestHeaderCarrier::new(req.headers())) + }); + 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. + let trace_id = span.context().span().span_context().trace_id().to_hex(); + span.record("trace_id", &tracing::field::display(trace_id)); +} diff --git a/src/otel_0_14.rs b/src/otel_0_14.rs new file mode 100644 index 000000000..40698c912 --- /dev/null +++ b/src/otel_0_14.rs @@ -0,0 +1,36 @@ +use actix_web::dev::ServiceRequest; +use opentelemetry_0_14_pkg::propagation::Extractor; + +pub(crate) struct RequestHeaderCarrier<'a> { + headers: &'a actix_web::http::HeaderMap, +} + +impl<'a> RequestHeaderCarrier<'a> { + pub(crate) fn new(headers: &'a actix_web::http::HeaderMap) -> Self { + RequestHeaderCarrier { headers } + } +} + +impl<'a> Extractor for RequestHeaderCarrier<'a> { + fn get(&self, key: &str) -> Option<&str> { + self.headers.get(key).and_then(|v| v.to_str().ok()) + } + + fn keys(&self) -> Vec<&str> { + self.headers.keys().map(|header| header.as_str()).collect() + } +} + +pub(crate) fn set_otel_parent(req: &ServiceRequest, span: &tracing::Span) { + use opentelemetry_0_14_pkg::trace::TraceContextExt as _; + use tracing_opentelemetry_0_13_pkg::OpenTelemetrySpanExt as _; + + let parent_context = opentelemetry_0_14_pkg::global::get_text_map_propagator(|propagator| { + propagator.extract(&crate::otel_0_14::RequestHeaderCarrier::new(req.headers())) + }); + 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. + let trace_id = span.context().span().span_context().trace_id().to_hex(); + span.record("trace_id", &tracing::field::display(trace_id)); +} diff --git a/src/root_span_macro.rs b/src/root_span_macro.rs index 2b4ee67b0..9902725b4 100644 --- a/src/root_span_macro.rs +++ b/src/root_span_macro.rs @@ -97,7 +97,12 @@ macro_rules! root_span { ); std::mem::drop(connection_info); - $crate::root_span_macro::private::set_otel_parent(&$request, &span); + #[cfg(feature = "opentelemetry_0_13")] + $crate::root_span_macro::private::set_otel_parent_0_13(&$request, &span); + + #[cfg(feature = "opentelemetry_0_14")] + $crate::root_span_macro::private::set_otel_parent_0_14(&$request, &span); + span } }; @@ -116,26 +121,16 @@ pub mod private { pub use tracing; - #[cfg(not(feature = "opentelemetry_0_13"))] - #[doc(hidden)] - pub fn set_otel_parent(_req: &ServiceRequest, _span: &tracing::Span) { - // No-op if the OpenTelemetry feature is not active - } - #[cfg(feature = "opentelemetry_0_13")] #[doc(hidden)] - pub fn set_otel_parent(req: &ServiceRequest, span: &tracing::Span) { - use opentelemetry::trace::TraceContextExt as _; - use tracing_opentelemetry::OpenTelemetrySpanExt as _; + pub fn set_otel_parent_0_13(req: &ServiceRequest, span: &tracing::Span) { + crate::otel_0_13::set_otel_parent(req, span); + } - let parent_context = opentelemetry::global::get_text_map_propagator(|propagator| { - propagator.extract(&crate::otel::RequestHeaderCarrier::new(req.headers())) - }); - 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. - let trace_id = span.context().span().span_context().trace_id().to_hex(); - span.record("trace_id", &tracing::field::display(trace_id)); + #[cfg(feature = "opentelemetry_0_14")] + #[doc(hidden)] + pub fn set_otel_parent_0_14(req: &ServiceRequest, span: &tracing::Span) { + crate::otel_0_14::set_otel_parent(req, span); } #[doc(hidden)]