From 0eb13e97949be25f5e800d28d940cc0e5886e06f Mon Sep 17 00:00:00 2001 From: Luca Palmieri <20745048+LukeMathWalker@users.noreply.github.com> Date: Sat, 29 Oct 2022 15:09:20 +0200 Subject: [PATCH] Allow level customisation (#91) * Add the possibility to specify the span level for the root span. * Improve docs. --- README.md | 29 ++++++++++++++++ src/lib.rs | 31 ++++++++++++++++++ src/root_span_builder.rs | 2 +- src/root_span_macro.rs | 71 +++++++++++++++++++++++++++------------- 4 files changed, 109 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index c67afbfe9..2df832363 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,35 @@ We need to use a macro because `tracing` requires all the properties attached to 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. +[`root_span!`] exposes more or less the same knob you can find on `tracing`'s `span!` macro. You can, for example, customise +the span level: + +```rust +use actix_web::dev::{ServiceResponse, ServiceRequest}; +use actix_web::Error; +use tracing_actix_web::{TracingLogger, DefaultRootSpanBuilder, RootSpanBuilder, Level}; +use tracing::Span; + +pub struct CustomLevelRootSpanBuilder; + +impl RootSpanBuilder for CustomLevelRootSpanBuilder { + fn on_request_start(request: &ServiceRequest) -> Span { + let level = if request.path() == "/health_check" { + Level::DEBUG + } else { + Level::INFO + }; + tracing_actix_web::root_span!(level = level, request) + } + + fn on_request_end(span: Span, outcome: &Result, Error>) { + DefaultRootSpanBuilder::on_request_end(span, outcome); + } +} + +let custom_middleware = TracingLogger::::new(); +``` + ## The [`RootSpan`] extractor It often happens that not all information about a task is known upfront, encoded in the incoming request. diff --git a/src/lib.rs b/src/lib.rs index cf2e10ab5..0b98bf9e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -151,6 +151,35 @@ //! 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. //! +//! [`root_span!`] exposes more or less the same knob you can find on `tracing`'s `span!` macro. You can, for example, customise +//! the span level: +//! +//! ```rust +//! use actix_web::dev::{ServiceResponse, ServiceRequest}; +//! use actix_web::Error; +//! use tracing_actix_web::{TracingLogger, DefaultRootSpanBuilder, RootSpanBuilder, Level}; +//! use tracing::Span; +//! +//! pub struct CustomLevelRootSpanBuilder; +//! +//! impl RootSpanBuilder for CustomLevelRootSpanBuilder { +//! fn on_request_start(request: &ServiceRequest) -> Span { +//! let level = if request.path() == "/health_check" { +//! Level::DEBUG +//! } else { +//! Level::INFO +//! }; +//! tracing_actix_web::root_span!(level = level, request) +//! } +//! +//! fn on_request_end(span: Span, outcome: &Result, Error>) { +//! DefaultRootSpanBuilder::on_request_end(span, outcome); +//! } +//! } +//! +//! let custom_middleware = TracingLogger::::new(); +//! ``` +//! //! ## The [`RootSpan`] extractor //! //! It often happens that not all information about a task is known upfront, encoded in the incoming request. @@ -244,6 +273,8 @@ pub use middleware::TracingLogger; pub use request_id::RequestId; pub use root_span::RootSpan; pub use root_span_builder::{DefaultRootSpanBuilder, RootSpanBuilder}; +// Re-exporting the `Level` enum since it's used in our `root_span!` macro +pub use tracing::Level; #[doc(hidden)] pub mod root_span_macro; diff --git a/src/root_span_builder.rs b/src/root_span_builder.rs index 74aeacd99..978519f1a 100644 --- a/src/root_span_builder.rs +++ b/src/root_span_builder.rs @@ -37,7 +37,7 @@ pub struct DefaultRootSpanBuilder; impl RootSpanBuilder for DefaultRootSpanBuilder { fn on_request_start(request: &ServiceRequest) -> Span { - root_span!(request) + root_span!(level = crate::Level::INFO, request) } fn on_request_end(span: Span, outcome: &Result, Error>) { diff --git a/src/root_span_macro.rs b/src/root_span_macro.rs index b23b73feb..d6ec7fd68 100644 --- a/src/root_span_macro.rs +++ b/src/root_span_macro.rs @@ -39,6 +39,7 @@ /// /// ```rust,should_panic /// # let request: &actix_web::dev::ServiceRequest = todo!(); +/// use tracing_actix_web::Level; /// /// // Define a `client_id` field as empty. It might be populated later. /// tracing_actix_web::root_span!(request, client_id = tracing::field::Empty); @@ -50,6 +51,9 @@ /// let app_id = "XYZ"; /// tracing_actix_web::root_span!(request, app_id); /// +/// // Use a custom level, `DEBUG`, instead of the default (`INFO`). +/// tracing_actix_web::root_span!(level = Level::DEBUG, request); +/// /// // All together /// tracing_actix_web::root_span!(request, client_id = tracing::field::Empty, name = "AppName", app_id); /// ``` @@ -58,10 +62,18 @@ macro_rules! root_span { // Vanilla root span, with no additional fields ($request:ident) => { - root_span!($request,) + $crate::root_span!($request,) + }; + // Vanilla root span, with a level but no additional fields + (level = $lvl:expr, $request:ident) => { + $crate::root_span!(level = $lvl, $request,) + }; + // One or more additional fields, comma separated, without a level + ($request:ident, $($field:tt)*) => { + $crate::root_span!(level = $crate::Level::INFO, $request, $($field)*) }; // One or more additional fields, comma separated - ($request:ident, $($field:tt)*) => { + (level = $lvl:expr, $request:ident, $($field:tt)*) => { { let user_agent = $request .headers() @@ -75,27 +87,40 @@ macro_rules! root_span { let http_method = $crate::root_span_macro::private::http_method_str($request.method()); let connection_info = $request.connection_info(); let request_id = $crate::root_span_macro::private::get_request_id($request); - let span = $crate::root_span_macro::private::tracing::info_span!( - "HTTP request", - http.method = %http_method, - http.route = %http_route, - http.flavor = %$crate::root_span_macro::private::http_flavor($request.version()), - http.scheme = %$crate::root_span_macro::private::http_scheme(connection_info.scheme()), - http.host = %connection_info.host(), - http.client_ip = %$request.connection_info().realip_remote_addr().unwrap_or(""), - http.user_agent = %user_agent, - http.target = %$request.uri().path_and_query().map(|p| p.as_str()).unwrap_or(""), - http.status_code = $crate::root_span_macro::private::tracing::field::Empty, - otel.name = %format!("HTTP {} {}", http_method, http_route), - otel.kind = "server", - otel.status_code = $crate::root_span_macro::private::tracing::field::Empty, - trace_id = $crate::root_span_macro::private::tracing::field::Empty, - request_id = %request_id, - exception.message = $crate::root_span_macro::private::tracing::field::Empty, - // Not proper OpenTelemetry, but their terminology is fairly exception-centric - exception.details = $crate::root_span_macro::private::tracing::field::Empty, - $($field)* - ); + + macro_rules! inner_span { + ($level:expr) => { + $crate::root_span_macro::private::tracing::span!( + $level, + "HTTP request", + http.method = %http_method, + http.route = %http_route, + http.flavor = %$crate::root_span_macro::private::http_flavor($request.version()), + http.scheme = %$crate::root_span_macro::private::http_scheme(connection_info.scheme()), + http.host = %connection_info.host(), + http.client_ip = %$request.connection_info().realip_remote_addr().unwrap_or(""), + http.user_agent = %user_agent, + http.target = %$request.uri().path_and_query().map(|p| p.as_str()).unwrap_or(""), + http.status_code = $crate::root_span_macro::private::tracing::field::Empty, + otel.name = %format!("HTTP {} {}", http_method, http_route), + otel.kind = "server", + otel.status_code = $crate::root_span_macro::private::tracing::field::Empty, + trace_id = $crate::root_span_macro::private::tracing::field::Empty, + request_id = %request_id, + exception.message = $crate::root_span_macro::private::tracing::field::Empty, + // Not proper OpenTelemetry, but their terminology is fairly exception-centric + exception.details = $crate::root_span_macro::private::tracing::field::Empty, + $($field)* + ) + }; + } + let span = match $lvl { + $crate::Level::TRACE => inner_span!($crate::Level::TRACE), + $crate::Level::DEBUG => inner_span!($crate::Level::DEBUG), + $crate::Level::INFO => inner_span!($crate::Level::INFO), + $crate::Level::WARN => inner_span!($crate::Level::WARN), + $crate::Level::ERROR => inner_span!($crate::Level::ERROR), + }; std::mem::drop(connection_info); // Previously, this line was instrumented with an opentelemetry-specific feature