diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 191b2b16fc..83ac15158d 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -11,8 +11,5 @@
# David contributed the Registry implementation.
/tracing-subscriber/registry @davidbarsky @hawkw @tokio-rs/tracing
-# Julian contributed the OpenTelemetry implementation.
-/tracing-opentelemetry/ @jtescher @tokio-rs/tracing
-
# Zeki contributed the TracingAppender implementation
/tracing-appender/ @zekisherif @tokio-rs/tracing
diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 52ce13bffe..a672bb28dd 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -85,13 +85,11 @@ jobs:
- tracing-macros
- tracing-serde
- tracing-tower
- - tracing-opentelemetry
- tracing
- tracing-subscriber
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
-
- name: install cargo-hack
uses: taiki-e/install-action@cargo-hack
- name: cargo hack check
@@ -123,7 +121,7 @@ jobs:
shell: bash
check-msrv:
- # Run `cargo check` on our minimum supported Rust version (1.49.0). This
+ # Run `cargo check` on our minimum supported Rust version (1.56.0). This
# checks with minimal versions; maximal versions are checked above.
name: "cargo check (+MSRV -Zminimal-versions)"
needs: check
@@ -143,27 +141,10 @@ jobs:
- tracing-serde
- tracing-subscriber
- tracing-tower
- - tracing-opentelemetry
- tracing
toolchain:
- - 1.49.0
+ - 1.56.0
- stable
- # TODO(eliza): remove this when appender is on the same MSRV.
- # same for tracing subscriber
- exclude:
- - subcrate: tracing-appender
- toolchain: 1.49.0
- - subcrate: tracing-subscriber
- toolchain: 1.49.0
- - subcrate: tracing-opentelemetry
- toolchain: 1.49.0
- include:
- - subcrate: tracing-appender
- toolchain: 1.53.0
- - subcrate: tracing-subscriber
- toolchain: 1.50.0
- - subcrate: tracing-opentelemetry
- toolchain: 1.56.0
steps:
- uses: actions/checkout@v3
- name: install Rust nightly
@@ -266,7 +247,6 @@ jobs:
- tracing-journald
- tracing-log
- tracing-macros
- - tracing-opentelemetry
- tracing-serde
- tracing-subscriber
- tracing-tower
diff --git a/Cargo.toml b/Cargo.toml
index 35d6147776..d272eef50a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,7 +11,6 @@ members = [
"tracing-log",
"tracing-macros",
"tracing-mock",
- "tracing-opentelemetry",
"tracing-subscriber",
"tracing-serde",
"tracing-appender",
diff --git a/README.md b/README.md
index 246c9f964b..7fb11378ff 100644
--- a/README.md
+++ b/README.md
@@ -254,14 +254,14 @@ attachment that `Future::instrument` does.
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.49. The current Tracing version is not guaranteed to build on Rust
+version is 1.56. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
project. The current stable Rust compiler and the three most recent minor
versions before it will always be supported. For example, if the current stable
-compiler version is 1.45, the minimum supported version will not be increased
-past 1.42, three minor versions prior. Increasing the minimum supported compiler
+compiler version is 1.69, the minimum supported version will not be increased
+past 1.66, three minor versions prior. Increasing the minimum supported compiler
version is not considered a semver breaking change as long as doing so complies
with this policy.
@@ -307,11 +307,6 @@ The crates included as part of Tracing are:
* [`tracing-log`]: Compatibility with the `log` crate (unstable).
-* [`tracing-opentelemetry`]: Provides a layer that connects spans from multiple
- systems into a trace and emits them to [OpenTelemetry]-compatible distributed
- tracing systems for processing and visualization.
- ([crates.io][otel-crates]|[docs][otel-docs])
-
* [`tracing-serde`]: A compatibility layer for serializing trace data with
`serde` (unstable).
@@ -339,7 +334,6 @@ The crates included as part of Tracing are:
[`tracing-macros`]: tracing-macros
[`tracing-attributes`]: tracing-attributes
[`tracing-log`]: tracing-log
-[`tracing-opentelemetry`]: tracing-opentelemetry
[`tracing-serde`]: tracing-serde
[`tracing-subscriber`]: tracing-subscriber
[`tracing-tower`]: tracing-tower
diff --git a/assets/warning.css b/assets/warning-css.html
similarity index 84%
rename from assets/warning.css
rename to assets/warning-css.html
index 50a79b4900..5d92b1bc9d 100644
--- a/assets/warning.css
+++ b/assets/warning-css.html
@@ -1,4 +1,5 @@
-#tracing-warning-header {
+
diff --git a/examples/Cargo.toml b/examples/Cargo.toml
index 4d33691a3a..b33c85f1c4 100644
--- a/examples/Cargo.toml
+++ b/examples/Cargo.toml
@@ -3,7 +3,7 @@ name = "tracing-examples"
version = "0.0.0"
publish = false
edition = "2018"
-rust-version = "1.49.0"
+rust-version = "1.56.0"
[features]
default = []
@@ -21,7 +21,6 @@ tracing-futures = { version = "0.3", path = "../tracing-futures", features = ["f
tracing-attributes = { path = "../tracing-attributes", version = "0.2"}
tracing-log = { path = "../tracing-log", version = "0.2", features = ["env_logger"] }
tracing-serde = { path = "../tracing-serde" }
-tracing-opentelemetry = { path = "../tracing-opentelemetry" }
tracing-appender = { path = "../tracing-appender" }
tracing-journald = { path = "../tracing-journald" }
@@ -51,10 +50,6 @@ log = "0.4.17"
inferno = "0.11.6"
tempfile = "3.3.0"
-# opentelemetry example
-opentelemetry = { version = "0.18.0", default-features = false, features = ["trace"] }
-opentelemetry-jaeger = "0.17.0"
-
# fmt examples
snafu = "0.6.10"
thiserror = "1.0.31"
diff --git a/examples/README.md b/examples/README.md
index 30f2ac7f87..c589edbbf7 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -65,12 +65,6 @@ This directory contains a collection of examples that demonstrate the use of the
unstructured logs from dependencies as `tracing` events, by instrumenting
[this example][echo] from `hyper`, and using `tracing-log` to record logs
emitted by `hyper`.
-- **tracing-opentelemetry**:
- + `opentelemetry`: Demonstrates how `tracing-opentelemetry` can be used to
- export and visualize `tracing` span data.
- + `opentelemetry-remote-context`: Demonstrates how `tracing-opentelemetry`
- can be used to extract and inject remote context when traces span multiple
- systems.
[tasks]: (https://docs.rs/tokio/0.2.21/tokio/task/index.html)
[tokio-proxy]: https://github.com/tokio-rs/tokio/blob/v0.1.x/tokio/examples/proxy.rs
diff --git a/examples/examples/opentelemetry-remote-context.rs b/examples/examples/opentelemetry-remote-context.rs
deleted file mode 100644
index d882ee9cbb..0000000000
--- a/examples/examples/opentelemetry-remote-context.rs
+++ /dev/null
@@ -1,46 +0,0 @@
-use opentelemetry::sdk::propagation::TraceContextPropagator;
-use opentelemetry::{global, Context};
-use std::collections::HashMap;
-use tracing::span;
-use tracing_opentelemetry::OpenTelemetrySpanExt;
-use tracing_subscriber::subscribe::CollectExt;
-use tracing_subscriber::Registry;
-
-fn make_request(_cx: Context) {
- // perform external request after injecting context
- // e.g. if there are request headers that impl `opentelemetry::propagation::Injector`
- // then `propagator.inject_context(cx, request.headers_mut())`
-}
-
-fn build_example_carrier() -> HashMap {
- let mut carrier = HashMap::new();
- carrier.insert(
- "traceparent".to_string(),
- "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01".to_string(),
- );
-
- carrier
-}
-
-fn main() {
- // Set a format for propagating context. This MUST be provided, as the default is a no-op.
- global::set_text_map_propagator(TraceContextPropagator::new());
- let subscriber = Registry::default().with(tracing_opentelemetry::subscriber());
-
- tracing::collect::with_default(subscriber, || {
- // Extract context from request headers
- let parent_context = global::get_text_map_propagator(|propagator| {
- propagator.extract(&build_example_carrier())
- });
-
- // Generate tracing span as usual
- let app_root = span!(tracing::Level::INFO, "app_start");
-
- // Assign parent trace from external context
- app_root.set_parent(parent_context);
-
- // To include tracing context in client requests from _this_ app,
- // use `context` to extract the current OpenTelemetry context.
- make_request(app_root.context());
- });
-}
diff --git a/examples/examples/opentelemetry.rs b/examples/examples/opentelemetry.rs
deleted file mode 100644
index 12e96d3f15..0000000000
--- a/examples/examples/opentelemetry.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-use opentelemetry::global;
-use std::{error::Error, thread, time::Duration};
-use tracing::{span, trace, warn};
-use tracing_attributes::instrument;
-use tracing_subscriber::prelude::*;
-
-#[instrument]
-#[inline]
-fn expensive_work() -> &'static str {
- span!(tracing::Level::INFO, "expensive_step_1")
- .in_scope(|| thread::sleep(Duration::from_millis(25)));
- span!(tracing::Level::INFO, "expensive_step_2")
- .in_scope(|| thread::sleep(Duration::from_millis(25)));
-
- "success"
-}
-
-fn main() -> Result<(), Box> {
- // Install an otel pipeline with a simple span processor that exports data one at a time when
- // spans end. See the `install_batch` option on each exporter's pipeline builder to see how to
- // export in batches.
- let tracer = opentelemetry_jaeger::new_agent_pipeline()
- .with_service_name("report_example")
- .install_simple()?;
- let opentelemetry = tracing_opentelemetry::subscriber().with_tracer(tracer);
- tracing_subscriber::registry()
- .with(opentelemetry)
- .try_init()?;
-
- {
- let root = span!(tracing::Level::INFO, "app_start", work_units = 2);
- let _enter = root.enter();
-
- let work_result = expensive_work();
-
- span!(tracing::Level::INFO, "faster_work")
- .in_scope(|| thread::sleep(Duration::from_millis(10)));
-
- warn!("About to exit!");
- trace!("status: {}", work_result);
- } // Once this scope is closed, all spans inside are closed as well
-
- // Shut down the current tracer provider. This will invoke the shutdown
- // method on all span processors. span processors should export remaining
- // spans before return.
- global::shutdown_tracer_provider();
-
- Ok(())
-}
diff --git a/examples/examples/panic_hook.rs b/examples/examples/panic_hook.rs
index 1c86f6d060..5bfd77355c 100644
--- a/examples/examples/panic_hook.rs
+++ b/examples/examples/panic_hook.rs
@@ -30,7 +30,7 @@ fn main() {
// On nightly Rust, where the `PanicInfo` type also exposes a
// `message()` method returning just the message, we could record
// just the message instead of the entire `fmt::Display`
- // implementation, avoiding the duplciated location
+ // implementation, avoiding the duplicated location
tracing::error!(
message = %panic,
panic.file = location.file(),
diff --git a/netlify.toml b/netlify.toml
index e82b88878f..a59c675e27 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -12,7 +12,7 @@
--cfg docsrs \
--html-before-content /opt/build/repo/assets/warning.html \
--html-in-header /opt/build/repo/assets/noindex.html \
- --extend-css /opt/build/repo/assets/warning.css \
+ --html-in-header /opt/build/repo/assets/warning-css.html \
"""
[[redirects]]
diff --git a/tracing-appender/README.md b/tracing-appender/README.md
index 121b92d701..54161684cb 100644
--- a/tracing-appender/README.md
+++ b/tracing-appender/README.md
@@ -152,8 +152,8 @@ Rust versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
project. The current stable Rust compiler and the three most recent minor
versions before it will always be supported. For example, if the current
-stable compiler version is 1.45, the minimum supported version will not be
-increased past 1.42, three minor versions prior. Increasing the minimum
+stable compiler version is 1.69, the minimum supported version will not be
+increased past 1.66, three minor versions prior. Increasing the minimum
supported compiler version is not considered a semver breaking change as
long as doing so complies with this policy.
diff --git a/tracing-appender/src/lib.rs b/tracing-appender/src/lib.rs
index ff336194ef..97ad8d4d58 100644
--- a/tracing-appender/src/lib.rs
+++ b/tracing-appender/src/lib.rs
@@ -116,8 +116,8 @@
//! Tracing follows the same compiler support policies as the rest of the Tokio
//! project. The current stable Rust compiler and the three most recent minor
//! versions before it will always be supported. For example, if the current
-//! stable compiler version is 1.45, the minimum supported version will not be
-//! increased past 1.42, three minor versions prior. Increasing the minimum
+//! stable compiler version is 1.69, the minimum supported version will not be
+//! increased past 1.66, three minor versions prior. Increasing the minimum
//! supported compiler version is not considered a semver breaking change as
//! long as doing so complies with this policy.
//!
diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml
index 4ef87a7c5f..ae20aa23da 100644
--- a/tracing-attributes/Cargo.toml
+++ b/tracing-attributes/Cargo.toml
@@ -28,14 +28,14 @@ keywords = ["logging", "tracing", "macro", "instrument", "log"]
license = "MIT"
readme = "README.md"
edition = "2018"
-rust-version = "1.49.0"
+rust-version = "1.56.0"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1.0.40"
-syn = { version = "1.0.98", default-features = false, features = ["full", "parsing", "printing", "visit", "visit-mut", "clone-impls", "extra-traits", "proc-macro"] }
+syn = { version = "2.0", default-features = false, features = ["full", "parsing", "printing", "visit-mut", "clone-impls", "extra-traits", "proc-macro"] }
quote = "1.0.20"
[dev-dependencies]
@@ -43,7 +43,7 @@ tracing = { path = "../tracing", version = "0.2" }
tracing-mock = { path = "../tracing-mock", features = ["tokio-test"] }
tokio-test = "0.4.2"
tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", features = ["env-filter"] }
-async-trait = "0.1.56"
+async-trait = "0.1.67"
trybuild = "1.0.64"
rustversion = "1.0.9"
diff --git a/tracing-attributes/README.md b/tracing-attributes/README.md
index efb8b50ebd..976aafe305 100644
--- a/tracing-attributes/README.md
+++ b/tracing-attributes/README.md
@@ -37,7 +37,7 @@ structured, event-based diagnostic information. This crate provides the
Note that this macro is also re-exported by the main `tracing` crate.
-*Compiler support: [requires `rustc` 1.49+][msrv]*
+*Compiler support: [requires `rustc` 1.56+][msrv]*
[msrv]: #supported-rust-versions
@@ -69,14 +69,14 @@ pub fn my_function(my_arg: usize) {
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.49. The current Tracing version is not guaranteed to build on Rust
+version is 1.56. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
project. The current stable Rust compiler and the three most recent minor
versions before it will always be supported. For example, if the current stable
-compiler version is 1.45, the minimum supported version will not be increased
-past 1.42, three minor versions prior. Increasing the minimum supported compiler
+compiler version is 1.69, the minimum supported version will not be increased
+past 1.66, three minor versions prior. Increasing the minimum supported compiler
version is not considered a semver breaking change as long as doing so complies
with this policy.
diff --git a/tracing-attributes/src/attr.rs b/tracing-attributes/src/attr.rs
index 8195a5ee02..e22ac629e1 100644
--- a/tracing-attributes/src/attr.rs
+++ b/tracing-attributes/src/attr.rs
@@ -240,7 +240,7 @@ impl Parse for Skips {
let _ = input.parse::();
let content;
let _ = syn::parenthesized!(content in input);
- let names: Punctuated = content.parse_terminated(Ident::parse_any)?;
+ let names = content.parse_terminated(Ident::parse_any, Token![,])?;
let mut skips = HashSet::new();
for name in names {
if skips.contains(&name) {
@@ -292,7 +292,7 @@ impl Parse for Fields {
let _ = input.parse::();
let content;
let _ = syn::parenthesized!(content in input);
- let fields: Punctuated<_, Token![,]> = content.parse_terminated(Field::parse)?;
+ let fields = content.parse_terminated(Field::parse, Token![,])?;
Ok(Self(fields))
}
}
@@ -337,7 +337,7 @@ impl ToTokens for Field {
let name = &self.name;
let kind = &self.kind;
tokens.extend(quote! {
- #name = #kind#value
+ #name = #kind #value
})
} else if self.kind == FieldKind::Value {
// XXX(eliza): I don't like that fields without values produce
diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs
index aaca842c0f..3e5b818004 100644
--- a/tracing-attributes/src/expand.rs
+++ b/tracing-attributes/src/expand.rs
@@ -483,10 +483,7 @@ fn param_names(pat: Pat, record_type: RecordType) -> Box Box::new(
+ Pat::TupleStruct(PatTupleStruct { elems, .. }) => Box::new(
elems
.into_iter()
.flat_map(|p| param_names(p, RecordType::Debug)),
@@ -570,7 +567,7 @@ impl<'block> AsyncInfo<'block> {
// last expression of the block: it determines the return value of the
// block, this is quite likely a `Box::pin` statement or an async block
let (last_expr_stmt, last_expr) = block.stmts.iter().rev().find_map(|stmt| {
- if let Stmt::Expr(expr) = stmt {
+ if let Stmt::Expr(expr, _semi) = stmt {
Some((stmt, expr))
} else {
None
diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs
index edbfd6cc29..bf0b0e566d 100644
--- a/tracing-attributes/src/lib.rs
+++ b/tracing-attributes/src/lib.rs
@@ -6,7 +6,7 @@
//!
//! Note that this macro is also re-exported by the main `tracing` crate.
//!
-//! *Compiler support: [requires `rustc` 1.49+][msrv]*
+//! *Compiler support: [requires `rustc` 1.56+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//!
@@ -41,14 +41,14 @@
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.49. The current Tracing version is not guaranteed to build on
+//! version is 1.56. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
//! project. The current stable Rust compiler and the three most recent minor
//! versions before it will always be supported. For example, if the current
-//! stable compiler version is 1.45, the minimum supported version will not be
-//! increased past 1.42, three minor versions prior. Increasing the minimum
+//! stable compiler version is 1.69, the minimum supported version will not be
+//! increased past 1.66, three minor versions prior. Increasing the minimum
//! supported compiler version is not considered a semver breaking change as
//! long as doing so complies with this policy.
//!
@@ -79,7 +79,7 @@
)]
use proc_macro2::TokenStream;
-use quote::ToTokens;
+use quote::{quote, ToTokens};
use syn::parse::{Parse, ParseStream};
use syn::{Attribute, ItemFn, Signature, Visibility};
@@ -281,6 +281,8 @@ mod expand;
/// }
/// ```
///
+/// The level of the error value event defaults to `ERROR`.
+///
/// Similarly, overriding the level of the `err` event :
///
/// ```
@@ -365,6 +367,14 @@ mod expand;
/// }
/// ```
///
+/// `const fn` cannot be instrumented, and will result in a compilation failure:
+///
+/// ```compile_fail
+/// # use tracing_attributes::instrument;
+/// #[instrument]
+/// const fn my_const_function() {}
+/// ```
+///
/// [span]: https://docs.rs/tracing/latest/tracing/span/index.html
/// [`follows_from`]: https://docs.rs/tracing/latest/tracing/struct.Span.html#method.follows_from
/// [`tracing`]: https://github.com/tokio-rs/tracing
@@ -408,6 +418,13 @@ fn instrument_precise(
let input = syn::parse::(item)?;
let instrumented_function_name = input.sig.ident.to_string();
+ if input.sig.constness.is_some() {
+ return Ok(quote! {
+ compile_error!("the `#[instrument]` attribute may not be used with `const fn`s")
+ }
+ .into());
+ }
+
// check for async_trait-like patterns in the block, and instrument
// the future instead of the wrapper
if let Some(async_like) = expand::AsyncInfo::from_fn(&input) {
diff --git a/tracing-attributes/tests/async_fn.rs b/tracing-attributes/tests/async_fn.rs
index 38675a451a..29481c4e7c 100644
--- a/tracing-attributes/tests/async_fn.rs
+++ b/tracing-attributes/tests/async_fn.rs
@@ -90,6 +90,8 @@ fn async_fn_only_enters_for_polls() {
.exit(expect::span().named("test_async_fn"))
.enter(expect::span().named("test_async_fn"))
.exit(expect::span().named("test_async_fn"))
+ .enter(expect::span().named("test_async_fn"))
+ .exit(expect::span().named("test_async_fn"))
.drop_span(expect::span().named("test_async_fn"))
.only()
.run_with_handle();
@@ -120,8 +122,12 @@ fn async_fn_nested() {
.enter(span2.clone())
.event(expect::event().with_fields(expect::field("nested").with_value(&true)))
.exit(span2.clone())
+ .enter(span2.clone())
+ .exit(span2.clone())
.drop_span(span2)
.exit(span.clone())
+ .enter(span.clone())
+ .exit(span.clone())
.drop_span(span)
.only()
.run_with_handle();
@@ -199,13 +205,19 @@ fn async_fn_with_async_trait() {
.enter(span3.clone())
.event(expect::event().with_fields(expect::field("val").with_value(&2u64)))
.exit(span3.clone())
+ .enter(span3.clone())
+ .exit(span3.clone())
.drop_span(span3)
.new_span(span2.clone().with_field(expect::field("self")))
.enter(span2.clone())
.event(expect::event().with_fields(expect::field("val").with_value(&5u64)))
.exit(span2.clone())
+ .enter(span2.clone())
+ .exit(span2.clone())
.drop_span(span2)
.exit(span.clone())
+ .enter(span.clone())
+ .exit(span.clone())
.drop_span(span)
.only()
.run_with_handle();
@@ -256,6 +268,8 @@ fn async_fn_with_async_trait_and_fields_expressions() {
)
.enter(span.clone())
.exit(span.clone())
+ .enter(span.clone())
+ .exit(span.clone())
.drop_span(span)
.only()
.run_with_handle();
@@ -331,8 +345,12 @@ fn async_fn_with_async_trait_and_fields_expressions_with_generic_parameter() {
.with_field(expect::field("Self").with_value(&std::any::type_name::())),
)
.enter(span4.clone())
+ .exit(span4.clone())
+ .enter(span4.clone())
.exit(span4)
.exit(span2.clone())
+ .enter(span2.clone())
+ .exit(span2.clone())
.drop_span(span2)
.new_span(
span3
@@ -341,6 +359,8 @@ fn async_fn_with_async_trait_and_fields_expressions_with_generic_parameter() {
)
.enter(span3.clone())
.exit(span3.clone())
+ .enter(span3.clone())
+ .exit(span3.clone())
.drop_span(span3)
.only()
.run_with_handle();
@@ -382,6 +402,8 @@ fn out_of_scope_fields() {
.new_span(span.clone())
.enter(span.clone())
.exit(span.clone())
+ .enter(span.clone())
+ .exit(span.clone())
.drop_span(span)
.only()
.run_with_handle();
@@ -417,6 +439,8 @@ fn manual_impl_future() {
.enter(span.clone())
.event(poll_event())
.exit(span.clone())
+ .enter(span.clone())
+ .exit(span.clone())
.drop_span(span)
.only()
.run_with_handle();
@@ -448,6 +472,8 @@ fn manual_box_pin() {
.enter(span.clone())
.event(poll_event())
.exit(span.clone())
+ .enter(span.clone())
+ .exit(span.clone())
.drop_span(span)
.only()
.run_with_handle();
diff --git a/tracing-attributes/tests/err.rs b/tracing-attributes/tests/err.rs
index 11e1266093..ee19301773 100644
--- a/tracing-attributes/tests/err.rs
+++ b/tracing-attributes/tests/err.rs
@@ -79,6 +79,8 @@ fn test_async() {
.enter(span.clone())
.event(expect::event().at_level(Level::ERROR))
.exit(span.clone())
+ .enter(span.clone())
+ .exit(span.clone())
.drop_span(span)
.only()
.run_with_handle();
@@ -133,6 +135,8 @@ fn test_mut_async() {
.enter(span.clone())
.event(expect::event().at_level(Level::ERROR))
.exit(span.clone())
+ .enter(span.clone())
+ .exit(span.clone())
.drop_span(span)
.only()
.run_with_handle();
diff --git a/tracing-attributes/tests/follows_from.rs b/tracing-attributes/tests/follows_from.rs
index 266f7b59a3..acfec81f4b 100644
--- a/tracing-attributes/tests/follows_from.rs
+++ b/tracing-attributes/tests/follows_from.rs
@@ -58,6 +58,8 @@ fn follows_from_async_test() {
.follows_from(consequence.clone(), cause_b)
.follows_from(consequence.clone(), cause_c)
.enter(consequence.clone())
+ .exit(consequence.clone())
+ .enter(consequence.clone())
.exit(consequence)
.only()
.run_with_handle();
diff --git a/tracing-attributes/tests/ret.rs b/tracing-attributes/tests/ret.rs
index c58ac27887..586212add0 100644
--- a/tracing-attributes/tests/ret.rs
+++ b/tracing-attributes/tests/ret.rs
@@ -138,6 +138,8 @@ fn test_async() {
.at_level(Level::INFO),
)
.exit(span.clone())
+ .enter(span.clone())
+ .exit(span.clone())
.drop_span(span)
.only()
.run_with_handle();
diff --git a/tracing-attributes/tests/ui.rs b/tracing-attributes/tests/ui.rs
index f11cc019eb..73d7fdcef8 100644
--- a/tracing-attributes/tests/ui.rs
+++ b/tracing-attributes/tests/ui.rs
@@ -5,3 +5,10 @@ fn async_instrument() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/ui/async_instrument.rs");
}
+
+#[rustversion::stable]
+#[test]
+fn const_instrument() {
+ let t = trybuild::TestCases::new();
+ t.compile_fail("tests/ui/const_instrument.rs");
+}
diff --git a/tracing-attributes/tests/ui/async_instrument.stderr b/tracing-attributes/tests/ui/async_instrument.stderr
index db6f6b4343..2c64b0c15e 100644
--- a/tracing-attributes/tests/ui/async_instrument.stderr
+++ b/tracing-attributes/tests/ui/async_instrument.stderr
@@ -16,7 +16,7 @@ error[E0308]: mismatched types
10 | ""
| ^^- help: try using a conversion method: `.to_string()`
| |
- | expected struct `String`, found `&str`
+ | expected `String`, found `&str`
|
note: return type inferred to be `String` here
--> tests/ui/async_instrument.rs:9:31
@@ -47,7 +47,7 @@ error[E0308]: mismatched types
--> tests/ui/async_instrument.rs:23:5
|
23 | ""
- | ^^ expected struct `Wrapper`, found `&str`
+ | ^^ expected `Wrapper<_>`, found `&str`
|
= note: expected struct `Wrapper<_>`
found reference `&'static str`
@@ -79,7 +79,7 @@ error[E0308]: mismatched types
36 | return "";
| ^^- help: try using a conversion method: `.to_string()`
| |
- | expected struct `String`, found `&str`
+ | expected `String`, found `&str`
|
note: return type inferred to be `String` here
--> tests/ui/async_instrument.rs:34:28
@@ -93,6 +93,6 @@ error[E0308]: mismatched types
42 | async fn extra_semicolon() -> i32 {
| ___________________________________^
43 | | 1;
- | | - help: remove this semicolon
+ | | - help: remove this semicolon to return this value
44 | | }
| |_^ expected `i32`, found `()`
diff --git a/tracing-attributes/tests/ui/const_instrument.rs b/tracing-attributes/tests/ui/const_instrument.rs
new file mode 100644
index 0000000000..a251e8f66b
--- /dev/null
+++ b/tracing-attributes/tests/ui/const_instrument.rs
@@ -0,0 +1,8 @@
+#![allow(unreachable_code)]
+
+#[tracing::instrument]
+const fn unit() {
+ ""
+}
+
+fn main() {}
diff --git a/tracing-attributes/tests/ui/const_instrument.stderr b/tracing-attributes/tests/ui/const_instrument.stderr
new file mode 100644
index 0000000000..e76d4acad9
--- /dev/null
+++ b/tracing-attributes/tests/ui/const_instrument.stderr
@@ -0,0 +1,15 @@
+error: macros that expand to items must be delimited with braces or followed by a semicolon
+ --> tests/ui/const_instrument.rs:3:1
+ |
+3 | #[tracing::instrument]
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in the attribute macro `tracing::instrument` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: the `#[instrument]` attribute may not be used with `const fn`s
+ --> tests/ui/const_instrument.rs:3:1
+ |
+3 | #[tracing::instrument]
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in the attribute macro `tracing::instrument` (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tracing-core/Cargo.toml b/tracing-core/Cargo.toml
index ecf5114d2e..363d8fad53 100644
--- a/tracing-core/Cargo.toml
+++ b/tracing-core/Cargo.toml
@@ -24,7 +24,7 @@ categories = [
]
keywords = ["logging", "tracing", "profiling"]
edition = "2018"
-rust-version = "1.49.0"
+rust-version = "1.56.0"
[features]
default = ["std"]
diff --git a/tracing-core/README.md b/tracing-core/README.md
index 189d4f66f6..24f0fb1783 100644
--- a/tracing-core/README.md
+++ b/tracing-core/README.md
@@ -53,7 +53,7 @@ The crate provides:
In addition, it defines the global callsite registry and per-thread current
dispatcher which other components of the tracing system rely on.
-*Compiler support: [requires `rustc` 1.49+][msrv]*
+*Compiler support: [requires `rustc` 1.56+][msrv]*
[msrv]: #supported-rust-versions
@@ -99,14 +99,14 @@ The following crate feature flags are available:
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.49. The current Tracing version is not guaranteed to build on Rust
+version is 1.56. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
project. The current stable Rust compiler and the three most recent minor
versions before it will always be supported. For example, if the current stable
-compiler version is 1.45, the minimum supported version will not be increased
-past 1.42, three minor versions prior. Increasing the minimum supported compiler
+compiler version is 1.69, the minimum supported version will not be increased
+past 1.69, three minor versions prior. Increasing the minimum supported compiler
version is not considered a semver breaking change as long as doing so complies
with this policy.
diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs
index 12697fd402..768107b042 100644
--- a/tracing-core/src/field.rs
+++ b/tracing-core/src/field.rs
@@ -697,6 +697,7 @@ impl FieldSet {
///
/// [`Identifier`]: super::callsite::Identifier
/// [`Callsite`]: super::callsite::Callsite
+ #[inline]
pub(crate) fn callsite(&self) -> callsite::Identifier {
callsite::Identifier(self.callsite.0)
}
@@ -734,6 +735,7 @@ impl FieldSet {
}
/// Returns an iterator over the `Field`s in this `FieldSet`.
+ #[inline]
pub fn iter(&self) -> Iter {
let idxs = 0..self.len();
Iter {
@@ -837,6 +839,7 @@ impl PartialEq for FieldSet {
impl Iterator for Iter {
type Item = Field;
+ #[inline]
fn next(&mut self) -> Option {
let i = self.idxs.next()?;
Some(Field {
diff --git a/tracing-core/src/lib.rs b/tracing-core/src/lib.rs
index 34e290eecd..e31978368d 100644
--- a/tracing-core/src/lib.rs
+++ b/tracing-core/src/lib.rs
@@ -23,7 +23,7 @@
//! In addition, it defines the global callsite registry and per-thread current
//! dispatcher which other components of the tracing system rely on.
//!
-//! *Compiler support: [requires `rustc` 1.49+][msrv]*
+//! *Compiler support: [requires `rustc` 1.56+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//!
@@ -109,14 +109,14 @@
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.49. The current Tracing version is not guaranteed to build on
+//! version is 1.56. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
//! project. The current stable Rust compiler and the three most recent minor
//! versions before it will always be supported. For example, if the current
-//! stable compiler version is 1.45, the minimum supported version will not be
-//! increased past 1.42, three minor versions prior. Increasing the minimum
+//! stable compiler version is 1.69, the minimum supported version will not be
+//! increased past 1.66, three minor versions prior. Increasing the minimum
//! supported compiler version is not considered a semver breaking change as
//! long as doing so complies with this policy.
//!
diff --git a/tracing-core/src/metadata.rs b/tracing-core/src/metadata.rs
index 73fdc03a22..7487f9d403 100644
--- a/tracing-core/src/metadata.rs
+++ b/tracing-core/src/metadata.rs
@@ -274,6 +274,7 @@ impl<'a> Metadata<'a> {
}
/// Returns the names of the fields on the described span or event.
+ #[inline]
pub fn fields(&self) -> &field::FieldSet {
&self.fields
}
diff --git a/tracing-error/Cargo.toml b/tracing-error/Cargo.toml
index 190f791bb6..7db6fb8837 100644
--- a/tracing-error/Cargo.toml
+++ b/tracing-error/Cargo.toml
@@ -32,7 +32,7 @@ keywords = [
"backtrace"
]
edition = "2018"
-rust-version = "1.49.0"
+rust-version = "1.56.0"
[features]
default = ["traced-error"]
diff --git a/tracing-error/README.md b/tracing-error/README.md
index 7a625ca296..ae2721233f 100644
--- a/tracing-error/README.md
+++ b/tracing-error/README.md
@@ -48,7 +48,7 @@ The crate provides the following:
**Note**: This crate is currently experimental.
-*Compiler support: [requires `rustc` 1.49+][msrv]*
+*Compiler support: [requires `rustc` 1.56+][msrv]*
[msrv]: #supported-rust-versions
@@ -186,14 +186,14 @@ fn main() {
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.49. The current Tracing version is not guaranteed to build on Rust
+version is 1.56. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
project. The current stable Rust compiler and the three most recent minor
versions before it will always be supported. For example, if the current stable
-compiler version is 1.45, the minimum supported version will not be increased
-past 1.42, three minor versions prior. Increasing the minimum supported compiler
+compiler version is 1.69, the minimum supported version will not be increased
+past 1.66, three minor versions prior. Increasing the minimum supported compiler
version is not considered a semver breaking change as long as doing so complies
with this policy.
diff --git a/tracing-error/src/lib.rs b/tracing-error/src/lib.rs
index 386b6a0e36..c6dba25fe3 100644
--- a/tracing-error/src/lib.rs
+++ b/tracing-error/src/lib.rs
@@ -18,7 +18,7 @@
//!
//! **Note**: This crate is currently experimental.
//!
-//! *Compiler support: [requires `rustc` 1.49+][msrv]*
+//! *Compiler support: [requires `rustc` 1.56+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//!
@@ -166,14 +166,14 @@
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.49. The current Tracing version is not guaranteed to build on
+//! version is 1.56. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
//! project. The current stable Rust compiler and the three most recent minor
//! versions before it will always be supported. For example, if the current
-//! stable compiler version is 1.45, the minimum supported version will not be
-//! increased past 1.42, three minor versions prior. Increasing the minimum
+//! stable compiler version is 1.69, the minimum supported version will not be
+//! increased past 1.66, three minor versions prior. Increasing the minimum
//! supported compiler version is not considered a semver breaking change as
//! long as doing so complies with this policy.
//!
diff --git a/tracing-error/src/subscriber.rs b/tracing-error/src/subscriber.rs
index f6840acc07..fa9ed679b1 100644
--- a/tracing-error/src/subscriber.rs
+++ b/tracing-error/src/subscriber.rs
@@ -111,9 +111,9 @@ where
}
impl WithContext {
- pub(crate) fn with_context<'a>(
+ pub(crate) fn with_context(
&self,
- dispatch: &'a Dispatch,
+ dispatch: &Dispatch,
id: &span::Id,
mut f: impl FnMut(&'static Metadata<'static>, &str) -> bool,
) {
diff --git a/tracing-flame/Cargo.toml b/tracing-flame/Cargo.toml
index 351aed7d61..2ea6d0b807 100644
--- a/tracing-flame/Cargo.toml
+++ b/tracing-flame/Cargo.toml
@@ -19,7 +19,7 @@ categories = [
"asynchronous",
]
keywords = ["tracing", "subscriber", "flamegraph", "profiling"]
-rust-version = "1.49.0"
+rust-version = "1.56.0"
[features]
default = ["smallvec"]
diff --git a/tracing-flame/README.md b/tracing-flame/README.md
index 46340ed541..5a6b9017af 100644
--- a/tracing-flame/README.md
+++ b/tracing-flame/README.md
@@ -26,7 +26,7 @@ flamegraph/flamechart. Flamegraphs/flamecharts are useful for identifying perfor
bottlenecks in an application. For more details, see Brendan Gregg's [post]
on flamegraphs.
-*Compiler support: [requires `rustc` 1.49+][msrv]*
+*Compiler support: [requires `rustc` 1.56+][msrv]*
[msrv]: #supported-rust-versions
[post]: http://www.brendangregg.com/flamegraphs.html
@@ -106,14 +106,14 @@ _flamechart_, which _does not_ sort or collapse identical stack frames.
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.49. The current Tracing version is not guaranteed to build on Rust
+version is 1.56. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
project. The current stable Rust compiler and the three most recent minor
versions before it will always be supported. For example, if the current stable
-compiler version is 1.45, the minimum supported version will not be increased
-past 1.42, three minor versions prior. Increasing the minimum supported compiler
+compiler version is 1.69, the minimum supported version will not be increased
+past 1.66, three minor versions prior. Increasing the minimum supported compiler
version is not considered a semver breaking change as long as doing so complies
with this policy.
diff --git a/tracing-flame/src/lib.rs b/tracing-flame/src/lib.rs
index dea0278b11..b123d80b8a 100644
--- a/tracing-flame/src/lib.rs
+++ b/tracing-flame/src/lib.rs
@@ -10,7 +10,7 @@
//! issues bottlenecks in an application. For more details, see Brendan Gregg's [post]
//! on flamegraphs.
//!
-//! *Compiler support: [requires `rustc` 1.49+][msrv]*
+//! *Compiler support: [requires `rustc` 1.56+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//! [post]: http://www.brendangregg.com/flamegraphs.html
@@ -95,14 +95,14 @@
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.49. The current Tracing version is not guaranteed to build on
+//! version is 1.56. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
//! project. The current stable Rust compiler and the three most recent minor
//! versions before it will always be supported. For example, if the current
-//! stable compiler version is 1.45, the minimum supported version will not be
-//! increased past 1.42, three minor versions prior. Increasing the minimum
+//! stable compiler version is 1.69, the minimum supported version will not be
+//! increased past 1.66, three minor versions prior. Increasing the minimum
//! supported compiler version is not considered a semver breaking change as
//! long as doing so complies with this policy.
//!
diff --git a/tracing-futures/Cargo.toml b/tracing-futures/Cargo.toml
index 3bda4cb601..f3b4dc1d61 100644
--- a/tracing-futures/Cargo.toml
+++ b/tracing-futures/Cargo.toml
@@ -16,7 +16,7 @@ categories = [
]
keywords = ["logging", "profiling", "tracing", "futures", "async"]
license = "MIT"
-rust-version = "1.49.0"
+rust-version = "1.56.0"
[features]
default = ["std-future", "std"]
@@ -40,6 +40,7 @@ tokio-threadpool = "0.1.18"
mio = "0.6.23"
[dev-dependencies]
+futures = "0.3.21"
tokio-test = "0.4.2"
tracing-core = { path = "../tracing-core", version = "0.2" }
tracing-mock = { path = "../tracing-mock", features = ["tokio-test"] }
diff --git a/tracing-futures/README.md b/tracing-futures/README.md
index c27248cfa3..a7d3331a76 100644
--- a/tracing-futures/README.md
+++ b/tracing-futures/README.md
@@ -51,21 +51,21 @@ The crate provides the following traits:
[`Subscriber`]: https://docs.rs/tracing/latest/tracing/subscriber/index.html
[`tracing`]: https://crates.io/crates/tracing
-*Compiler support: [requires `rustc` 1.49+][msrv]*
+*Compiler support: [requires `rustc` 1.56+][msrv]*
[msrv]: #supported-rust-versions
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.49. The current Tracing version is not guaranteed to build on Rust
+version is 1.56. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
project. The current stable Rust compiler and the three most recent minor
versions before it will always be supported. For example, if the current stable
-compiler version is 1.45, the minimum supported version will not be increased
-past 1.42, three minor versions prior. Increasing the minimum supported compiler
+compiler version is 1.69, the minimum supported version will not be increased
+past 1.66, three minor versions prior. Increasing the minimum supported compiler
version is not considered a semver breaking change as long as doing so complies
with this policy.
diff --git a/tracing-futures/src/executor/futures_01.rs b/tracing-futures/src/executor/futures_01.rs
index 56ba6e3c42..7d4b674af8 100644
--- a/tracing-futures/src/executor/futures_01.rs
+++ b/tracing-futures/src/executor/futures_01.rs
@@ -4,16 +4,6 @@ use futures_01::{
Future,
};
-macro_rules! deinstrument_err {
- ($e:expr) => {
- $e.map_err(|e| {
- let kind = e.kind();
- let future = e.into_future().inner;
- ExecuteError::new(kind, future)
- })
- };
-}
-
impl Executor for Instrumented
where
T: Executor>,
@@ -21,7 +11,11 @@ where
{
fn execute(&self, future: F) -> Result<(), ExecuteError> {
let future = future.instrument(self.span.clone());
- deinstrument_err!(self.inner.execute(future))
+ self.inner.execute(future).map_err(|e| {
+ let kind = e.kind();
+ let future = e.into_future().into_inner();
+ ExecuteError::new(kind, future)
+ })
}
}
@@ -32,7 +26,11 @@ where
{
fn execute(&self, future: F) -> Result<(), ExecuteError> {
let future = self.with_dispatch(future);
- deinstrument_err!(self.inner.execute(future))
+ self.inner.execute(future).map_err(|e| {
+ let kind = e.kind();
+ let future = e.into_future().inner;
+ ExecuteError::new(kind, future)
+ })
}
}
diff --git a/tracing-futures/src/lib.rs b/tracing-futures/src/lib.rs
index 520f92d437..262d15fc87 100644
--- a/tracing-futures/src/lib.rs
+++ b/tracing-futures/src/lib.rs
@@ -15,7 +15,7 @@
//! * [`WithCollector`] allows a `tracing` [collector] to be attached to a
//! future, sink, stream, or executor.
//!
-//! *Compiler support: [requires `rustc` 1.49+][msrv]*
+//! *Compiler support: [requires `rustc` 1.56+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//!
@@ -60,14 +60,14 @@
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.49. The current Tracing version is not guaranteed to build on
+//! version is 1.56. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
//! project. The current stable Rust compiler and the three most recent minor
//! versions before it will always be supported. For example, if the current
-//! stable compiler version is 1.45, the minimum supported version will not be
-//! increased past 1.42, three minor versions prior. Increasing the minimum
+//! stable compiler version is 1.69, the minimum supported version will not be
+//! increased past 1.66, three minor versions prior. Increasing the minimum
//! supported compiler version is not considered a semver breaking change as
//! long as doing so complies with this policy.
//!
@@ -103,7 +103,11 @@
use pin_project_lite::pin_project;
#[cfg(feature = "std-future")]
-use core::{pin::Pin, task::Context};
+use core::{
+ mem::{self, ManuallyDrop},
+ pin::Pin,
+ task::Context,
+};
#[cfg(feature = "std")]
use tracing::{dispatch, Dispatch};
@@ -118,13 +122,13 @@ pub mod executor;
///
/// [span]: mod@tracing::span
pub trait Instrument: Sized {
- /// Instruments this type with the provided `Span`, returning an
- /// `Instrumented` wrapper.
+ /// Instruments this type with the provided [`Span`], returning an
+ /// [`Instrumented`] wrapper.
///
- /// If the instrumented type is a future, stream, or sink, the attached `Span`
- /// will be [entered] every time it is polled. If the instrumented type
- /// is a future executor, every future spawned on that executor will be
- /// instrumented by the attached `Span`.
+ /// If the instrumented type is a future, stream, or sink, the attached
+ /// [`Span`] will be [entered] every time it is polled or [`Drop`]ped. If
+ /// the instrumented type is a future executor, every future spawned on that
+ /// executor will be instrumented by the attached [`Span`].
///
/// # Examples
///
@@ -145,18 +149,22 @@ pub trait Instrument: Sized {
/// # }
/// ```
///
- /// [entered]: tracing::span::Span::enter()
+ /// [entered]: Span::enter()
fn instrument(self, span: Span) -> Instrumented {
- Instrumented { inner: self, span }
+ #[cfg(feature = "std-future")]
+ let inner = ManuallyDrop::new(self);
+ #[cfg(not(feature = "std-future"))]
+ let inner = self;
+ Instrumented { inner, span }
}
- /// Instruments this type with the [current] `Span`, returning an
- /// `Instrumented` wrapper.
+ /// Instruments this type with the [current] [`Span`], returning an
+ /// [`Instrumented`] wrapper.
///
- /// If the instrumented type is a future, stream, or sink, the attached `Span`
- /// will be [entered] every time it is polled. If the instrumented type
- /// is a future executor, every future spawned on that executor will be
- /// instrumented by the attached `Span`.
+ /// If the instrumented type is a future, stream, or sink, the attached
+ /// [`Span`] will be [entered] every time it is polled or [`Drop`]ped. If
+ /// the instrumented type is a future executor, every future spawned on that
+ /// executor will be instrumented by the attached [`Span`].
///
/// This can be used to propagate the current span when spawning a new future.
///
@@ -180,8 +188,8 @@ pub trait Instrument: Sized {
/// # }
/// ```
///
- /// [current]: tracing::span::Span::current()
- /// [entered]: tracing::span::Span::enter()
+ /// [current]: Span::current()
+ /// [entered]: Span::enter()
#[inline]
fn in_current_span(self) -> Instrumented {
self.instrument(Span::current())
@@ -240,12 +248,56 @@ pub trait WithCollector: Sized {
#[cfg(feature = "std-future")]
pin_project! {
/// A future, stream, sink, or executor that has been instrumented with a `tracing` span.
+ #[project = InstrumentedProj]
+ #[project_ref = InstrumentedProjRef]
#[derive(Debug, Clone)]
pub struct Instrumented {
+ // `ManuallyDrop` is used here to to enter instrument `Drop` by entering
+ // `Span` and executing `ManuallyDrop::drop`.
#[pin]
- inner: T,
+ inner: ManuallyDrop,
span: Span,
}
+
+ impl PinnedDrop for Instrumented {
+ fn drop(this: Pin<&mut Self>) {
+ let this = this.project();
+ let _enter = this.span.enter();
+ // SAFETY: 1. `Pin::get_unchecked_mut()` is safe, because this isn't
+ // different from wrapping `T` in `Option` and calling
+ // `Pin::set(&mut this.inner, None)`, except avoiding
+ // additional memory overhead.
+ // 2. `ManuallyDrop::drop()` is safe, because
+ // `PinnedDrop::drop()` is guaranteed to be called only
+ // once.
+ unsafe { ManuallyDrop::drop(this.inner.get_unchecked_mut()) }
+ }
+ }
+}
+
+#[cfg(feature = "std-future")]
+impl<'a, T> InstrumentedProj<'a, T> {
+ /// Get a mutable reference to the [`Span`] a pinned mutable reference to
+ /// the wrapped type.
+ fn span_and_inner_pin_mut(self) -> (&'a mut Span, Pin<&'a mut T>) {
+ // SAFETY: As long as `ManuallyDrop` does not move, `T` won't move
+ // and `inner` is valid, because `ManuallyDrop::drop` is called
+ // only inside `Drop` of the `Instrumented`.
+ let inner = unsafe { self.inner.map_unchecked_mut(|v| &mut **v) };
+ (self.span, inner)
+ }
+}
+
+#[cfg(feature = "std-future")]
+impl<'a, T> InstrumentedProjRef<'a, T> {
+ /// Get a reference to the [`Span`] a pinned reference to the wrapped type.
+ fn span_and_inner_pin_ref(self) -> (&'a Span, Pin<&'a T>) {
+ // SAFETY: As long as `ManuallyDrop` does not move, `T` won't move
+ // and `inner` is valid, because `ManuallyDrop::drop` is called
+ // only inside `Drop` of the `Instrumented`.
+ let inner = unsafe { self.inner.map_unchecked(|v| &**v) };
+ (self.span, inner)
+ }
}
/// A future, stream, sink, or executor that has been instrumented with a `tracing` span.
@@ -287,9 +339,9 @@ impl core::future::Future for Instrumented {
type Output = T::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> core::task::Poll {
- let this = self.project();
- let _enter = this.span.enter();
- this.inner.poll(cx)
+ let (span, inner) = self.project().span_and_inner_pin_mut();
+ let _enter = span.enter();
+ inner.poll(cx)
}
}
@@ -346,9 +398,9 @@ impl futures::Stream for Instrumented {
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> futures::task::Poll> {
- let this = self.project();
- let _enter = this.span.enter();
- T::poll_next(this.inner, cx)
+ let (span, inner) = self.project().span_and_inner_pin_mut();
+ let _enter = span.enter();
+ T::poll_next(inner, cx)
}
}
@@ -364,33 +416,33 @@ where
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> futures::task::Poll> {
- let this = self.project();
- let _enter = this.span.enter();
- T::poll_ready(this.inner, cx)
+ let (span, inner) = self.project().span_and_inner_pin_mut();
+ let _enter = span.enter();
+ T::poll_ready(inner, cx)
}
fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> {
- let this = self.project();
- let _enter = this.span.enter();
- T::start_send(this.inner, item)
+ let (span, inner) = self.project().span_and_inner_pin_mut();
+ let _enter = span.enter();
+ T::start_send(inner, item)
}
fn poll_flush(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> futures::task::Poll> {
- let this = self.project();
- let _enter = this.span.enter();
- T::poll_flush(this.inner, cx)
+ let (span, inner) = self.project().span_and_inner_pin_mut();
+ let _enter = span.enter();
+ T::poll_flush(inner, cx)
}
fn poll_close(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> futures::task::Poll> {
- let this = self.project();
- let _enter = this.span.enter();
- T::poll_close(this.inner, cx)
+ let (span, inner) = self.project().span_and_inner_pin_mut();
+ let _enter = span.enter();
+ T::poll_close(inner, cx)
}
}
@@ -419,20 +471,36 @@ impl Instrumented {
#[cfg(feature = "std-future")]
#[cfg_attr(docsrs, doc(cfg(feature = "std-future")))]
pub fn inner_pin_ref(self: Pin<&Self>) -> Pin<&T> {
- self.project_ref().inner
+ self.project_ref().span_and_inner_pin_ref().1
}
/// Get a pinned mutable reference to the wrapped type.
#[cfg(feature = "std-future")]
#[cfg_attr(docsrs, doc(cfg(feature = "std-future")))]
pub fn inner_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
- self.project().inner
+ self.project().span_and_inner_pin_mut().1
}
/// Consumes the `Instrumented`, returning the wrapped type.
///
/// Note that this drops the span.
pub fn into_inner(self) -> T {
+ #[cfg(feature = "std-future")]
+ {
+ // To manually destructure `Instrumented` without `Drop`, we save
+ // pointers to the fields and use `mem::forget` to leave those pointers
+ // valid.
+ let span: *const Span = &self.span;
+ let inner: *const ManuallyDrop = &self.inner;
+ mem::forget(self);
+ // SAFETY: Those pointers are valid for reads, because `Drop` didn't
+ // run, and properly aligned, because `Instrumented` isn't
+ // `#[repr(packed)]`.
+ let _span = unsafe { span.read() };
+ let inner = unsafe { inner.read() };
+ ManuallyDrop::into_inner(inner)
+ }
+ #[cfg(not(feature = "std-future"))]
self.inner
}
}
@@ -570,6 +638,8 @@ mod tests {
.exit(expect::span().named("foo"))
.enter(expect::span().named("foo"))
.exit(expect::span().named("foo"))
+ .enter(expect::span().named("foo"))
+ .exit(expect::span().named("foo"))
.drop_span(expect::span().named("foo"))
.only()
.run_with_handle();
@@ -589,6 +659,8 @@ mod tests {
.exit(expect::span().named("foo"))
.enter(expect::span().named("foo"))
.exit(expect::span().named("foo"))
+ .enter(expect::span().named("foo"))
+ .exit(expect::span().named("foo"))
.drop_span(expect::span().named("foo"))
.only()
.run_with_handle();
@@ -613,6 +685,8 @@ mod tests {
.exit(expect::span().named("foo"))
.enter(expect::span().named("foo"))
.exit(expect::span().named("foo"))
+ .enter(expect::span().named("foo"))
+ .exit(expect::span().named("foo"))
.drop_span(expect::span().named("foo"))
.run_with_handle();
with_default(collector, || {
diff --git a/tracing-futures/tests/std_future.rs b/tracing-futures/tests/std_future.rs
index cd9656f153..ebba8cf084 100644
--- a/tracing-futures/tests/std_future.rs
+++ b/tracing-futures/tests/std_future.rs
@@ -1,3 +1,6 @@
+use std::{future::Future, pin::Pin, task};
+
+use futures::FutureExt as _;
use tracing::Instrument;
use tracing::{collect::with_default, Level};
use tracing_mock::*;
@@ -9,6 +12,8 @@ fn enter_exit_is_reasonable() {
.exit(expect::span().named("foo"))
.enter(expect::span().named("foo"))
.exit(expect::span().named("foo"))
+ .enter(expect::span().named("foo"))
+ .exit(expect::span().named("foo"))
.drop_span(expect::span().named("foo"))
.only()
.run_with_handle();
@@ -26,6 +31,8 @@ fn error_ends_span() {
.exit(expect::span().named("foo"))
.enter(expect::span().named("foo"))
.exit(expect::span().named("foo"))
+ .enter(expect::span().named("foo"))
+ .exit(expect::span().named("foo"))
.drop_span(expect::span().named("foo"))
.only()
.run_with_handle();
@@ -35,3 +42,59 @@ fn error_ends_span() {
});
handle.assert_finished();
}
+
+#[test]
+fn span_on_drop() {
+ #[derive(Clone, Debug)]
+ struct AssertSpanOnDrop;
+
+ impl Drop for AssertSpanOnDrop {
+ fn drop(&mut self) {
+ tracing::info!("Drop");
+ }
+ }
+
+ struct Fut(Option);
+
+ impl Future for Fut {
+ type Output = ();
+
+ fn poll(mut self: Pin<&mut Self>, _: &mut task::Context<'_>) -> task::Poll {
+ self.set(Fut(None));
+ task::Poll::Ready(())
+ }
+ }
+
+ let collector = collector::mock()
+ .enter(expect::span().named("foo"))
+ .event(
+ expect::event()
+ .with_contextual_parent(Some("foo"))
+ .at_level(Level::INFO),
+ )
+ .exit(expect::span().named("foo"))
+ .enter(expect::span().named("foo"))
+ .exit(expect::span().named("foo"))
+ .drop_span(expect::span().named("foo"))
+ .enter(expect::span().named("bar"))
+ .event(
+ expect::event()
+ .with_contextual_parent(Some("bar"))
+ .at_level(Level::INFO),
+ )
+ .exit(expect::span().named("bar"))
+ .drop_span(expect::span().named("bar"))
+ .only()
+ .run();
+
+ with_default(collector, || {
+ // polled once
+ Fut(Some(AssertSpanOnDrop))
+ .instrument(tracing::span!(Level::TRACE, "foo"))
+ .now_or_never()
+ .unwrap();
+
+ // never polled
+ drop(Fut(Some(AssertSpanOnDrop)).instrument(tracing::span!(Level::TRACE, "bar")));
+ });
+}
diff --git a/tracing-journald/Cargo.toml b/tracing-journald/Cargo.toml
index 29390804d8..a2c0755283 100644
--- a/tracing-journald/Cargo.toml
+++ b/tracing-journald/Cargo.toml
@@ -13,7 +13,7 @@ categories = [
"development-tools::profiling",
]
keywords = ["tracing", "journald"]
-rust-version = "1.49.0"
+rust-version = "1.56.0"
[dependencies]
libc = "0.2.126"
diff --git a/tracing-journald/README.md b/tracing-journald/README.md
index 76d3d4c0d7..6e6ec5942a 100644
--- a/tracing-journald/README.md
+++ b/tracing-journald/README.md
@@ -28,7 +28,7 @@ scoped, structured, and async-aware diagnostics. `tracing-journald` provides a
and events to [`systemd-journald`][journald], on Linux distributions that use
`systemd`.
-*Compiler support: [requires `rustc` 1.49+][msrv]*
+*Compiler support: [requires `rustc` 1.56+][msrv]*
[msrv]: #supported-rust-versions
[`tracing`]: https://crates.io/crates/tracing
@@ -38,14 +38,14 @@ and events to [`systemd-journald`][journald], on Linux distributions that use
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.49. The current Tracing version is not guaranteed to build on Rust
+version is 1.56. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
project. The current stable Rust compiler and the three most recent minor
versions before it will always be supported. For example, if the current stable
-compiler version is 1.45, the minimum supported version will not be increased
-past 1.42, three minor versions prior. Increasing the minimum supported compiler
+compiler version is 1.69, the minimum supported version will not be increased
+past 1.66, three minor versions prior. Increasing the minimum supported compiler
version is not considered a semver breaking change as long as doing so complies
with this policy.
diff --git a/tracing-journald/src/lib.rs b/tracing-journald/src/lib.rs
index 2433418ce9..237a0de647 100644
--- a/tracing-journald/src/lib.rs
+++ b/tracing-journald/src/lib.rs
@@ -11,7 +11,7 @@
//! and events to [`systemd-journald`][journald], on Linux distributions that
//! use `systemd`.
//!
-//! *Compiler support: [requires `rustc` 1.49+][msrv]*
+//! *Compiler support: [requires `rustc` 1.56+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//! [`tracing`]: https://crates.io/crates/tracing
@@ -21,14 +21,14 @@
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.49. The current Tracing version is not guaranteed to build on
+//! version is 1.56. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
//! project. The current stable Rust compiler and the three most recent minor
//! versions before it will always be supported. For example, if the current
-//! stable compiler version is 1.45, the minimum supported version will not be
-//! increased past 1.42, three minor versions prior. Increasing the minimum
+//! stable compiler version is 1.69, the minimum supported version will not be
+//! increased past 1.66, three minor versions prior. Increasing the minimum
//! supported compiler version is not considered a semver breaking change as
//! long as doing so complies with this policy.
//!
diff --git a/tracing-journald/tests/journal.rs b/tracing-journald/tests/journal.rs
index dadb37aac3..5eab5f79cd 100644
--- a/tracing-journald/tests/journal.rs
+++ b/tracing-journald/tests/journal.rs
@@ -121,7 +121,7 @@ fn read_from_journal(test_name: &str) -> Vec> {
let stdout = String::from_utf8(
Command::new("journalctl")
// We pass --all to circumvent journalctl's default limit of 4096 bytes for field values
- .args(&["--user", "--output=json", "--all"])
+ .args(["--user", "--output=json", "--all"])
// Filter by the PID of the current test process
.arg(format!("_PID={}", std::process::id()))
.arg(format!("TEST_NAME={}", test_name))
diff --git a/tracing-log/Cargo.toml b/tracing-log/Cargo.toml
index 4976491527..61e68b376c 100644
--- a/tracing-log/Cargo.toml
+++ b/tracing-log/Cargo.toml
@@ -15,7 +15,7 @@ categories = [
keywords = ["logging", "tracing", "log"]
license = "MIT"
readme = "README.md"
-rust-version = "1.49.0"
+rust-version = "1.56.0"
[features]
default = ["log-tracer", "std"]
diff --git a/tracing-log/README.md b/tracing-log/README.md
index 1cfee3681d..3da5f6b7e1 100644
--- a/tracing-log/README.md
+++ b/tracing-log/README.md
@@ -56,21 +56,21 @@ This crate provides:
[`tracing::Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html
[`tracing::Event`]: https://docs.rs/tracing/latest/tracing/struct.Event.html
-*Compiler support: [requires `rustc` 1.49+][msrv]*
+*Compiler support: [requires `rustc` 1.56+][msrv]*
[msrv]: #supported-rust-versions
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.49. The current Tracing version is not guaranteed to build on Rust
+version is 1.56. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
project. The current stable Rust compiler and the three most recent minor
versions before it will always be supported. For example, if the current stable
-compiler version is 1.45, the minimum supported version will not be increased
-past 1.42, three minor versions prior. Increasing the minimum supported compiler
+compiler version is 1.69, the minimum supported version will not be increased
+past 1.66, three minor versions prior. Increasing the minimum supported compiler
version is not considered a semver breaking change as long as doing so complies
with this policy.
diff --git a/tracing-log/src/lib.rs b/tracing-log/src/lib.rs
index 36f0b6fc05..916d0b6b7f 100644
--- a/tracing-log/src/lib.rs
+++ b/tracing-log/src/lib.rs
@@ -16,7 +16,7 @@
//! - An [`env_logger`] module, with helpers for using the [`env_logger` crate]
//! with `tracing` (optional, enabled by the `env-logger` feature).
//!
-//! *Compiler support: [requires `rustc` 1.49+][msrv]*
+//! *Compiler support: [requires `rustc` 1.56+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//!
@@ -76,14 +76,14 @@
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.49. The current Tracing version is not guaranteed to build on
+//! version is 1.56. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
//! project. The current stable Rust compiler and the three most recent minor
//! versions before it will always be supported. For example, if the current
-//! stable compiler version is 1.45, the minimum supported version will not be
-//! increased past 1.42, three minor versions prior. Increasing the minimum
+//! stable compiler version is 1.69, the minimum supported version will not be
+//! increased past 1.66, three minor versions prior. Increasing the minimum
//! supported compiler version is not considered a semver breaking change as
//! long as doing so complies with this policy.
//!
diff --git a/tracing-macros/Cargo.toml b/tracing-macros/Cargo.toml
index 3d15aee3b6..b457067e26 100644
--- a/tracing-macros/Cargo.toml
+++ b/tracing-macros/Cargo.toml
@@ -15,7 +15,7 @@ categories = [
]
keywords = ["logging", "tracing"]
license = "MIT"
-rust-version = "1.49.0"
+rust-version = "1.56.0"
[dependencies]
tracing = { path = "../tracing", version = "0.2", default-features = false, features = ["std"] }
diff --git a/tracing-mock/Cargo.toml b/tracing-mock/Cargo.toml
index 5f50409556..e041338b0a 100644
--- a/tracing-mock/Cargo.toml
+++ b/tracing-mock/Cargo.toml
@@ -14,13 +14,13 @@ readme = "README.md"
repository = "https://github.com/tokio-rs/tracing"
homepage = "https://tokio.rs"
edition = "2018"
-rust-version = "1.49.0"
+rust-version = "1.56.0"
publish = false
[dependencies]
tracing = { path = "../tracing", version = "0.2" }
tracing-core = { path = "../tracing-core", version = "0.2", default-features = false }
-tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", default-features = false, optional = true }
+tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", default-features = false, features = ["registry"], optional = true }
tokio-test = { version = "0.4.2", optional = true }
# Fix minimal-versions; tokio-test fails with otherwise acceptable 0.1.0
diff --git a/tracing-mock/README.md b/tracing-mock/README.md
index b508db00cb..e561606119 100644
--- a/tracing-mock/README.md
+++ b/tracing-mock/README.md
@@ -29,7 +29,7 @@ structured, event-based diagnostic information. `tracing-mock` provides
tools for making assertions about what `tracing` diagnostics are emitted
by code under test.
-*Compiler support: [requires `rustc` 1.49+][msrv]*
+*Compiler support: [requires `rustc` 1.56+][msrv]*
[msrv]: #supported-rust-versions
@@ -154,14 +154,14 @@ handle.assert_finished();
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.49. The current Tracing version is not guaranteed to build on Rust
+version is 1.56. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
project. The current stable Rust compiler and the three most recent minor
versions before it will always be supported. For example, if the current stable
-compiler version is 1.45, the minimum supported version will not be increased
-past 1.42, three minor versions prior. Increasing the minimum supported compiler
+compiler version is 1.69, the minimum supported version will not be increased
+past 1.66, three minor versions prior. Increasing the minimum supported compiler
version is not considered a semver breaking change as long as doing so complies
with this policy.
diff --git a/tracing-mock/src/collector.rs b/tracing-mock/src/collector.rs
index 21cb6d6e2f..530766fd6c 100644
--- a/tracing-mock/src/collector.rs
+++ b/tracing-mock/src/collector.rs
@@ -229,6 +229,14 @@ where
match self.expected.lock().unwrap().pop_front() {
None => {}
Some(Expect::Event(mut expected)) => {
+ #[cfg(feature = "tracing-subscriber")]
+ {
+ if expected.scope_mut().is_some() {
+ unimplemented!(
+ "Expected scope for events is not supported with `MockCollector`."
+ )
+ }
+ }
let get_parent_name = || {
let stack = self.current.lock().unwrap();
let spans = self.spans.lock().unwrap();
diff --git a/tracing-mock/src/event.rs b/tracing-mock/src/event.rs
index 4258d89fba..497b9e9aa8 100644
--- a/tracing-mock/src/event.rs
+++ b/tracing-mock/src/event.rs
@@ -1,17 +1,49 @@
+//! An [`ExpectedEvent`] defines an event to be matched by the mock
+//! collector API in the [`collector`] module.
+//!
+//! The expected event should be created with [`expect::event`] and a
+//! chain of method calls to describe the assertions we wish to make
+//! about the event.
+//!
+//! # Examples
+//!
+//! ```
+//! use tracing::collect::with_default;
+//! use tracing_mock::{collector, expect};
+//!
+//! let event = expect::event()
+//! .at_level(tracing::Level::INFO)
+//! .with_fields(expect::field("field.name").with_value(&"field_value"));
+//!
+//! let (collector, handle) = collector::mock()
+//! .event(event)
+//! .run_with_handle();
+//!
+//! with_default(collector, || {
+//! tracing::info!(field.name = "field_value");
+//! });
+//!
+//! handle.assert_finished();
+//! ```
+//!
+//! [`collector`]: mod@crate::collector
+//! [`expect::event`]: fn@crate::expect::event
#![allow(missing_docs)]
use super::{expect, field, metadata::ExpectedMetadata, span, Parent};
use std::fmt;
-/// A mock event.
+/// An expected event.
///
-/// This is intended for use with the mock subscriber API in the
-/// `subscriber` module.
+/// For a detailed description and examples, see the documentation for
+/// the methods and the [`event`] module.
+///
+/// [`event`]: mod@crate::event
#[derive(Default, Eq, PartialEq)]
pub struct ExpectedEvent {
pub(super) fields: Option,
pub(super) parent: Option,
- pub(super) in_spans: Vec,
+ pub(super) in_spans: Option>,
pub(super) metadata: ExpectedMetadata,
}
@@ -20,6 +52,20 @@ pub fn msg(message: impl fmt::Display) -> ExpectedEvent {
}
impl ExpectedEvent {
+ /// Sets a name to expect when matching an event.
+ ///
+ /// By default, an event's name takes takes the form:
+ /// `event :` where `` and `` refer to the
+ /// location in the source code where the event was generated.
+ ///
+ /// To override the name of an event, it has to be constructed
+ /// directly, rather than by using the `tracing` crate's macros.
+ ///
+ /// In general, there are not many use cases for expecting an
+ /// event with a particular name, as the value includes the file
+ /// name and line number. Assertions about event names are
+ /// therefore quite fragile, since they will change as the source
+ /// code is modified.
pub fn named(self, name: I) -> Self
where
I: Into,
@@ -33,6 +79,59 @@ impl ExpectedEvent {
}
}
+ /// Adds fields to expect when matching an event.
+ ///
+ /// If an event is recorded with fields that do not match the provided
+ /// [`ExpectedFields`], this expectation will fail.
+ ///
+ /// If the provided field is not present on the recorded event, or
+ /// if the value for that field is different, then the expectation
+ /// will fail.
+ ///
+ /// More information on the available validations is available in
+ /// the [`ExpectedFields`] documentation.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tracing::collect::with_default;
+ /// use tracing_mock::{collector, expect};
+ ///
+ /// let event = expect::event()
+ /// .with_fields(expect::field("field.name").with_value(&"field_value"));
+ ///
+ /// let (collector, handle) = collector::mock()
+ /// .event(event)
+ /// .run_with_handle();
+ ///
+ /// with_default(collector, || {
+ /// tracing::info!(field.name = "field_value");
+ /// });
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// A different field value will cause the expectation to fail:
+ ///
+ /// ```should_panic
+ /// use tracing::collect::with_default;
+ /// use tracing_mock::{collector, expect};
+ ///
+ /// let event = expect::event()
+ /// .with_fields(expect::field("field.name").with_value(&"field_value"));
+ ///
+ /// let (collector, handle) = collector::mock()
+ /// .event(event)
+ /// .run_with_handle();
+ ///
+ /// with_default(collector, || {
+ /// tracing::info!(field.name = "different_field_value");
+ /// });
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// [`ExpectedFields`]: struct@crate::field::ExpectedFields
pub fn with_fields(self, fields: I) -> Self
where
I: Into,
@@ -43,6 +142,51 @@ impl ExpectedEvent {
}
}
+ /// Sets the [`Level`](tracing::Level) to expect when matching an event.
+ ///
+ /// If an event is recorded at a different level, this expectation
+ /// will fail.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tracing::collect::with_default;
+ /// use tracing_mock::{collector, expect};
+ ///
+ /// let event = expect::event()
+ /// .at_level(tracing::Level::WARN);
+ ///
+ /// let (collector, handle) = collector::mock()
+ /// .event(event)
+ /// .run_with_handle();
+ ///
+ /// with_default(collector, || {
+ /// tracing::warn!("this message is bad news");
+ /// });
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// Expecting an event at `INFO` level will fail if the event is
+ /// recorded at any other level:
+ ///
+ /// ```should_panic
+ /// use tracing::collect::with_default;
+ /// use tracing_mock::{collector, expect};
+ ///
+ /// let event = expect::event()
+ /// .at_level(tracing::Level::INFO);
+ ///
+ /// let (collector, handle) = collector::mock()
+ /// .event(event)
+ /// .run_with_handle();
+ ///
+ /// with_default(collector, || {
+ /// tracing::warn!("this message is bad news");
+ /// });
+ ///
+ /// handle.assert_finished();
+ /// ```
pub fn at_level(self, level: tracing::Level) -> Self {
Self {
metadata: ExpectedMetadata {
@@ -53,6 +197,49 @@ impl ExpectedEvent {
}
}
+ /// Sets the target to expect when matching events.
+ ///
+ /// If an event is recorded with a different target, this expectation will fail.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tracing::collect::with_default;
+ /// use tracing_mock::{collector, expect};
+ ///
+ /// let event = expect::event()
+ /// .with_target("some_target");
+ ///
+ /// let (collector, handle) = collector::mock()
+ /// .event(event)
+ /// .run_with_handle();
+ ///
+ /// with_default(collector, || {
+ /// tracing::info!(target: "some_target", field = &"value");
+ /// });
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// The test will fail if the target is different:
+ ///
+ /// ```should_panic
+ /// use tracing::collect::with_default;
+ /// use tracing_mock::{collector, expect};
+ ///
+ /// let event = expect::event()
+ /// .with_target("some_target");
+ ///
+ /// let (collector, handle) = collector::mock()
+ /// .event(event)
+ /// .run_with_handle();
+ ///
+ /// with_default(collector, || {
+ /// tracing::info!(target: "a_different_target", field = &"value");
+ /// });
+ ///
+ /// handle.assert_finished();
+ /// ```
pub fn with_target(self, target: I) -> Self
where
I: Into,
@@ -66,6 +253,90 @@ impl ExpectedEvent {
}
}
+ /// Configures this `ExpectedEvent` to expect an explicit parent span
+ /// when matching events or to be an explicit root.
+ ///
+ /// An _explicit_ parent span is one passed to the `span!` macro in the
+ /// `parent:` field.
+ ///
+ /// If `Some("parent_name")` is passed to `with_explicit_parent` then
+ /// the provided string is the name of the parent span to expect.
+ ///
+ /// To expect that an event is recorded with `parent: None`, `None`
+ /// can be passed to `with_explicit_parent` instead.
+ ///
+ /// If an event is recorded without an explicit parent, or if the
+ /// explicit parent has a different name, this expectation will
+ /// fail.
+ ///
+ /// # Examples
+ ///
+ /// The explicit parent is matched by name:
+ ///
+ /// ```
+ /// use tracing::collect::with_default;
+ /// use tracing_mock::{collector, expect};
+ ///
+ /// let event = expect::event()
+ /// .with_explicit_parent(Some("parent_span"));
+ ///
+ /// let (collector, handle) = collector::mock()
+ /// .event(event)
+ /// .run_with_handle();
+ ///
+ /// with_default(collector, || {
+ /// let parent = tracing::info_span!("parent_span");
+ /// tracing::info!(parent: parent.id(), field = &"value");
+ /// });
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// In the following example, we expect that the matched event is
+ /// an explicit root:
+ ///
+ /// ```
+ /// use tracing::collect::with_default;
+ /// use tracing_mock::{collector, expect};
+ ///
+ /// let event = expect::event()
+ /// .with_explicit_parent(None);
+ ///
+ /// let (collector, handle) = collector::mock()
+ /// .event(event)
+ /// .run_with_handle();
+ ///
+ /// with_default(collector, || {
+ /// tracing::info!(parent: None, field = &"value");
+ /// });
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// In the example below, the expectation fails because the
+ /// event is contextually (rather than explicitly) within the span
+ /// `parent_span`:
+ ///
+ /// ```should_panic
+ /// use tracing::collect::with_default;
+ /// use tracing_mock::{collector, expect};
+ ///
+ /// let event = expect::event()
+ /// .with_explicit_parent(Some("parent_span"));
+ ///
+ /// let (collector, handle) = collector::mock()
+ /// .enter(expect::span())
+ /// .event(event)
+ /// .run_with_handle();
+ ///
+ /// with_default(collector, || {
+ /// let parent = tracing::info_span!("parent_span");
+ /// let _guard = parent.enter();
+ /// tracing::info!(field = &"value");
+ /// });
+ ///
+ /// handle.assert_finished();
+ /// ```
pub fn with_explicit_parent(self, parent: Option<&str>) -> ExpectedEvent {
let parent = match parent {
Some(name) => Parent::Explicit(name.into()),
@@ -77,7 +348,213 @@ impl ExpectedEvent {
}
}
- pub fn check(
+ /// Configures this `ExpectedEvent` to match an event with a
+ /// contextually-determined parent span.
+ ///
+ /// The provided string is the name of the parent span to expect.
+ /// To expect that the event is a contextually-determined root, pass
+ /// `None` instead.
+ ///
+ /// To expect an event with an explicit parent span, use
+ /// [`ExpectedEvent::with_explicit_parent`].
+ ///
+ /// If an event is recorded which is not inside a span, has an explicitly
+ /// overridden parent span, or with a differently-named span as its
+ /// parent, this expectation will fail.
+ ///
+ /// # Examples
+ ///
+ /// The explicit parent is matched by name:
+ ///
+ /// ```
+ /// use tracing::collect::with_default;
+ /// use tracing_mock::{collector, expect};
+ ///
+ /// let event = expect::event()
+ /// .with_contextual_parent(Some("parent_span"));
+ ///
+ /// let (collector, handle) = collector::mock()
+ /// .enter(expect::span())
+ /// .event(event)
+ /// .run_with_handle();
+ ///
+ /// with_default(collector, || {
+ /// let parent = tracing::info_span!("parent_span");
+ /// let _guard = parent.enter();
+ /// tracing::info!(field = &"value");
+ /// });
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// Matching an event recorded outside of a span:
+ ///
+ /// ```
+ /// use tracing::collect::with_default;
+ /// use tracing_mock::{collector, expect};
+ ///
+ /// let event = expect::event()
+ /// .with_contextual_parent(None);
+ ///
+ /// let (collector, handle) = collector::mock()
+ /// .event(event)
+ /// .run_with_handle();
+ ///
+ /// with_default(collector, || {
+ /// tracing::info!(field = &"value");
+ /// });
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// In the example below, the expectation fails because the
+ /// event is recorded with an explicit parent:
+ ///
+ /// ```should_panic
+ /// use tracing::collect::with_default;
+ /// use tracing_mock::{collector, expect};
+ ///
+ /// let event = expect::event()
+ /// .with_contextual_parent(Some("parent_span"));
+ ///
+ /// let (collector, handle) = collector::mock()
+ /// .enter(expect::span())
+ /// .event(event)
+ /// .run_with_handle();
+ ///
+ /// with_default(collector, || {
+ /// let parent = tracing::info_span!("parent_span");
+ /// tracing::info!(parent: parent.id(), field = &"value");
+ /// });
+ ///
+ /// handle.assert_finished();
+ /// ```
+ pub fn with_contextual_parent(self, parent: Option<&str>) -> ExpectedEvent {
+ let parent = match parent {
+ Some(name) => Parent::Contextual(name.into()),
+ None => Parent::ContextualRoot,
+ };
+ Self {
+ parent: Some(parent),
+ ..self
+ }
+ }
+
+ /// Validates that the event is emitted within the scope of the
+ /// provided `spans`.
+ ///
+ /// The spans must be provided reverse hierarchy order, so the
+ /// closest span to the event would be first, followed by its
+ /// parent, and so on.
+ ///
+ /// If the spans provided do not match the hierarchy of the
+ /// recorded event, the expectation will fail.
+ ///
+ /// **Note**: This validation currently only works with a
+ /// [`MockSubscriber`]. If used with a [`MockCollector`], the
+ /// expectation will fail directly as it is unimplemented.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tracing_mock::{expect, subscriber};
+ /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+ ///
+ /// let event = expect::event().in_scope([
+ /// expect::span().named("parent_span"),
+ /// expect::span().named("grandparent_span")
+ /// ]);
+ ///
+ /// let (subscriber, handle) = subscriber::mock()
+ /// .enter(expect::span())
+ /// .enter(expect::span())
+ /// .event(event)
+ /// .run_with_handle();
+ ///
+ /// let _collect = tracing_subscriber::registry()
+ /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+ /// .set_default();
+ ///
+ /// let grandparent = tracing::info_span!("grandparent_span");
+ /// let _gp_guard = grandparent.enter();
+ /// let parent = tracing::info_span!("parent_span");
+ /// let _p_guard = parent.enter();
+ /// tracing::info!(field = &"value");
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// The scope must match exactly, otherwise the expectation will fail:
+ ///
+ /// ```should_panic
+ /// use tracing_mock::{expect, subscriber};
+ /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+ ///
+ /// let event = expect::event().in_scope([
+ /// expect::span().named("parent_span"),
+ /// expect::span().named("grandparent_span")
+ /// ]);
+ ///
+ /// let (subscriber, handle) = subscriber::mock()
+ /// .enter(expect::span())
+ /// .event(event)
+ /// .run_with_handle();
+ ///
+ /// let _collect = tracing_subscriber::registry()
+ /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+ /// .set_default();
+ ///
+ /// let parent = tracing::info_span!("parent_span");
+ /// let _p_guard = parent.enter();
+ /// tracing::info!(field = &"value");
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// It is also possible to test that an event has no parent spans
+ /// by passing `None` to `in_scope`. If the event is within a
+ /// span, the test will fail:
+ ///
+ /// ```should_panic
+ /// use tracing_mock::{expect, subscriber};
+ /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+ ///
+ /// let event = expect::event().in_scope(None);
+ ///
+ /// let (subscriber, handle) = subscriber::mock()
+ /// .enter(expect::span())
+ /// .event(event)
+ /// .run_with_handle();
+ ///
+ /// let _collect = tracing_subscriber::registry()
+ /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+ /// .set_default();
+ ///
+ /// let parent = tracing::info_span!("parent_span");
+ /// let _guard = parent.enter();
+ /// tracing::info!(field = &"value");
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// [`MockSubscriber`]: struct@crate::subscriber::MockSubscriber
+ /// [`MockCollector`]: struct@crate::collector::MockCollector
+ #[cfg(feature = "tracing-subscriber")]
+ pub fn in_scope(self, spans: impl IntoIterator- ) -> Self {
+ Self {
+ in_spans: Some(spans.into_iter().collect()),
+ ..self
+ }
+ }
+
+ /// Provides access to the expected scope (spans) for this expected
+ /// event.
+ #[cfg(feature = "tracing-subscriber")]
+ pub(crate) fn scope_mut(&mut self) -> Option<&mut [span::ExpectedSpan]> {
+ self.in_spans.as_mut().map(|s| &mut s[..])
+ }
+
+ pub(crate) fn check(
&mut self,
event: &tracing::Event<'_>,
get_parent_name: impl FnOnce() -> Option
,
@@ -110,17 +587,6 @@ impl ExpectedEvent {
)
}
}
-
- pub fn in_scope(self, spans: impl IntoIterator- ) -> Self {
- Self {
- in_spans: spans.into_iter().collect(),
- ..self
- }
- }
-
- pub fn scope_mut(&mut self) -> &mut [span::ExpectedSpan] {
- &mut self.in_spans[..]
- }
}
impl fmt::Display for ExpectedEvent {
@@ -153,8 +619,8 @@ impl fmt::Debug for ExpectedEvent {
s.field("parent", &format_args!("{:?}", parent));
}
- if !self.in_spans.is_empty() {
- s.field("in_spans", &self.in_spans);
+ if let Some(in_spans) = &self.in_spans {
+ s.field("in_spans", in_spans);
}
s.finish()
diff --git a/tracing-mock/src/lib.rs b/tracing-mock/src/lib.rs
index 712549744f..965b36a5e5 100644
--- a/tracing-mock/src/lib.rs
+++ b/tracing-mock/src/lib.rs
@@ -48,6 +48,13 @@ impl Parent {
);
}
Parent::Explicit(expected_parent) => {
+ assert!(
+ provided_parent.is_some(),
+ "[{}] expected {} to have explicit parent {}, but it has no explicit parent",
+ collector_name,
+ ctx,
+ expected_parent,
+ );
assert_eq!(
Some(expected_parent.as_ref()),
parent_name,
@@ -62,7 +69,7 @@ impl Parent {
Parent::ContextualRoot => {
assert!(
provided_parent.is_none(),
- "[{}] expected {} to have a contextual parent, but its parent was actually {:?} (name: {:?})",
+ "[{}] expected {} to be a contextual root, but its parent was actually {:?} (name: {:?})",
collector_name,
ctx,
provided_parent,
@@ -78,7 +85,7 @@ impl Parent {
}
Parent::Contextual(expected_parent) => {
assert!(provided_parent.is_none(),
- "[{}] expected {} to have a contextual parent\nbut its parent was actually {:?} (name: {:?})",
+ "[{}] expected {} to have a contextual parent\nbut it has the explicit parent {:?} (name: {:?})",
collector_name,
ctx,
provided_parent,
diff --git a/tracing-mock/src/subscriber.rs b/tracing-mock/src/subscriber.rs
index 007d0ef035..e923f56863 100644
--- a/tracing-mock/src/subscriber.rs
+++ b/tracing-mock/src/subscriber.rs
@@ -1,9 +1,124 @@
-#![allow(missing_docs, dead_code)]
+//! An implementation of the [`Subscribe`] trait which validates that
+//! the `tracing` data it recieves matches the expected output for a test.
+//!
+//!
+//! The [`MockSubscriber`] is the central component in these tools. The
+//! `MockSubscriber` has expectations set on it which are later
+//! validated as the code under test is run.
+//!
+//! ```
+//! use tracing_mock::{expect, field, subscriber};
+//! use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+//!
+//! let (subscriber, handle) = subscriber::mock()
+//! // Expect a single event with a specified message
+//! .event(expect::event().with_fields(field::msg("droids")))
+//! .run_with_handle();
+//!
+//! // Use `set_default` to apply the `MockSubscriber` until the end
+//! // of the current scope (when the guard `_collect` is dropped).
+//! let _collect = tracing_subscriber::registry()
+//! .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+//! .set_default();
+//!
+//! // These *are* the droids we are looking for
+//! tracing::info!("droids");
+//!
+//! // Use the handle to check the assertions. This line will panic if an
+//! // assertion is not met.
+//! handle.assert_finished();
+//! ```
+//!
+//! A more complex example may consider multiple spans and events with
+//! their respective fields:
+//!
+//! ```
+//! use tracing_mock::{expect, field, subscriber};
+//! use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+//!
+//! let span = expect::span()
+//! .named("my_span");
+//! let (subscriber, handle) = subscriber::mock()
+//! // Enter a matching span
+//! .enter(span.clone())
+//! // Record an event with message "collect parting message"
+//! .event(expect::event().with_fields(field::msg("say hello")))
+//! // Exit a matching span
+//! .exit(span)
+//! // Expect no further messages to be recorded
+//! .only()
+//! // Return the collector and handle
+//! .run_with_handle();
+//!
+//! // Use `set_default` to apply the `MockSubscriber` until the end
+//! // of the current scope (when the guard `_collect` is dropped).
+//! let _collect = tracing_subscriber::registry()
+//! .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+//! .set_default();
+//!
+//! {
+//! let span = tracing::trace_span!(
+//! "my_span",
+//! greeting = "hello world",
+//! );
+//!
+//! let _guard = span.enter();
+//! tracing::info!("say hello");
+//! }
+//!
+//! // Use the handle to check the assertions. This line will panic if an
+//! // assertion is not met.
+//! handle.assert_finished();
+//! ```
+//!
+//! If we modify the previous example so that we **don't** enter the
+//! span before recording an event, the test will fail:
+//!
+//! ```should_panic
+//! use tracing_mock::{expect, field, subscriber};
+//! use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+//!
+//! let span = expect::span()
+//! .named("my_span");
+//! let (subscriber, handle) = subscriber::mock()
+//! // Enter a matching span
+//! .enter(span.clone())
+//! // Record an event with message "collect parting message"
+//! .event(expect::event().with_fields(field::msg("say hello")))
+//! // Exit a matching span
+//! .exit(span)
+//! // Expect no further messages to be recorded
+//! .only()
+//! // Return the collector and handle
+//! .run_with_handle();
+//!
+//! // Use `set_default` to apply the `MockSubscriber` until the end
+//! // of the current scope (when the guard `_collect` is dropped).
+//! let _collect = tracing_subscriber::registry()
+//! .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+//! .set_default();
+//!
+//! {
+//! let span = tracing::trace_span!(
+//! "my_span",
+//! greeting = "hello world",
+//! );
+//!
+//! // Don't enter the span.
+//! // let _guard = span.enter();
+//! tracing::info!("say hello");
+//! }
+//!
+//! // Use the handle to check the assertions. This line will panic if an
+//! // assertion is not met.
+//! handle.assert_finished();
+//! ```
+//!
+//! [`Subscribe`]: trait@tracing_subscriber::subscribe::Subscribe
use crate::{
collector::MockHandle,
event::ExpectedEvent,
expect::Expect,
- field::ExpectedFields,
span::{ExpectedSpan, NewSpan},
};
use tracing_core::{
@@ -21,6 +136,55 @@ use std::{
sync::{Arc, Mutex},
};
+/// Create a [`MockSubscriberBuilder`] used to construct a
+/// [`MockSubscriber`].
+///
+/// For additional information and examples, see the [`subscriber`]
+/// module and [`MockSubscriberBuilder`] documentation.
+///
+/// # Examples
+///
+/// ```
+/// use tracing_mock::{expect, field, subscriber};
+/// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+///
+/// let span = expect::span()
+/// .named("my_span");
+/// let (subscriber, handle) = subscriber::mock()
+/// // Enter a matching span
+/// .enter(span.clone())
+/// // Record an event with message "collect parting message"
+/// .event(expect::event().with_fields(field::msg("say hello")))
+/// // Exit a matching span
+/// .exit(span)
+/// // Expect no further messages to be recorded
+/// .only()
+/// // Return the collector and handle
+/// .run_with_handle();
+///
+/// // Use `set_default` to apply the `MockSubscriber` until the end
+/// // of the current scope (when the guard `_collect` is dropped).
+/// let _collect = tracing_subscriber::registry()
+/// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+/// .set_default();
+///
+/// {
+/// let span = tracing::trace_span!(
+/// "my_span",
+/// greeting = "hello world",
+/// );
+///
+/// let _guard = span.enter();
+/// tracing::info!("say hello");
+/// }
+///
+/// // Use the handle to check the assertions. This line will panic if an
+/// // assertion is not met.
+/// handle.assert_finished();
+/// ```
+///
+/// [`subscriber`]: mod@crate::subscriber
+#[must_use]
pub fn mock() -> MockSubscriberBuilder {
MockSubscriberBuilder {
expected: Default::default(),
@@ -31,15 +195,82 @@ pub fn mock() -> MockSubscriberBuilder {
}
}
+/// Create a [`MockSubscriberBuilder`] with a name already set.
+///
+/// This constructor is equivalent to calling
+/// [`MockSubscriberBuilder::named`] in the following way
+/// `subscriber::mock().named(name)`.
+///
+/// For additional information and examples, see the [`subscriber`]
+/// module and [`MockSubscriberBuilder`] documentation.
+///
+/// # Examples
+///
+/// The example from [`MockSubscriberBuilder::named`] could be
+/// rewritten as:
+///
+/// ```should_panic
+/// use tracing_mock::{subscriber, expect};
+/// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+///
+/// let (subscriber_1, handle_1) = subscriber::named("subscriber-1")
+/// .event(expect::event())
+/// .run_with_handle();
+///
+/// let (subscriber_2, handle_2) = subscriber::named("subscriber-2")
+/// .event(expect::event())
+/// .run_with_handle();
+///
+/// let _collect = tracing_subscriber::registry()
+/// .with(
+/// subscriber_2.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true))
+/// )
+/// .set_default();
+/// {
+/// let _collect = tracing_subscriber::registry()
+/// .with(
+/// subscriber_1
+/// .with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true))
+/// )
+/// .set_default();
+///
+/// tracing::info!("a");
+/// }
+///
+/// handle_1.assert_finished();
+/// handle_2.assert_finished();
+/// ```
+///
+/// [`named`]: fn@crate::subscriber::MockSubscriberBuilder::named
+/// [`subscriber`]: mod@crate::subscriber
+#[must_use]
pub fn named(name: impl std::fmt::Display) -> MockSubscriberBuilder {
mock().named(name)
}
+/// A builder for constructing [`MockSubscriber`]s.
+///
+/// The methods on this builder set expectations which are then
+/// validated by the constructed [`MockSubscriber`].
+///
+/// For a detailed description and examples see the documentation
+/// for the methods and the [`subscriber`] module.
+///
+/// [`subscriber`]: mod@crate::subscriber
+
pub struct MockSubscriberBuilder {
expected: VecDeque
,
name: String,
}
+/// A subscriber which validates the traces it receives.
+///
+/// A `MockSubscriber` is constructed with a
+/// [`MockSubscriberBuilder`]. For a detailed description and examples,
+/// see the documentation for that struct and for the [`subscriber`]
+/// module.
+///
+/// [`subscriber`]: mod@crate::subscriber
pub struct MockSubscriber {
expected: Arc>>,
current: Mutex>,
@@ -60,6 +291,59 @@ impl MockSubscriberBuilder {
/// interactions between multiple subscribers. In that case, it can be
/// helpful to give each subscriber a separate name to distinguish where the
/// debugging output comes from.
+ ///
+ /// # Examples
+ ///
+ /// In the following example, we create two subscribers, both
+ /// expecting to receive an event. As we only record a single
+ /// event, the test will fail:
+ ///
+ /// ```should_panic
+ /// use tracing_mock::{subscriber, expect};
+ /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+ ///
+ /// let (subscriber_1, handle_1) = subscriber::mock()
+ /// .named("subscriber-1")
+ /// .event(expect::event())
+ /// .run_with_handle();
+ ///
+ /// let (subscriber_2, handle_2) = subscriber::mock()
+ /// .named("subscriber-2")
+ /// .event(expect::event())
+ /// .run_with_handle();
+ ///
+ /// let _collect = tracing_subscriber::registry()
+ /// .with(
+ /// subscriber_2.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true))
+ /// )
+ /// .set_default();
+ /// {
+ /// let _collect = tracing_subscriber::registry()
+ /// .with(
+ /// subscriber_1
+ /// .with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true))
+ /// )
+ /// .set_default();
+ ///
+ /// tracing::info!("a");
+ /// }
+ ///
+ /// handle_1.assert_finished();
+ /// handle_2.assert_finished();
+ /// ```
+ ///
+ /// In the test output, we see that the subscriber which didn't
+ /// received the event was the one named `subscriber-2`, which is
+ /// correct as the subscriber named `subscriber-1` was the default
+ /// when the event was recorded:
+ ///
+ /// ```text
+ /// [main::subscriber-2] more notifications expected: [
+ /// Event(
+ /// MockEvent,
+ /// ),
+ /// ]', tracing-mock/src/collector.rs:472:13
+ /// ```
pub fn named(mut self, name: impl fmt::Display) -> Self {
use std::fmt::Write;
if !self.name.is_empty() {
@@ -70,42 +354,379 @@ impl MockSubscriberBuilder {
self
}
- pub fn enter(mut self, span: ExpectedSpan) -> Self {
- self.expected.push_back(Expect::Enter(span));
- self
- }
-
+ /// Adds an expectation that an event matching the [`ExpectedEvent`]
+ /// will be recorded next.
+ ///
+ /// The `event` can be a default mock which will match any event
+ /// (`expect::event()`) or can include additional expectations.
+ /// See the [`ExpectedEvent`] documentation for more details.
+ ///
+ /// If an event is recorded that doesn't match the `ExpectedEvent`,
+ /// or if something else (such as entering a span) is recorded
+ /// first, then the expectation will fail.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tracing_mock::{expect, subscriber};
+ /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+ ///
+ /// let (subscriber, handle) = subscriber::mock()
+ /// .event(expect::event())
+ /// .run_with_handle();
+ ///
+ /// let _collect = tracing_subscriber::registry()
+ /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+ /// .set_default();
+ ///
+ /// tracing::info!("event");
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// A span is entered before the event, causing the test to fail:
+ ///
+ /// ```should_panic
+ /// use tracing_mock::{expect, subscriber};
+ /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+ ///
+ /// let (subscriber, handle) = subscriber::mock()
+ /// .event(expect::event())
+ /// .run_with_handle();
+ ///
+ /// let _collect = tracing_subscriber::registry()
+ /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+ /// .set_default();
+ ///
+ /// let span = tracing::info_span!("span");
+ /// let _guard = span.enter();
+ /// tracing::info!("event");
+ ///
+ /// handle.assert_finished();
+ /// ```
pub fn event(mut self, event: ExpectedEvent) -> Self {
self.expected.push_back(Expect::Event(event));
self
}
- pub fn exit(mut self, span: ExpectedSpan) -> Self {
- self.expected.push_back(Expect::Exit(span));
+ /// Adds an expectation that the creation of a span will be
+ /// recorded next.
+ ///
+ /// This function accepts `Into` instead of
+ /// [`ExpectedSpan`] directly. [`NewSpan`] can be used to test
+ /// span fields and the span parent.
+ ///
+ /// The new span doesn't need to be entered for this expectation
+ /// to succeed.
+ ///
+ /// If a span is recorded that doesn't match the `ExpectedSpan`,
+ /// or if something else (such as an event) is recorded first,
+ /// then the expectation will fail.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tracing_mock::{expect, subscriber};
+ /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+ ///
+ /// let span = expect::span()
+ /// .at_level(tracing::Level::INFO)
+ /// .named("the span we're testing")
+ /// .with_field(expect::field("testing").with_value(&"yes"));
+ /// let (subscriber, handle) = subscriber::mock()
+ /// .new_span(span)
+ /// .run_with_handle();
+ ///
+ /// let _collect = tracing_subscriber::registry()
+ /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+ /// .set_default();
+ ///
+ /// _ = tracing::info_span!("the span we're testing", testing = "yes");
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// An event is recorded before the span is created, causing the
+ /// test to fail:
+ ///
+ /// ```should_panic
+ /// use tracing_mock::{expect, subscriber};
+ /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+ ///
+ /// let span = expect::span()
+ /// .at_level(tracing::Level::INFO)
+ /// .named("the span we're testing")
+ /// .with_field(expect::field("testing").with_value(&"yes"));
+ /// let (subscriber, handle) = subscriber::mock()
+ /// .new_span(span)
+ /// .run_with_handle();
+ ///
+ /// let _collect = tracing_subscriber::registry()
+ /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+ /// .set_default();
+ ///
+ /// tracing::info!("an event");
+ /// _ = tracing::info_span!("the span we're testing", testing = "yes");
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// [`ExpectedSpan`]: struct@crate::span::ExpectedSpan
+ /// [`NewSpan`]: struct@crate::span::NewSpan
+ pub fn new_span(mut self, new_span: I) -> Self
+ where
+ I: Into,
+ {
+ self.expected.push_back(Expect::NewSpan(new_span.into()));
self
}
- pub fn only(mut self) -> Self {
- self.expected.push_back(Expect::Nothing);
+ /// Adds an expectation that entering a span matching the
+ /// [`ExpectedSpan`] will be recorded next.
+ ///
+ /// This expectation is generally accompanied by a call to
+ /// [`exit`], since an entered span will typically be exited. If used
+ /// together with [`only`], this is likely necessary, because the span
+ /// will be dropped before the test completes (except in rare cases,
+ /// such as if [`std::mem::forget`] is used).
+ ///
+ /// If the span that is entered doesn't match the [`ExpectedSpan`],
+ /// or if something else (such as an event) is recorded first,
+ /// then the expectation will fail.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tracing_mock::{expect, subscriber};
+ /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+ ///
+ /// let span = expect::span()
+ /// .at_level(tracing::Level::INFO)
+ /// .named("the span we're testing");
+ /// let (subscriber, handle) = subscriber::mock()
+ /// .enter(span.clone())
+ /// .exit(span)
+ /// .only()
+ /// .run_with_handle();
+ ///
+ /// let _collect = tracing_subscriber::registry()
+ /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+ /// .set_default();
+ ///
+ /// {
+ /// let span = tracing::info_span!("the span we're testing");
+ /// let _entered = span.enter();
+ /// }
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// An event is recorded before the span is entered, causing the
+ /// test to fail:
+ ///
+ /// ```should_panic
+ /// use tracing_mock::{expect, subscriber};
+ /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+ ///
+ /// let span = expect::span()
+ /// .at_level(tracing::Level::INFO)
+ /// .named("the span we're testing");
+ /// let (subscriber, handle) = subscriber::mock()
+ /// .enter(span.clone())
+ /// .exit(span)
+ /// .only()
+ /// .run_with_handle();
+ ///
+ /// let _collect = tracing_subscriber::registry()
+ /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+ /// .set_default();
+ ///
+ /// {
+ /// tracing::info!("an event");
+ /// let span = tracing::info_span!("the span we're testing");
+ /// let _entered = span.enter();
+ /// }
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// [`exit`]: fn@Self::exit
+ /// [`only`]: fn@Self::only
+ pub fn enter(mut self, span: ExpectedSpan) -> Self {
+ self.expected.push_back(Expect::Enter(span));
self
}
- pub fn record(mut self, span: ExpectedSpan, fields: I) -> Self
- where
- I: Into,
- {
- self.expected.push_back(Expect::Visit(span, fields.into()));
+ /// Adds an expectation that exiting a span matching the
+ /// [`ExpectedSpan`] will be recorded next.
+ ///
+ /// As a span may be entered and exited multiple times,
+ /// this is different from the span being closed. In
+ /// general [`enter`] and `exit` should be paired.
+ ///
+ /// If the span that is exited doesn't match the [`ExpectedSpan`],
+ /// or if something else (such as an event) is recorded first,
+ /// then the expectation will fail.
+ ///
+ /// **Note**: Ensure that the guard returned by [`Span::enter`]
+ /// is dropped before calling [`MockHandle::assert_finished`].
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tracing_mock::{expect, subscriber};
+ /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+ ///
+ /// let span = expect::span()
+ /// .at_level(tracing::Level::INFO)
+ /// .named("the span we're testing");
+ /// let (subscriber, handle) = subscriber::mock()
+ /// .enter(span.clone())
+ /// .exit(span)
+ /// .only()
+ /// .run_with_handle();
+ ///
+ /// let _collect = tracing_subscriber::registry()
+ /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+ /// .set_default();
+ /// {
+ /// let span = tracing::info_span!("the span we're testing");
+ /// let _entered = span.enter();
+ /// }
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// An event is recorded before the span is exited, causing the
+ /// test to fail:
+ ///
+ /// ```should_panic
+ /// use tracing_mock::{expect, subscriber};
+ /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+ ///
+ /// let span = expect::span()
+ /// .at_level(tracing::Level::INFO)
+ /// .named("the span we're testing");
+ /// let (subscriber, handle) = subscriber::mock()
+ /// .enter(span.clone())
+ /// .exit(span)
+ /// .only()
+ /// .run_with_handle();
+ ///
+ /// let _collect = tracing_subscriber::registry()
+ /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+ /// .set_default();
+ ///
+ /// {
+ /// let span = tracing::info_span!("the span we're testing");
+ /// let _entered = span.enter();
+ /// tracing::info!("an event");
+ /// }
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// [`enter`]: fn@Self::enter
+ /// [`MockHandle::assert_finished`]: fn@crate::collector::MockHandle::assert_finished
+ /// [`Span::enter`]: fn@tracing::Span::enter
+ pub fn exit(mut self, span: ExpectedSpan) -> Self {
+ self.expected.push_back(Expect::Exit(span));
self
}
- pub fn new_span(mut self, new_span: I) -> Self
- where
- I: Into,
- {
- self.expected.push_back(Expect::NewSpan(new_span.into()));
+ /// Expects that no further traces are received.
+ ///
+ /// The call to `only` should appear immediately before the final
+ /// call to [`run`] or [`run_with_handle`], as any expectations which
+ /// are added after `only` will not be considered.
+ ///
+ /// # Examples
+ ///
+ /// Consider this simple test. It passes even though we only
+ /// expect a single event, but receive three:
+ ///
+ /// ```
+ /// use tracing_mock::{expect, subscriber};
+ /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+ ///
+ /// let (subscriber, handle) = subscriber::mock()
+ /// .event(expect::event())
+ /// .run_with_handle();
+ ///
+ /// let _collect = tracing_subscriber::registry()
+ /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+ /// .set_default();
+ ///
+ /// tracing::info!("a");
+ /// tracing::info!("b");
+ /// tracing::info!("c");
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// After including `only`, the test will fail:
+ ///
+ /// ```should_panic
+ /// use tracing_mock::{expect, subscriber};
+ /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+ ///
+ /// let (subscriber, handle) = subscriber::mock()
+ /// .event(expect::event())
+ /// .only()
+ /// .run_with_handle();
+ ///
+ /// let _collect = tracing_subscriber::registry()
+ /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+ /// .set_default();
+ ///
+ /// tracing::info!("a");
+ /// tracing::info!("b");
+ /// tracing::info!("c");
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// [`run`]: fn@Self::run
+ /// [`run_with_handle`]: fn@Self::run_with_handle
+ pub fn only(mut self) -> Self {
+ self.expected.push_back(Expect::Nothing);
self
}
+ /// Consume this builder and return a [`MockSubscriber`] which can
+ /// be set as the default subscriber.
+ ///
+ /// This function is similar to [`run_with_handle`], but it doesn't
+ /// return a [`MockHandle`]. This is useful if the desired
+ /// assertions can be checked externally to the subscriber.
+ ///
+ /// # Examples
+ ///
+ /// The following test is used within the `tracing-subscriber`
+ /// codebase:
+ ///
+ /// ```
+ /// use tracing::Collect;
+ /// use tracing_mock::subscriber;
+ /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+ ///
+ /// let unfiltered = subscriber::named("unfiltered").run().boxed();
+ /// let info = subscriber::named("info")
+ /// .run()
+ /// .with_filter(tracing_core::LevelFilter::INFO)
+ /// .boxed();
+ /// let debug = subscriber::named("debug")
+ /// .run()
+ /// .with_filter(tracing_core::LevelFilter::DEBUG)
+ /// .boxed();
+ ///
+ /// let collector = tracing_subscriber::registry().with(vec![unfiltered, info, debug]);
+ ///
+ /// assert_eq!(collector.max_level_hint(), None);
+ /// ```
+ ///
+ /// [`MockHandle`]: struct@crate::collector::MockHandle
+ /// [`run_with_handle`]: fn@Self::run_with_handle
pub fn run(self) -> MockSubscriber {
MockSubscriber {
expected: Arc::new(Mutex::new(self.expected)),
@@ -114,6 +735,31 @@ impl MockSubscriberBuilder {
}
}
+ /// Consume this builder and return a [`MockSubscriber`] which can
+ /// be set as the default subscriber and a [`MockHandle`] which can
+ /// be used to validate the provided expectations.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use tracing_mock::{expect, subscriber};
+ /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
+ ///
+ /// let (subscriber, handle) = subscriber::mock()
+ /// .event(expect::event())
+ /// .run_with_handle();
+ ///
+ /// let _collect = tracing_subscriber::registry()
+ /// .with(subscriber.with_filter(tracing_subscriber::filter::filter_fn(move |_meta| true)))
+ /// .set_default();
+ ///
+ /// tracing::info!("event");
+ ///
+ /// handle.assert_finished();
+ /// ```
+ ///
+ /// [`MockHandle`]: struct@crate::collector::MockHandle
+ /// [`MockSubscriber`]: struct@crate::subscriber::MockSubscriber
pub fn run_with_handle(self) -> (MockSubscriber, MockHandle) {
let expected = Arc::new(Mutex::new(self.expected));
let handle = MockHandle::new(expected.clone(), self.name.clone());
@@ -185,6 +831,46 @@ impl MockSubscriber {
);
}
}
+
+ fn check_event_scope(
+ &self,
+ current_scope: Option>,
+ expected_scope: &mut [ExpectedSpan],
+ ) where
+ C: for<'lookup> tracing_subscriber::registry::LookupSpan<'lookup>,
+ {
+ let mut current_scope = current_scope.into_iter().flatten();
+ let mut i = 0;
+ for (expected, actual) in expected_scope.iter_mut().zip(&mut current_scope) {
+ println!(
+ "[{}] event_scope[{}] actual={} ({:?}); expected={}",
+ self.name,
+ i,
+ actual.name(),
+ actual.id(),
+ expected
+ );
+ self.check_span_ref(
+ expected,
+ &actual,
+ format_args!("the {}th span in the event's scope to be", i),
+ );
+ i += 1;
+ }
+ let remaining_expected = &expected_scope[i..];
+ assert!(
+ remaining_expected.is_empty(),
+ "\n[{}] did not observe all expected spans in event scope!\n[{}] missing: {:#?}",
+ self.name,
+ self.name,
+ remaining_expected,
+ );
+ assert!(
+ current_scope.next().is_none(),
+ "\n[{}] did not expect all spans in the actual event scope!",
+ self.name,
+ );
+ }
}
impl Subscribe for MockSubscriber
@@ -221,45 +907,21 @@ where
Some(Expect::Event(mut expected)) => {
let get_parent_name = || cx.event_span(event).map(|span| span.name().to_string());
expected.check(event, get_parent_name, &self.name);
- let mut current_scope = cx.event_scope(event).into_iter().flatten();
- let expected_scope = expected.scope_mut();
- let mut i = 0;
- for (expected, actual) in expected_scope.iter_mut().zip(&mut current_scope) {
- println!(
- "[{}] event_scope[{}] actual={} ({:?}); expected={}",
- self.name,
- i,
- actual.name(),
- actual.id(),
- expected
- );
- self.check_span_ref(
- expected,
- &actual,
- format_args!("the {}th span in the event's scope to be", i),
- );
- i += 1;
+
+ if let Some(expected_scope) = expected.scope_mut() {
+ self.check_event_scope(cx.event_scope(event), expected_scope);
}
- let remaining_expected = &expected_scope[i..];
- assert!(
- remaining_expected.is_empty(),
- "\n[{}] did not observe all expected spans in event scope!\n[{}] missing: {:#?}",
- self.name,
- self.name,
- remaining_expected,
- );
- assert!(
- current_scope.next().is_none(),
- "\n[{}] did not expect all spans in the actual event scope!",
- self.name,
- );
}
Some(ex) => ex.bad(&self.name, format_args!("observed event {:#?}", event)),
}
}
fn on_follows_from(&self, _span: &Id, _follows: &Id, _: Context<'_, C>) {
- // TODO: it should be possible to expect spans to follow from other spans
+ unimplemented!(
+ "so far, we don't have any tests that need an `on_follows_from` \
+ implementation.\nif you just wrote one that does, feel free to \
+ implement it!"
+ );
}
fn on_new_span(&self, span: &Attributes<'_>, id: &Id, cx: Context<'_, C>) {
diff --git a/tracing-opentelemetry/CHANGELOG.md b/tracing-opentelemetry/CHANGELOG.md
deleted file mode 100644
index 9cfb38def4..0000000000
--- a/tracing-opentelemetry/CHANGELOG.md
+++ /dev/null
@@ -1,167 +0,0 @@
-# 0.16.0 (January 30, 2022)
-
-### Breaking Changes
-
-- Upgrade to `v0.17.0` of `opentelemetry` (#1853)
- For list of breaking changes in OpenTelemetry, see the
- [v0.17.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0170).
-# 0.15.0 (August 7, 2021)
-
-### Breaking Changes
-
-- Upgrade to `v0.16.0` of `opentelemetry` (#1497)
- For list of breaking changes in OpenTelemetry, see the
- [v0.16.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0160).
-
-# 0.14.0 (July 9, 2021)
-
-### Breaking Changes
-
-- Upgrade to `v0.15.0` of `opentelemetry` (#1441)
- For list of breaking changes in OpenTelemetry, see the
- [v0.15.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0150).
-
-# 0.13.0 (May 15, 2021)
-
-### Breaking Changes
-
-- Upgrade to `v0.14.0` of `opentelemetry` (#1394)
- For list of breaking changes in OpenTelemetry, see the
- [v0.14.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0140).
-
-# 0.12.0 (March 31, 2021)
-
-### Breaking Changes
-
-- Upgrade to `v0.13.0` of `opentelemetry` (#1322)
- For list of breaking changes in OpenTelemetry, see the
- [v0.13.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0130).
-
-### Changed
-
-- Improve performance when tracked inactivity is disabled (#1315)
-
-# 0.11.0 (January 25, 2021)
-
-### Breaking Changes
-
-- Upgrade to `v0.12.0` of `opentelemetry` (#1200)
- For list of breaking changes in OpenTelemetry, see the
- [v0.12.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0120).
-
-# 0.10.0 (December 30, 2020)
-
-### Breaking Changes
-
-- Upgrade to `v0.11.0` of `opentelemetry` (#1161)
- For list of breaking changes in OpenTelemetry, see the
- [v0.11.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/master/opentelemetry/CHANGELOG.md#v0110).
-- Update `OpenTelemetrySpanExt::set_parent` to take a context by value as it is
- now stored and propagated. (#1161)
-- Rename `PreSampledTracer::sampled_span_context` to
- `PreSampledTracer::sampled_context` as it now returns a full otel context. (#1161)
-
-# 0.9.0 (November 13, 2020)
-
-### Added
-
-- Track busy/idle timings as attributes via `with_tracked_inactivity` (#1096)
-
-### Breaking Changes
-
-- Upgrade to `v0.10.0` of `opentelemetry` (#1049)
- For list of breaking changes in OpenTelemetry, see the
- [v0.10.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/master/opentelemetry/CHANGELOG.md#v0100).
-
-# 0.8.0 (October 13, 2020)
-
-### Added
-
-- Implement additional record types (bool, i64, u64) (#1007)
-
-### Breaking changes
-
-- Add `PreSampledTracer` interface, removes need to specify sampler (#962)
-
-### Fixed
-
-- Connect external traces (#956)
-- Assign default ids if missing (#1027)
-
-# 0.7.0 (August 14, 2020)
-
-### Breaking Changes
-
-- Upgrade to `v0.8.0` of `opentelemetry` (#932)
- For list of breaking changes in OpenTelemetry, see the
- [v0.8.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/master/CHANGELOG.md#v080).
-
-# 0.6.0 (August 4, 2020)
-
-### Breaking Changes
-
-- Upgrade to `v0.7.0` of `opentelemetry` (#867)
- For list of breaking changes in OpenTelemetry, see the
- [v0.7.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/master/CHANGELOG.md#v070).
-
-# 0.5.0 (June 2, 2020)
-
-### Added
-
-- Support `tracing-log` special values (#735)
-- Support `Span::follows_from` creating otel span links (#723)
-- Dynamic otel span names via `otel.name` field (#732)
-
-### Breaking Changes
-
-- Upgrade to `v0.6.0` of `opentelemetry` (#745)
-
-### Fixed
-
-- Filter out invalid parent contexts when building span contexts (#743)
-
-# 0.4.0 (May 12, 2020)
-
-### Added
-
-- `tracing_opentelemetry::layer()` method to construct a default layer.
-- `OpenTelemetryLayer::with_sampler` method to configure the opentelemetry
- sampling behavior.
-- `OpenTelemetryLayer::new` method to configure both the tracer and sampler.
-
-### Breaking Changes
-
-- `OpenTelemetrySpanExt::set_parent` now accepts a reference to an extracted
- parent `Context` instead of a `SpanContext` to match propagators.
-- `OpenTelemetrySpanExt::context` now returns a `Context` instead of a
- `SpanContext` to match propagators.
-- `OpenTelemetryLayer::with_tracer` now takes `&self` as a parameter
-- Upgrade to `v0.5.0` of `opentelemetry`.
-
-### Fixed
-
-- Fixes bug where child spans were always marked as sampled
-
-# 0.3.1 (April 19, 2020)
-
-### Added
-
-- Change span status code to unknown on error event
-
-# 0.3.0 (April 5, 2020)
-
-### Added
-
-- Span extension for injecting and extracting `opentelemetry` span contexts
- into `tracing` spans
-
-### Removed
-
-- Disabled the `metrics` feature of the opentelemetry as it is unused.
-
-# 0.2.0 (February 7, 2020)
-
-### Changed
-
-- Update `tracing-subscriber` to 0.2.0 stable
-- Update to `opentelemetry` 0.2.0
diff --git a/tracing-opentelemetry/Cargo.toml b/tracing-opentelemetry/Cargo.toml
deleted file mode 100644
index 868f0a2d91..0000000000
--- a/tracing-opentelemetry/Cargo.toml
+++ /dev/null
@@ -1,55 +0,0 @@
-[package]
-name = "tracing-opentelemetry"
-version = "0.16.0"
-authors = [
- "Julian Tescher ",
- "Tokio Contributors "
-]
-description = "OpenTelemetry integration for tracing"
-homepage = "https://github.com/tokio-rs/tracing/tree/master/tracing-opentelemetry"
-repository = "https://github.com/tokio-rs/tracing"
-readme = "README.md"
-categories = [
- "development-tools::debugging",
- "development-tools::profiling",
- "asynchronous",
-]
-keywords = ["tracing", "opentelemetry", "jaeger", "zipkin", "async"]
-license = "MIT"
-edition = "2018"
-rust-version = "1.56.0"
-
-[features]
-default = ["tracing-log", "metrics"]
-# Enables support for exporting OpenTelemetry metrics
-metrics = ["opentelemetry/metrics"]
-
-[dependencies]
-opentelemetry = { version = "0.18.0", default-features = false, features = ["trace"] }
-tracing = { path = "../tracing", version = "0.2", default-features = false, features = ["std"] }
-tracing-core = { path = "../tracing-core", version = "0.2" }
-tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", default-features = false, features = ["registry", "std"] }
-tracing-log = { path = "../tracing-log", version = "0.2", default-features = false, optional = true }
-once_cell = "1.13.0"
-
-# Fix minimal-versions; opentelemetry specifies async-trait = "0.1" which breaks
-async-trait = "0.1.20"
-
-[dev-dependencies]
-async-trait = "0.1.56"
-criterion = { version = "0.3.6", default_features = false }
-opentelemetry-jaeger = "0.17.0"
-futures-util = { version = "0.3.21", default-features = false }
-tokio = { version = "1.20.0", features = ["full"] }
-tokio-stream = "0.1.9"
-
-[lib]
-bench = false
-
-[[bench]]
-name = "trace"
-harness = false
-
-[package.metadata.docs.rs]
-all-features = true
-rustdoc-args = ["--cfg", "docsrs"]
diff --git a/tracing-opentelemetry/LICENSE b/tracing-opentelemetry/LICENSE
deleted file mode 100644
index cdb28b4b56..0000000000
--- a/tracing-opentelemetry/LICENSE
+++ /dev/null
@@ -1,25 +0,0 @@
-Copyright (c) 2019 Tokio Contributors
-
-Permission is hereby granted, free of charge, to any
-person obtaining a copy of this software and associated
-documentation files (the "Software"), to deal in the
-Software without restriction, including without
-limitation the rights to use, copy, modify, merge,
-publish, distribute, sublicense, and/or sell copies of
-the Software, and to permit persons to whom the Software
-is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice
-shall be included in all copies or substantial portions
-of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
-ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
-TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
-PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
-SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
-IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
diff --git a/tracing-opentelemetry/README.md b/tracing-opentelemetry/README.md
deleted file mode 100644
index 796b2386cf..0000000000
--- a/tracing-opentelemetry/README.md
+++ /dev/null
@@ -1,132 +0,0 @@
-![Tracing — Structured, application-level diagnostics][splash]
-
-[splash]: https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/splash.svg
-
-# Tracing OpenTelemetry
-
-Utilities for adding [OpenTelemetry] interoperability to [`tracing`].
-
-[![Crates.io][crates-badge]][crates-url]
-[![Documentation][docs-badge]][docs-url]
-[![Documentation (master)][docs-master-badge]][docs-master-url]
-[![MIT licensed][mit-badge]][mit-url]
-[![Build Status][actions-badge]][actions-url]
-[![Discord chat][discord-badge]][discord-url]
-![maintenance status][maint-badge]
-
-[Documentation][docs-url] | [Chat][discord-url]
-
-[crates-badge]: https://img.shields.io/crates/v/tracing-opentelemetry.svg
-[crates-url]: https://crates.io/crates/tracing-opentelemetry/0.16.0
-[docs-badge]: https://docs.rs/tracing-opentelemetry/badge.svg
-[docs-url]: https://docs.rs/tracing-opentelemetry/0.16.0/tracing_opentelemetry
-[docs-master-badge]: https://img.shields.io/badge/docs-master-blue
-[docs-master-url]: https://tracing-rs.netlify.com/tracing_opentelemetry
-[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
-[mit-url]: LICENSE
-[actions-badge]: https://github.com/tokio-rs/tracing/workflows/CI/badge.svg
-[actions-url]:https://github.com/tokio-rs/tracing/actions?query=workflow%3ACI
-[discord-badge]: https://img.shields.io/discord/500028886025895936?logo=discord&label=discord&logoColor=white
-[discord-url]: https://discord.gg/EeF3cQw
-[maint-badge]: https://img.shields.io/badge/maintenance-actively--developed-brightgreen.svg
-
-## Overview
-
-[`tracing`] is a framework for instrumenting Rust programs to collect
-structured, event-based diagnostic information. This crate provides a
-subscriber that connects spans from multiple systems into a trace and
-emits them to [OpenTelemetry]-compatible distributed tracing systems
-for processing and visualization.
-
-The crate provides the following types:
-
-* [`OpenTelemetrySubscriber`] adds OpenTelemetry context to all `tracing` [span]s.
-* [`OpenTelemetrySpanExt`] allows OpenTelemetry parent trace information to be
- injected and extracted from a `tracing` [span].
-
-[`OpenTelemetrySubscriber`]: https://docs.rs/tracing-opentelemetry/latest/tracing_opentelemetry/struct.OpenTelemetrySubscriber.html
-[`OpenTelemetrySpanExt`]: https://docs.rs/tracing-opentelemetry/latest/tracing_opentelemetry/trait.OpenTelemetrySpanExt.html
-[span]: https://docs.rs/tracing/latest/tracing/span/index.html
-[`tracing`]: https://crates.io/crates/tracing
-[OpenTelemetry]: https://opentelemetry.io/
-
-*Compiler support: [requires `rustc` 1.56+][msrv]*
-
-[msrv]: #supported-rust-versions
-
-## Examples
-
-### Basic Usage
-
-```rust
-use opentelemetry::exporter::trace::stdout;
-use tracing::{error, span};
-use tracing_subscriber::subscriber::SubscriberExt;
-use tracing_subscriber::Registry;
-
-fn main() {
- // Install a new OpenTelemetry trace pipeline
- let (tracer, _uninstall) = stdout::new_pipeline().install();
-
- // Create a tracing subscriber with the configured tracer
- let telemetry = tracing_opentelemetry::subscriber().with_tracer(tracer);
-
- // Use the tracing subscriber `Registry`, or any other subscriber
- // that impls `LookupSpan`
- let collector = Registry::default().with(telemetry);
-
- // Trace executed code
- tracing::collect::with_default(collector, || {
- // Spans will be sent to the configured OpenTelemetry exporter
- let root = span!(tracing::Level::TRACE, "app_start", work_units = 2);
- let _enter = root.enter();
-
- error!("This event will be logged in the root span.");
- });
-}
-```
-
-### Visualization example
-
-```console
-# Run a supported collector like jaeger in the background
-$ docker run -d -p6831:6831/udp -p6832:6832/udp -p16686:16686 jaegertracing/all-in-one:latest
-
-# Run example to produce spans (from parent examples directory)
-$ cargo run --example opentelemetry
-
-# View spans (see the image below)
-$ firefox http://localhost:16686/
-```
-
-![Jaeger UI](trace.png)
-
-## Feature Flags
-
- - `metrics`: Enables the [`MetricsSubscriber`] type, a [subscriber] that
- exports OpenTelemetry metrics from specifically-named events. This enables
- the `metrics` feature flag on the `opentelemetry` crate.
-
-## Supported Rust Versions
-
-Tracing Opentelemetry is built against the latest stable release. The minimum
-supported version is 1.56. The current Tracing version is not guaranteed to
-build on Rust versions earlier than the minimum supported version.
-
-Tracing follows the same compiler support policies as the rest of the Tokio
-project. The current stable Rust compiler and the three most recent minor
-versions before it will always be supported. For example, if the current stable
-compiler version is 1.45, the minimum supported version will not be increased
-past 1.42, three minor versions prior. Increasing the minimum supported compiler
-version is not considered a semver breaking change as long as doing so complies
-with this policy.
-
-## License
-
-This project is licensed under the [MIT license](LICENSE).
-
-### Contribution
-
-Unless you explicitly state otherwise, any contribution intentionally submitted
-for inclusion in Tracing by you, shall be licensed as MIT, without any additional
-terms or conditions.
diff --git a/tracing-opentelemetry/benches/trace.rs b/tracing-opentelemetry/benches/trace.rs
deleted file mode 100644
index 13d12554fe..0000000000
--- a/tracing-opentelemetry/benches/trace.rs
+++ /dev/null
@@ -1,126 +0,0 @@
-use criterion::{criterion_group, criterion_main, Criterion};
-use opentelemetry::{
- sdk::trace::{Tracer, TracerProvider},
- trace::{SpanBuilder, Tracer as _, TracerProvider as _},
- Context,
-};
-use std::time::SystemTime;
-use tracing::trace_span;
-use tracing_subscriber::prelude::*;
-
-fn many_children(c: &mut Criterion) {
- let mut group = c.benchmark_group("otel_many_children");
-
- group.bench_function("spec_baseline", |b| {
- let provider = TracerProvider::default();
- let tracer = provider.tracer("bench");
- b.iter(|| {
- fn dummy(tracer: &Tracer, cx: &Context) {
- for _ in 0..99 {
- tracer.start_with_context("child", cx);
- }
- }
-
- tracer.in_span("parent", |cx| dummy(&tracer, &cx));
- });
- });
-
- {
- let _subscriber = tracing_subscriber::registry()
- .with(RegistryAccessCollector)
- .set_default();
- group.bench_function("no_data_baseline", |b| b.iter(tracing_harness));
- }
-
- {
- let _subscriber = tracing_subscriber::registry()
- .with(OtelDataCollector)
- .set_default();
- group.bench_function("data_only_baseline", |b| b.iter(tracing_harness));
- }
-
- {
- let provider = TracerProvider::default();
- let tracer = provider.tracer("bench");
- let otel_layer = tracing_opentelemetry::subscriber()
- .with_tracer(tracer)
- .with_tracked_inactivity(false);
- let _subscriber = tracing_subscriber::registry()
- .with(otel_layer)
- .set_default();
-
- group.bench_function("full", |b| b.iter(tracing_harness));
- }
-}
-
-struct NoDataSpan;
-struct RegistryAccessCollector;
-
-impl tracing_subscriber::Subscribe for RegistryAccessCollector
-where
- C: tracing_core::Collect + for<'span> tracing_subscriber::registry::LookupSpan<'span>,
-{
- fn on_new_span(
- &self,
- _attrs: &tracing_core::span::Attributes<'_>,
- id: &tracing::span::Id,
- ctx: tracing_subscriber::subscribe::Context<'_, C>,
- ) {
- let span = ctx.span(id).expect("Span not found, this is a bug");
- let mut extensions = span.extensions_mut();
- extensions.insert(NoDataSpan);
- }
-
- fn on_close(&self, id: tracing::span::Id, ctx: tracing_subscriber::subscribe::Context<'_, C>) {
- let span = ctx.span(&id).expect("Span not found, this is a bug");
- let mut extensions = span.extensions_mut();
-
- extensions.remove::();
- }
-}
-
-struct OtelDataCollector;
-
-impl tracing_subscriber::Subscribe for OtelDataCollector
-where
- C: tracing_core::Collect + for<'span> tracing_subscriber::registry::LookupSpan<'span>,
-{
- fn on_new_span(
- &self,
- attrs: &tracing_core::span::Attributes<'_>,
- id: &tracing::span::Id,
- ctx: tracing_subscriber::subscribe::Context<'_, C>,
- ) {
- let span = ctx.span(id).expect("Span not found, this is a bug");
- let mut extensions = span.extensions_mut();
- extensions.insert(
- SpanBuilder::from_name(attrs.metadata().name()).with_start_time(SystemTime::now()),
- );
- }
-
- fn on_close(&self, id: tracing::span::Id, ctx: tracing_subscriber::subscribe::Context<'_, C>) {
- let span = ctx.span(&id).expect("Span not found, this is a bug");
- let mut extensions = span.extensions_mut();
-
- if let Some(builder) = extensions.remove::() {
- builder.with_end_time(SystemTime::now());
- }
- }
-}
-
-fn tracing_harness() {
- fn dummy() {
- for _ in 0..99 {
- let child = trace_span!("child");
- let _enter = child.enter();
- }
- }
-
- let parent = trace_span!("parent");
- let _enter = parent.enter();
-
- dummy();
-}
-
-criterion_group!(benches, many_children);
-criterion_main!(benches);
diff --git a/tracing-opentelemetry/src/lib.rs b/tracing-opentelemetry/src/lib.rs
deleted file mode 100644
index 61a496e535..0000000000
--- a/tracing-opentelemetry/src/lib.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-//! # Tracing OpenTelemetry
-//!
-//! [`tracing`] is a framework for instrumenting Rust programs to collect
-//! structured, event-based diagnostic information. This crate provides a layer
-//! that connects spans from multiple systems into a trace and emits them to
-//! [OpenTelemetry]-compatible distributed tracing systems for processing and
-//! visualization.
-//!
-//! [OpenTelemetry]: https://opentelemetry.io
-//! [`tracing`]: https://github.com/tokio-rs/tracing
-//!
-//! *Compiler support: [requires `rustc` 1.56+][msrv]*
-//!
-//! [msrv]: #supported-rust-versions
-//!
-//! ### Special Fields
-//!
-//! Fields with an `otel.` prefix are reserved for this crate and have specific
-//! meaning. They are treated as ordinary fields by other layers. The current
-//! special fields are:
-//!
-//! * `otel.name`: Override the span name sent to OpenTelemetry exporters.
-//! Setting this field is useful if you want to display non-static information
-//! in your span name.
-//! * `otel.kind`: Set the span kind to one of the supported OpenTelemetry [span kinds].
-//! * `otel.status_code`: Set the span status code to one of the supported OpenTelemetry [span status codes].
-//! * `otel.status_message`: Set the span status message.
-//!
-//! [span kinds]: https://docs.rs/opentelemetry/latest/opentelemetry/trace/enum.SpanKind.html
-//! [span status codes]: https://docs.rs/opentelemetry/latest/opentelemetry/trace/enum.StatusCode.html
-//!
-//! ### Semantic Conventions
-//!
-//! OpenTelemetry defines conventional names for attributes of common
-//! operations. These names can be assigned directly as fields, e.g.
-//! `trace_span!("request", "otel.kind" = %SpanKind::Client, "http.url" = ..)`, and they
-//! will be passed through to your configured OpenTelemetry exporter. You can
-//! find the full list of the operations and their expected field names in the
-//! [semantic conventions] spec.
-//!
-//! [semantic conventions]: https://github.com/open-telemetry/opentelemetry-specification/tree/master/specification/trace/semantic_conventions
-//!
-//! ### Stability Status
-//!
-//! The OpenTelemetry specification is currently in beta so some breaking
-//! changes may still occur on the path to 1.0. You can follow the changes via
-//! the [spec repository] to track progress toward stabilization.
-//!
-//! [spec repository]: https://github.com/open-telemetry/opentelemetry-specification
-//!
-//! ## Examples
-//!
-//! ```
-//! use opentelemetry::sdk::export::trace::stdout;
-//! use tracing::{error, span};
-//! use tracing_subscriber::subscribe::CollectExt;
-//! use tracing_subscriber::Registry;
-//!
-//! // Create a new OpenTelemetry pipeline
-//! let tracer = stdout::new_pipeline().install_simple();
-//!
-//! // Create a tracing layer with the configured tracer
-//! let telemetry = tracing_opentelemetry::subscriber().with_tracer(tracer);
-//!
-//! // Use the tracing subscriber `Registry`, or any other subscriber
-//! // that impls `LookupSpan`
-//! let subscriber = Registry::default().with(telemetry);
-//!
-//! // Trace executed code
-//! tracing::collect::with_default(subscriber, || {
-//! // Spans will be sent to the configured OpenTelemetry exporter
-//! let root = span!(tracing::Level::TRACE, "app_start", work_units = 2);
-//! let _enter = root.enter();
-//!
-//! error!("This event will be logged in the root span.");
-//! });
-//! ```
-//!
-//! ## Feature Flags
-//!
-//! - `metrics`: Enables the [`MetricsSubscriber`] type, a [subscriber] that
-//! exports OpenTelemetry metrics from specifically-named events. This enables
-//! the `metrics` feature flag on the `opentelemetry` crate. *Enabled by
-//! default*.
-//!
-//! ## Supported Rust Versions
-//!
-//! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.56. The current Tracing version is not guaranteed to build on
-//! Rust versions earlier than the minimum supported version.
-//!
-//! Tracing follows the same compiler support policies as the rest of the Tokio
-//! project. The current stable Rust compiler and the three most recent minor
-//! versions before it will always be supported. For example, if the current
-//! stable compiler version is 1.45, the minimum supported version will not be
-//! increased past 1.42, three minor versions prior. Increasing the minimum
-//! supported compiler version is not considered a semver breaking change as
-//! long as doing so complies with this policy.
-//!
-//! [subscriber]: tracing_subscriber::subscribe
-#![deny(unreachable_pub)]
-#![cfg_attr(test, deny(warnings))]
-#![doc(
- html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png",
- html_favicon_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/favicon.ico",
- issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/"
-)]
-#![cfg_attr(
- docsrs,
- // Allows displaying cfgs/feature flags in the documentation.
- feature(doc_cfg, doc_auto_cfg),
- // Allows adding traits to RustDoc's list of "notable traits"
- feature(doc_notable_trait),
- // Fail the docs build if any intra-docs links are broken
- deny(rustdoc::broken_intra_doc_links),
-)]
-
-/// Implementation of the trace::Subscriber trait; publishes OpenTelemetry metrics.
-#[cfg(feature = "metrics")]
-mod metrics;
-/// Span extension which enables OpenTelemetry context management.
-mod span_ext;
-/// Implementation of the trace::Subscriber trait; publishes OpenTelemetry traces.
-mod subscriber;
-/// Protocols for OpenTelemetry Tracers that are compatible with Tracing
-mod tracer;
-
-#[cfg(feature = "metrics")]
-pub use metrics::MetricsSubscriber;
-pub use span_ext::OpenTelemetrySpanExt;
-pub use subscriber::{subscriber, OpenTelemetrySubscriber};
-pub use tracer::PreSampledTracer;
-
-/// Per-span OpenTelemetry data tracked by this crate.
-///
-/// Useful for implementing [PreSampledTracer] in alternate otel SDKs.
-#[derive(Debug, Clone)]
-pub struct OtelData {
- /// The parent otel `Context` for the current tracing span.
- pub parent_cx: opentelemetry::Context,
-
- /// The otel span data recorded during the current tracing span.
- pub builder: opentelemetry::trace::SpanBuilder,
-}
diff --git a/tracing-opentelemetry/src/metrics.rs b/tracing-opentelemetry/src/metrics.rs
deleted file mode 100644
index 9a87eea769..0000000000
--- a/tracing-opentelemetry/src/metrics.rs
+++ /dev/null
@@ -1,366 +0,0 @@
-use std::{collections::HashMap, fmt, sync::RwLock};
-use tracing::{field::Visit, Collect};
-use tracing_core::Field;
-
-use opentelemetry::{
- metrics::{Counter, Histogram, Meter, MeterProvider, UpDownCounter},
- sdk::metrics::controllers::BasicController,
- Context as OtelContext,
-};
-use tracing_subscriber::{registry::LookupSpan, subscribe::Context, Subscribe};
-
-const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
-const INSTRUMENTATION_LIBRARY_NAME: &str = "tracing/tracing-opentelemetry";
-
-const METRIC_PREFIX_MONOTONIC_COUNTER: &str = "monotonic_counter.";
-const METRIC_PREFIX_COUNTER: &str = "counter.";
-const METRIC_PREFIX_HISTOGRAM: &str = "histogram.";
-const I64_MAX: u64 = i64::MAX as u64;
-
-#[derive(Default)]
-pub(crate) struct Instruments {
- u64_counter: MetricsMap>,
- f64_counter: MetricsMap>,
- i64_up_down_counter: MetricsMap>,
- f64_up_down_counter: MetricsMap>,
- u64_histogram: MetricsMap>,
- i64_histogram: MetricsMap>,
- f64_histogram: MetricsMap>,
-}
-
-type MetricsMap = RwLock>;
-
-#[derive(Copy, Clone, Debug)]
-pub(crate) enum InstrumentType {
- CounterU64(u64),
- CounterF64(f64),
- UpDownCounterI64(i64),
- UpDownCounterF64(f64),
- HistogramU64(u64),
- HistogramI64(i64),
- HistogramF64(f64),
-}
-
-impl Instruments {
- pub(crate) fn update_metric(
- &self,
- cx: &OtelContext,
- meter: &Meter,
- instrument_type: InstrumentType,
- metric_name: &'static str,
- ) {
- fn update_or_insert(
- map: &MetricsMap,
- name: &'static str,
- insert: impl FnOnce() -> T,
- update: impl FnOnce(&T),
- ) {
- {
- let lock = map.read().unwrap();
- if let Some(metric) = lock.get(name) {
- update(metric);
- return;
- }
- }
-
- // that metric did not already exist, so we have to acquire a write lock to
- // create it.
- let mut lock = map.write().unwrap();
- // handle the case where the entry was created while we were waiting to
- // acquire the write lock
- let metric = lock.entry(name).or_insert_with(insert);
- update(metric)
- }
-
- match instrument_type {
- InstrumentType::CounterU64(value) => {
- update_or_insert(
- &self.u64_counter,
- metric_name,
- || meter.u64_counter(metric_name).init(),
- |ctr| ctr.add(cx, value, &[]),
- );
- }
- InstrumentType::CounterF64(value) => {
- update_or_insert(
- &self.f64_counter,
- metric_name,
- || meter.f64_counter(metric_name).init(),
- |ctr| ctr.add(cx, value, &[]),
- );
- }
- InstrumentType::UpDownCounterI64(value) => {
- update_or_insert(
- &self.i64_up_down_counter,
- metric_name,
- || meter.i64_up_down_counter(metric_name).init(),
- |ctr| ctr.add(cx, value, &[]),
- );
- }
- InstrumentType::UpDownCounterF64(value) => {
- update_or_insert(
- &self.f64_up_down_counter,
- metric_name,
- || meter.f64_up_down_counter(metric_name).init(),
- |ctr| ctr.add(cx, value, &[]),
- );
- }
- InstrumentType::HistogramU64(value) => {
- update_or_insert(
- &self.u64_histogram,
- metric_name,
- || meter.u64_histogram(metric_name).init(),
- |rec| rec.record(cx, value, &[]),
- );
- }
- InstrumentType::HistogramI64(value) => {
- update_or_insert(
- &self.i64_histogram,
- metric_name,
- || meter.i64_histogram(metric_name).init(),
- |rec| rec.record(cx, value, &[]),
- );
- }
- InstrumentType::HistogramF64(value) => {
- update_or_insert(
- &self.f64_histogram,
- metric_name,
- || meter.f64_histogram(metric_name).init(),
- |rec| rec.record(cx, value, &[]),
- );
- }
- };
- }
-}
-
-pub(crate) struct MetricVisitor<'a> {
- pub(crate) instruments: &'a Instruments,
- pub(crate) meter: &'a Meter,
-}
-
-impl<'a> Visit for MetricVisitor<'a> {
- fn record_debug(&mut self, _field: &Field, _value: &dyn fmt::Debug) {
- // Do nothing
- }
-
- fn record_u64(&mut self, field: &Field, value: u64) {
- let cx = OtelContext::current();
- if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) {
- self.instruments.update_metric(
- &cx,
- self.meter,
- InstrumentType::CounterU64(value),
- metric_name,
- );
- } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_COUNTER) {
- if value <= I64_MAX {
- self.instruments.update_metric(
- &cx,
- self.meter,
- InstrumentType::UpDownCounterI64(value as i64),
- metric_name,
- );
- } else {
- eprintln!(
- "[tracing-opentelemetry]: Received Counter metric, but \
- provided u64: {} is greater than i64::MAX. Ignoring \
- this metric.",
- value
- );
- }
- } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_HISTOGRAM) {
- self.instruments.update_metric(
- &cx,
- self.meter,
- InstrumentType::HistogramU64(value),
- metric_name,
- );
- }
- }
-
- fn record_f64(&mut self, field: &Field, value: f64) {
- let cx = OtelContext::current();
- if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) {
- self.instruments.update_metric(
- &cx,
- self.meter,
- InstrumentType::CounterF64(value),
- metric_name,
- );
- } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_COUNTER) {
- self.instruments.update_metric(
- &cx,
- self.meter,
- InstrumentType::UpDownCounterF64(value),
- metric_name,
- );
- } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_HISTOGRAM) {
- self.instruments.update_metric(
- &cx,
- self.meter,
- InstrumentType::HistogramF64(value),
- metric_name,
- );
- }
- }
-
- fn record_i64(&mut self, field: &Field, value: i64) {
- let cx = OtelContext::current();
- if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) {
- self.instruments.update_metric(
- &cx,
- self.meter,
- InstrumentType::CounterU64(value as u64),
- metric_name,
- );
- } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_COUNTER) {
- self.instruments.update_metric(
- &cx,
- self.meter,
- InstrumentType::UpDownCounterI64(value),
- metric_name,
- );
- } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_HISTOGRAM) {
- self.instruments.update_metric(
- &cx,
- self.meter,
- InstrumentType::HistogramI64(value),
- metric_name,
- );
- }
- }
-}
-
-/// A subscriber that publishes metrics via the OpenTelemetry SDK.
-///
-/// # Usage
-///
-/// No configuration is needed for this Subscriber, as it's only responsible for
-/// pushing data out to the `opentelemetry` family of crates. For example, when
-/// using `opentelemetry-otlp`, that crate will provide its own set of
-/// configuration options for setting up the duration metrics will be collected
-/// before exporting to the OpenTelemetry Collector, aggregation of data points,
-/// etc.
-///
-/// ```no_run
-/// use tracing_opentelemetry::MetricsSubscriber;
-/// use tracing_subscriber::subscribe::CollectExt;
-/// use tracing_subscriber::Registry;
-/// # use opentelemetry::sdk::metrics::controllers::BasicController;
-///
-/// // Constructing a BasicController is out-of-scope for the docs here, but there
-/// // are examples in the opentelemetry repository. See:
-/// // https://github.com/open-telemetry/opentelemetry-rust/blob/d4b9befea04bcc7fc19319a6ebf5b5070131c486/examples/basic-otlp/src/main.rs#L35-L52
-/// # let controller: BasicController = unimplemented!();
-///
-/// let opentelemetry_metrics = MetricsSubscriber::new(controller);
-/// let collector = Registry::default().with(opentelemetry_metrics);
-/// tracing::collect::set_global_default(collector).unwrap();
-/// ```
-///
-/// To publish a new metric, add a key-value pair to your `tracing::Event` that
-/// contains following prefixes:
-/// - `monotonic_counter.` (non-negative numbers): Used when the counter should
-/// only ever increase
-/// - `counter.`: Used when the counter can go up or down
-/// - `histogram.`: Used for discrete data points (i.e., summing them does not make
-/// semantic sense)
-///
-/// Examples:
-/// ```
-/// # use tracing::info;
-/// info!(monotonic_counter.foo = 1);
-/// info!(monotonic_counter.bar = 1.1);
-///
-/// info!(counter.baz = 1);
-/// info!(counter.baz = -1);
-/// info!(counter.xyz = 1.1);
-///
-/// info!(histogram.qux = 1);
-/// info!(histogram.abc = -1);
-/// info!(histogram.def = 1.1);
-/// ```
-///
-/// # Mixing data types
-///
-/// ## Floating-point numbers
-///
-/// Do not mix floating point and non-floating point numbers for the same
-/// metric. If a floating point number will be used for a given metric, be sure
-/// to cast any other usages of that metric to a floating point number.
-///
-/// Do this:
-/// ```
-/// # use tracing::info;
-/// info!(monotonic_counter.foo = 1_f64);
-/// info!(monotonic_counter.foo = 1.1);
-/// ```
-///
-/// This is because all data published for a given metric name must be the same
-/// numeric type.
-///
-/// ## Integers
-///
-/// Positive and negative integers can be mixed freely. The instrumentation
-/// provided by `tracing` assumes that all integers are `i64` unless explicitly
-/// cast to something else. In the case that an integer *is* cast to `u64`, this
-/// subscriber will handle the conversion internally.
-///
-/// For example:
-/// ```
-/// # use tracing::info;
-/// // The subscriber receives an i64
-/// info!(counter.baz = 1);
-///
-/// // The subscriber receives an i64
-/// info!(counter.baz = -1);
-///
-/// // The subscriber receives a u64, but casts it to i64 internally
-/// info!(counter.baz = 1_u64);
-///
-/// // The subscriber receives a u64, but cannot cast it to i64 because of
-/// // overflow. An error is printed to stderr, and the metric is dropped.
-/// info!(counter.baz = (i64::MAX as u64) + 1)
-/// ```
-///
-/// # Implementation Details
-///
-/// `MetricsSubscriber` holds a set of maps, with each map corresponding to a
-/// type of metric supported by OpenTelemetry. These maps are populated lazily.
-/// The first time that a metric is emitted by the instrumentation, a `Metric`
-/// instance will be created and added to the corresponding map. This means that
-/// any time a metric is emitted by the instrumentation, one map lookup has to
-/// be performed.
-///
-/// In the future, this can be improved by associating each `Metric` instance to
-/// its callsite, eliminating the need for any maps.
-#[cfg_attr(docsrs, doc(cfg(feature = "metrics")))]
-pub struct MetricsSubscriber {
- meter: Meter,
- instruments: Instruments,
-}
-
-impl MetricsSubscriber {
- /// Create a new instance of MetricsSubscriber.
- pub fn new(controller: BasicController) -> Self {
- let meter =
- controller.versioned_meter(INSTRUMENTATION_LIBRARY_NAME, Some(CARGO_PKG_VERSION), None);
- MetricsSubscriber {
- meter,
- instruments: Default::default(),
- }
- }
-}
-
-impl Subscribe for MetricsSubscriber
-where
- C: Collect + for<'span> LookupSpan<'span>,
-{
- fn on_event(&self, event: &tracing::Event<'_>, _ctx: Context<'_, C>) {
- let mut metric_visitor = MetricVisitor {
- instruments: &self.instruments,
- meter: &self.meter,
- };
- event.record(&mut metric_visitor);
- }
-}
diff --git a/tracing-opentelemetry/src/span_ext.rs b/tracing-opentelemetry/src/span_ext.rs
deleted file mode 100644
index 78fbef6600..0000000000
--- a/tracing-opentelemetry/src/span_ext.rs
+++ /dev/null
@@ -1,170 +0,0 @@
-use crate::subscriber::WithContext;
-use opentelemetry::{trace::SpanContext, Context, KeyValue};
-
-/// Utility functions to allow tracing [`Span`]s to accept and return
-/// [OpenTelemetry] [`Context`]s.
-///
-/// [`Span`]: tracing::Span
-/// [OpenTelemetry]: https://opentelemetry.io
-/// [`Context`]: opentelemetry::Context
-pub trait OpenTelemetrySpanExt {
- /// Associates `self` with a given OpenTelemetry trace, using the provided
- /// parent [`Context`].
- ///
- /// [`Context`]: opentelemetry::Context
- ///
- /// # Examples
- ///
- /// ```rust
- /// use opentelemetry::{propagation::TextMapPropagator, trace::TraceContextExt};
- /// use opentelemetry::sdk::propagation::TraceContextPropagator;
- /// use tracing_opentelemetry::OpenTelemetrySpanExt;
- /// use std::collections::HashMap;
- /// use tracing::Span;
- ///
- /// // Example carrier, could be a framework header map that impls otel's `Extractor`.
- /// let mut carrier = HashMap::new();
- ///
- /// // Propagator can be swapped with b3 propagator, jaeger propagator, etc.
- /// let propagator = TraceContextPropagator::new();
- ///
- /// // Extract otel parent context via the chosen propagator
- /// let parent_context = propagator.extract(&carrier);
- ///
- /// // Generate a tracing span as usual
- /// let app_root = tracing::span!(tracing::Level::INFO, "app_start");
- ///
- /// // Assign parent trace from external context
- /// app_root.set_parent(parent_context.clone());
- ///
- /// // Or if the current span has been created elsewhere:
- /// Span::current().set_parent(parent_context);
- /// ```
- fn set_parent(&self, cx: Context);
-
- /// Associates `self` with a given OpenTelemetry trace, using the provided
- /// followed span [`SpanContext`].
- ///
- /// [`SpanContext`]: opentelemetry::trace::SpanContext
- ///
- /// # Examples
- ///
- /// ```rust
- /// use opentelemetry::{propagation::TextMapPropagator, trace::TraceContextExt};
- /// use opentelemetry::sdk::propagation::TraceContextPropagator;
- /// use tracing_opentelemetry::OpenTelemetrySpanExt;
- /// use std::collections::HashMap;
- /// use tracing::Span;
- ///
- /// // Example carrier, could be a framework header map that impls otel's `Extractor`.
- /// let mut carrier = HashMap::new();
- ///
- /// // Propagator can be swapped with b3 propagator, jaeger propagator, etc.
- /// let propagator = TraceContextPropagator::new();
- ///
- /// // Extract otel context of linked span via the chosen propagator
- /// let linked_span_otel_context = propagator.extract(&carrier);
- ///
- /// // Extract the linked span context from the otel context
- /// let linked_span_context = linked_span_otel_context.span().span_context().clone();
- ///
- /// // Generate a tracing span as usual
- /// let app_root = tracing::span!(tracing::Level::INFO, "app_start");
- ///
- /// // Assign linked trace from external context
- /// app_root.add_link(linked_span_context);
- ///
- /// // Or if the current span has been created elsewhere:
- /// let linked_span_context = linked_span_otel_context.span().span_context().clone();
- /// Span::current().add_link(linked_span_context);
- /// ```
- fn add_link(&self, cx: SpanContext);
-
- /// Associates `self` with a given OpenTelemetry trace, using the provided
- /// followed span [`SpanContext`] and attributes.
- ///
- /// [`SpanContext`]: opentelemetry::trace::SpanContext
- fn add_link_with_attributes(&self, cx: SpanContext, attributes: Vec);
-
- /// Extracts an OpenTelemetry [`Context`] from `self`.
- ///
- /// [`Context`]: opentelemetry::Context
- ///
- /// # Examples
- ///
- /// ```rust
- /// use opentelemetry::Context;
- /// use tracing_opentelemetry::OpenTelemetrySpanExt;
- /// use tracing::Span;
- ///
- /// fn make_request(cx: Context) {
- /// // perform external request after injecting context
- /// // e.g. if the request's headers impl `opentelemetry::propagation::Injector`
- /// // then `propagator.inject_context(cx, request.headers_mut())`
- /// }
- ///
- /// // Generate a tracing span as usual
- /// let app_root = tracing::span!(tracing::Level::INFO, "app_start");
- ///
- /// // To include tracing context in client requests from _this_ app,
- /// // extract the current OpenTelemetry context.
- /// make_request(app_root.context());
- ///
- /// // Or if the current span has been created elsewhere:
- /// make_request(Span::current().context())
- /// ```
- fn context(&self) -> Context;
-}
-
-impl OpenTelemetrySpanExt for tracing::Span {
- fn set_parent(&self, cx: Context) {
- let mut cx = Some(cx);
- self.with_collector(move |(id, collector)| {
- if let Some(get_context) = collector.downcast_ref::() {
- get_context.with_context(collector, id, move |data, _tracer| {
- if let Some(cx) = cx.take() {
- data.parent_cx = cx;
- }
- });
- }
- });
- }
-
- fn add_link(&self, cx: SpanContext) {
- self.add_link_with_attributes(cx, Vec::new())
- }
-
- fn add_link_with_attributes(&self, cx: SpanContext, attributes: Vec) {
- if cx.is_valid() {
- let mut cx = Some(cx);
- let mut att = Some(attributes);
- self.with_collector(move |(id, collector)| {
- if let Some(get_context) = collector.downcast_ref::() {
- get_context.with_context(collector, id, move |data, _tracer| {
- if let Some(cx) = cx.take() {
- let attr = att.take().unwrap_or_default();
- let follows_link = opentelemetry::trace::Link::new(cx, attr);
- data.builder
- .links
- .get_or_insert_with(|| Vec::with_capacity(1))
- .push(follows_link);
- }
- });
- }
- });
- }
- }
-
- fn context(&self) -> Context {
- let mut cx = None;
- self.with_collector(|(id, collector)| {
- if let Some(get_context) = collector.downcast_ref::() {
- get_context.with_context(collector, id, |builder, tracer| {
- cx = Some(tracer.sampled_context(builder));
- })
- }
- });
-
- cx.unwrap_or_default()
- }
-}
diff --git a/tracing-opentelemetry/src/subscriber.rs b/tracing-opentelemetry/src/subscriber.rs
deleted file mode 100644
index 26ebccd128..0000000000
--- a/tracing-opentelemetry/src/subscriber.rs
+++ /dev/null
@@ -1,1413 +0,0 @@
-use crate::{OtelData, PreSampledTracer};
-use once_cell::unsync;
-use opentelemetry::{
- trace::{self as otel, noop, OrderMap, TraceContextExt},
- Context as OtelContext, Key, KeyValue, StringValue, Value,
-};
-use std::fmt;
-use std::marker;
-use std::thread;
-use std::time::{Instant, SystemTime};
-use std::{any::TypeId, ptr::NonNull};
-use tracing_core::span::{self, Attributes, Id, Record};
-use tracing_core::{field, Collect, Event};
-#[cfg(feature = "tracing-log")]
-use tracing_log::NormalizeEvent;
-use tracing_subscriber::registry::LookupSpan;
-use tracing_subscriber::subscribe::Context;
-use tracing_subscriber::Subscribe;
-
-const SPAN_NAME_FIELD: &str = "otel.name";
-const SPAN_KIND_FIELD: &str = "otel.kind";
-const SPAN_STATUS_CODE_FIELD: &str = "otel.status_code";
-const SPAN_STATUS_MESSAGE_FIELD: &str = "otel.status_message";
-
-const FIELD_EXCEPTION_MESSAGE: &str = "exception.message";
-const FIELD_EXCEPTION_STACKTRACE: &str = "exception.stacktrace";
-
-/// An [OpenTelemetry] propagation subscriber for use in a project that uses
-/// [tracing].
-///
-/// [OpenTelemetry]: https://opentelemetry.io
-/// [tracing]: https://github.com/tokio-rs/tracing
-pub struct OpenTelemetrySubscriber {
- tracer: T,
- location: bool,
- tracked_inactivity: bool,
- with_threads: bool,
- exception_config: ExceptionFieldConfig,
- get_context: WithContext,
- _registry: marker::PhantomData,
-}
-
-impl Default for OpenTelemetrySubscriber
-where
- C: Collect + for<'span> LookupSpan<'span>,
-{
- fn default() -> Self {
- OpenTelemetrySubscriber::new(noop::NoopTracer::new())
- }
-}
-
-/// Construct a subscriber to track spans via [OpenTelemetry].
-///
-/// [OpenTelemetry]: https://opentelemetry.io
-///
-/// # Examples
-///
-/// ```rust,no_run
-/// use tracing_subscriber::subscribe::CollectExt;
-/// use tracing_subscriber::Registry;
-///
-/// // Use the tracing subscriber `Registry`, or any other subscriber
-/// // that impls `LookupSpan`
-/// let subscriber = Registry::default().with(tracing_opentelemetry::subscriber());
-/// # drop(subscriber);
-/// ```
-pub fn subscriber() -> OpenTelemetrySubscriber
-where
- C: Collect + for<'span> LookupSpan<'span>,
-{
- OpenTelemetrySubscriber::default()
-}
-
-// this function "remembers" the types of the subscriber so that we
-// can downcast to something aware of them without knowing those
-// types at the callsite.
-//
-// See https://github.com/tokio-rs/tracing/blob/4dad420ee1d4607bad79270c1520673fa6266a3d/tracing-error/src/layer.rs
-pub(crate) struct WithContext(
- fn(&tracing::Dispatch, &span::Id, f: &mut dyn FnMut(&mut OtelData, &dyn PreSampledTracer)),
-);
-
-impl WithContext {
- // This function allows a function to be called in the context of the
- // "remembered" subscriber.
- pub(crate) fn with_context<'a>(
- &self,
- dispatch: &'a tracing::Dispatch,
- id: &span::Id,
- mut f: impl FnMut(&mut OtelData, &dyn PreSampledTracer),
- ) {
- (self.0)(dispatch, id, &mut f)
- }
-}
-
-fn str_to_span_kind(s: &str) -> Option {
- match s {
- s if s.eq_ignore_ascii_case("server") => Some(otel::SpanKind::Server),
- s if s.eq_ignore_ascii_case("client") => Some(otel::SpanKind::Client),
- s if s.eq_ignore_ascii_case("producer") => Some(otel::SpanKind::Producer),
- s if s.eq_ignore_ascii_case("consumer") => Some(otel::SpanKind::Consumer),
- s if s.eq_ignore_ascii_case("internal") => Some(otel::SpanKind::Internal),
- _ => None,
- }
-}
-
-fn str_to_status(s: &str) -> otel::Status {
- match s {
- s if s.eq_ignore_ascii_case("ok") => otel::Status::Ok,
- s if s.eq_ignore_ascii_case("error") => otel::Status::error(""),
- _ => otel::Status::Unset,
- }
-}
-
-struct SpanEventVisitor<'a, 'b> {
- event_builder: &'a mut otel::Event,
- span_builder: Option<&'b mut otel::SpanBuilder>,
- exception_config: ExceptionFieldConfig,
-}
-
-impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
- /// Record events on the underlying OpenTelemetry [`Span`] from `bool` values.
- ///
- /// [`Span`]: opentelemetry::trace::Span
- fn record_bool(&mut self, field: &field::Field, value: bool) {
- match field.name() {
- "message" => self.event_builder.name = value.to_string().into(),
- // Skip fields that are actually log metadata that have already been handled
- #[cfg(feature = "tracing-log")]
- name if name.starts_with("log.") => (),
- name => {
- self.event_builder
- .attributes
- .push(KeyValue::new(name, value));
- }
- }
- }
-
- /// Record events on the underlying OpenTelemetry [`Span`] from `f64` values.
- ///
- /// [`Span`]: opentelemetry::trace::Span
- fn record_f64(&mut self, field: &field::Field, value: f64) {
- match field.name() {
- "message" => self.event_builder.name = value.to_string().into(),
- // Skip fields that are actually log metadata that have already been handled
- #[cfg(feature = "tracing-log")]
- name if name.starts_with("log.") => (),
- name => {
- self.event_builder
- .attributes
- .push(KeyValue::new(name, value));
- }
- }
- }
-
- /// Record events on the underlying OpenTelemetry [`Span`] from `i64` values.
- ///
- /// [`Span`]: opentelemetry::trace::Span
- fn record_i64(&mut self, field: &field::Field, value: i64) {
- match field.name() {
- "message" => self.event_builder.name = value.to_string().into(),
- // Skip fields that are actually log metadata that have already been handled
- #[cfg(feature = "tracing-log")]
- name if name.starts_with("log.") => (),
- name => {
- self.event_builder
- .attributes
- .push(KeyValue::new(name, value));
- }
- }
- }
-
- /// Record events on the underlying OpenTelemetry [`Span`] from `&str` values.
- ///
- /// [`Span`]: opentelemetry::trace::Span
- fn record_str(&mut self, field: &field::Field, value: &str) {
- match field.name() {
- "message" => self.event_builder.name = value.to_string().into(),
- // Skip fields that are actually log metadata that have already been handled
- #[cfg(feature = "tracing-log")]
- name if name.starts_with("log.") => (),
- name => {
- self.event_builder
- .attributes
- .push(KeyValue::new(name, value.to_string()));
- }
- }
- }
-
- /// Record events on the underlying OpenTelemetry [`Span`] from values that
- /// implement Debug.
- ///
- /// [`Span`]: opentelemetry::trace::Span
- fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
- match field.name() {
- "message" => self.event_builder.name = format!("{:?}", value).into(),
- // Skip fields that are actually log metadata that have already been handled
- #[cfg(feature = "tracing-log")]
- name if name.starts_with("log.") => (),
- name => {
- self.event_builder
- .attributes
- .push(KeyValue::new(name, format!("{:?}", value)));
- }
- }
- }
-
- /// Set attributes on the underlying OpenTelemetry [`Span`] using a [`std::error::Error`]'s
- /// [`std::fmt::Display`] implementation. Also adds the `source` chain as an extra field
- ///
- /// [`Span`]: opentelemetry::trace::Span
- fn record_error(
- &mut self,
- field: &tracing_core::Field,
- value: &(dyn std::error::Error + 'static),
- ) {
- let mut chain = Vec::new();
- let mut next_err = value.source();
-
- while let Some(err) = next_err {
- chain.push(StringValue::from(err.to_string()));
- next_err = err.source();
- }
-
- let error_msg = value.to_string();
-
- if self.exception_config.record {
- self.event_builder
- .attributes
- .push(Key::new(FIELD_EXCEPTION_MESSAGE).string(error_msg.clone()));
-
- // NOTE: This is actually not the stacktrace of the exception. This is
- // the "source chain". It represents the heirarchy of errors from the
- // app level to the lowest level such as IO. It does not represent all
- // of the callsites in the code that led to the error happening.
- // `std::error::Error::backtrace` is a nightly-only API and cannot be
- // used here until the feature is stabilized.
- self.event_builder
- .attributes
- .push(Key::new(FIELD_EXCEPTION_STACKTRACE).array(chain.clone()));
- }
-
- if self.exception_config.propagate {
- if let Some(span) = &mut self.span_builder {
- if let Some(attrs) = span.attributes.as_mut() {
- attrs.insert(Key::new(FIELD_EXCEPTION_MESSAGE), error_msg.clone().into());
-
- // NOTE: This is actually not the stacktrace of the exception. This is
- // the "source chain". It represents the heirarchy of errors from the
- // app level to the lowest level such as IO. It does not represent all
- // of the callsites in the code that led to the error happening.
- // `std::error::Error::backtrace` is a nightly-only API and cannot be
- // used here until the feature is stabilized.
- attrs.insert(
- Key::new(FIELD_EXCEPTION_STACKTRACE),
- Value::Array(chain.clone().into()),
- );
- }
- }
- }
-
- self.event_builder
- .attributes
- .push(Key::new(field.name()).string(error_msg));
- self.event_builder
- .attributes
- .push(Key::new(format!("{}.chain", field.name())).array(chain));
- }
-}
-
-/// Control over opentelemetry conventional exception fields
-#[derive(Clone, Copy)]
-struct ExceptionFieldConfig {
- /// If an error value is recorded on an event/span, should the otel fields
- /// be added
- record: bool,
-
- /// If an error value is recorded on an event, should the otel fields be
- /// added to the corresponding span
- propagate: bool,
-}
-
-struct SpanAttributeVisitor<'a> {
- span_builder: &'a mut otel::SpanBuilder,
- exception_config: ExceptionFieldConfig,
-}
-
-impl<'a> SpanAttributeVisitor<'a> {
- fn record(&mut self, attribute: KeyValue) {
- debug_assert!(self.span_builder.attributes.is_some());
- if let Some(v) = self.span_builder.attributes.as_mut() {
- v.insert(attribute.key, attribute.value);
- }
- }
-}
-
-impl<'a> field::Visit for SpanAttributeVisitor<'a> {
- /// Set attributes on the underlying OpenTelemetry [`Span`] from `bool` values.
- ///
- /// [`Span`]: opentelemetry::trace::Span
- fn record_bool(&mut self, field: &field::Field, value: bool) {
- self.record(KeyValue::new(field.name(), value));
- }
-
- /// Set attributes on the underlying OpenTelemetry [`Span`] from `f64` values.
- ///
- /// [`Span`]: opentelemetry::trace::Span
- fn record_f64(&mut self, field: &field::Field, value: f64) {
- self.record(KeyValue::new(field.name(), value));
- }
-
- /// Set attributes on the underlying OpenTelemetry [`Span`] from `i64` values.
- ///
- /// [`Span`]: opentelemetry::trace::Span
- fn record_i64(&mut self, field: &field::Field, value: i64) {
- self.record(KeyValue::new(field.name(), value));
- }
-
- /// Set attributes on the underlying OpenTelemetry [`Span`] from `&str` values.
- ///
- /// [`Span`]: opentelemetry::trace::Span
- fn record_str(&mut self, field: &field::Field, value: &str) {
- match field.name() {
- SPAN_NAME_FIELD => self.span_builder.name = value.to_string().into(),
- SPAN_KIND_FIELD => self.span_builder.span_kind = str_to_span_kind(value),
- SPAN_STATUS_CODE_FIELD => self.span_builder.status = str_to_status(value),
- SPAN_STATUS_MESSAGE_FIELD => {
- self.span_builder.status = otel::Status::error(value.to_string())
- }
- _ => self.record(KeyValue::new(field.name(), value.to_string())),
- }
- }
-
- /// Set attributes on the underlying OpenTelemetry [`Span`] from values that
- /// implement Debug.
- ///
- /// [`Span`]: opentelemetry::trace::Span
- fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) {
- match field.name() {
- SPAN_NAME_FIELD => self.span_builder.name = format!("{:?}", value).into(),
- SPAN_KIND_FIELD => {
- self.span_builder.span_kind = str_to_span_kind(&format!("{:?}", value))
- }
- SPAN_STATUS_CODE_FIELD => {
- self.span_builder.status = str_to_status(&format!("{:?}", value))
- }
- SPAN_STATUS_MESSAGE_FIELD => {
- self.span_builder.status = otel::Status::error(format!("{:?}", value))
- }
- _ => self.record(Key::new(field.name()).string(format!("{:?}", value))),
- }
- }
-
- /// Set attributes on the underlying OpenTelemetry [`Span`] using a [`std::error::Error`]'s
- /// [`std::fmt::Display`] implementation. Also adds the `source` chain as an extra field
- ///
- /// [`Span`]: opentelemetry::trace::Span
- fn record_error(
- &mut self,
- field: &tracing_core::Field,
- value: &(dyn std::error::Error + 'static),
- ) {
- let mut chain = Vec::new();
- let mut next_err = value.source();
-
- while let Some(err) = next_err {
- chain.push(StringValue::from(err.to_string()));
- next_err = err.source();
- }
-
- let error_msg = value.to_string();
-
- if self.exception_config.record {
- self.record(Key::new(FIELD_EXCEPTION_MESSAGE).string(error_msg.clone()));
-
- // NOTE: This is actually not the stacktrace of the exception. This is
- // the "source chain". It represents the heirarchy of errors from the
- // app level to the lowest level such as IO. It does not represent all
- // of the callsites in the code that led to the error happening.
- // `std::error::Error::backtrace` is a nightly-only API and cannot be
- // used here until the feature is stabilized.
- self.record(Key::new(FIELD_EXCEPTION_STACKTRACE).array(chain.clone()));
- }
-
- self.record(Key::new(field.name()).string(error_msg));
- self.record(Key::new(format!("{}.chain", field.name())).array(chain));
- }
-}
-
-impl OpenTelemetrySubscriber
-where
- C: Collect + for<'span> LookupSpan<'span>,
- T: otel::Tracer + PreSampledTracer + 'static,
-{
- /// Set the [`Tracer`] that this subscriber will use to produce and track
- /// OpenTelemetry [`Span`]s.
- ///
- /// [`Tracer`]: opentelemetry::trace::Tracer
- /// [`Span`]: opentelemetry::trace::Span
- ///
- /// # Examples
- ///
- /// ```no_run
- /// use tracing_opentelemetry::OpenTelemetrySubscriber;
- /// use tracing_subscriber::subscribe::CollectExt;
- /// use tracing_subscriber::Registry;
- ///
- /// // Create a jaeger exporter pipeline for a `trace_demo` service.
- /// let tracer = opentelemetry_jaeger::new_agent_pipeline()
- /// .with_service_name("trace_demo")
- /// .install_simple()
- /// .expect("Error initializing Jaeger exporter");
- ///
- /// // Create a subscriber with the configured tracer
- /// let otel_subscriber = OpenTelemetrySubscriber::new(tracer);
- ///
- /// // Use the tracing subscriber `Registry`, or any other subscriber
- /// // that impls `LookupSpan`
- /// let subscriber = Registry::default().with(otel_subscriber);
- /// # drop(subscriber);
- /// ```
- pub fn new(tracer: T) -> Self {
- OpenTelemetrySubscriber {
- tracer,
- location: true,
- tracked_inactivity: true,
- with_threads: true,
- exception_config: ExceptionFieldConfig {
- record: false,
- propagate: false,
- },
- get_context: WithContext(Self::get_context),
- _registry: marker::PhantomData,
- }
- }
-
- /// Set the [`Tracer`] that this subscriber will use to produce and track
- /// OpenTelemetry [`Span`]s.
- ///
- /// [`Tracer`]: opentelemetry::trace::Tracer
- /// [`Span`]: opentelemetry::trace::Span
- ///
- /// # Examples
- ///
- /// ```no_run
- /// use tracing_subscriber::subscribe::CollectExt;
- /// use tracing_subscriber::Registry;
- ///
- /// // Create a jaeger exporter pipeline for a `trace_demo` service.
- /// let tracer = opentelemetry_jaeger::new_agent_pipeline()
- /// .with_service_name("trace_demo")
- /// .install_simple()
- /// .expect("Error initializing Jaeger exporter");
- ///
- /// // Create a subscriber with the configured tracer
- /// let otel_subscriber = tracing_opentelemetry::subscriber().with_tracer(tracer);
- ///
- /// // Use the tracing subscriber `Registry`, or any other subscriber
- /// // that impls `LookupSpan`
- /// let subscriber = Registry::default().with(otel_subscriber);
- /// # drop(subscriber);
- /// ```
- pub fn with_tracer(self, tracer: Tracer) -> OpenTelemetrySubscriber
- where
- Tracer: otel::Tracer + PreSampledTracer + 'static,
- {
- OpenTelemetrySubscriber {
- tracer,
- location: self.location,
- tracked_inactivity: self.tracked_inactivity,
- with_threads: self.with_threads,
- get_context: WithContext(OpenTelemetrySubscriber::::get_context),
- exception_config: self.exception_config,
- _registry: self._registry,
- }
- }
-
- /// Sets whether or not span and event metadata should include OpenTelemetry
- /// exception fields such as `exception.message` and `exception.backtrace`
- /// when an `Error` value is recorded. If multiple error values are recorded
- /// on the same span/event, only the most recently recorded error value will
- /// show up under these fields.
- ///
- /// These attributes follow the [OpenTelemetry semantic conventions for
- /// exceptions][conv].
- ///
- /// By default, these fields are disabled
- ///
- /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/exceptions/
- pub fn with_exception_fields(self, exception_fields: bool) -> Self {
- Self {
- exception_config: ExceptionFieldConfig {
- record: exception_fields,
- ..self.exception_config
- },
- ..self
- }
- }
-
- /// Sets whether or not reporting an `Error` value on an event will
- /// propagate the OpenTelemetry exception fields such as `exception.message`
- /// and `exception.backtrace` to the corresponding span. You do not need to
- /// enable `with_exception_fields` in order to enable this. If multiple
- /// error values are recorded on the same span/event, only the most recently
- /// recorded error value will show up under these fields.
- ///
- /// These attributes follow the [OpenTelemetry semantic conventions for
- /// exceptions][conv].
- ///
- /// By default, this is disabled
- ///
- /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/exceptions/
- pub fn with_exception_field_propagation(self, exception_field_propagation: bool) -> Self {
- Self {
- exception_config: ExceptionFieldConfig {
- propagate: exception_field_propagation,
- ..self.exception_config
- },
- ..self
- }
- }
-
- /// Sets whether or not span and event metadata should include OpenTelemetry
- /// attributes with location information, such as the file, module and line number.
- ///
- /// These attributes follow the [OpenTelemetry semantic conventions for
- /// source locations][conv].
- ///
- /// By default, locations are enabled.
- ///
- /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#source-code-attributes
- pub fn with_location(self, location: bool) -> Self {
- Self { location, ..self }
- }
-
- /// Sets whether or not span and event metadata should include OpenTelemetry
- /// attributes with location information, such as the file, module and line number.
- ///
- /// These attributes follow the [OpenTelemetry semantic conventions for
- /// source locations][conv].
- ///
- /// By default, locations are enabled.
- ///
- /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#source-code-attributes
- #[deprecated(
- since = "0.17.3",
- note = "renamed to `OpenTelemetrySubscriber::with_location`"
- )]
- pub fn with_event_location(self, event_location: bool) -> Self {
- Self {
- location: event_location,
- ..self
- }
- }
-
- /// Sets whether or not spans metadata should include the _busy time_
- /// (total time for which it was entered), and _idle time_ (total time
- /// the span existed but was not entered).
- pub fn with_tracked_inactivity(self, tracked_inactivity: bool) -> Self {
- Self {
- tracked_inactivity,
- ..self
- }
- }
-
- /// Sets whether or not spans record additional attributes for the thread
- /// name and thread ID of the thread they were created on, following the
- /// [OpenTelemetry semantic conventions for threads][conv].
- ///
- /// By default, thread attributes are enabled.
- ///
- /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#general-thread-attributes
- pub fn with_threads(self, threads: bool) -> Self {
- Self {
- with_threads: threads,
- ..self
- }
- }
-
- /// Retrieve the parent OpenTelemetry [`Context`] from the current tracing
- /// [`span`] through the [`Registry`]. This [`Context`] links spans to their
- /// parent for proper hierarchical visualization.
- ///
- /// [`Context`]: opentelemetry::Context
- /// [`span`]: tracing::Span
- /// [`Registry`]: tracing_subscriber::Registry
- fn parent_context(&self, attrs: &Attributes<'_>, ctx: &Context<'_, C>) -> OtelContext {
- // If a span is specified, it _should_ exist in the underlying `Registry`.
- if let Some(parent) = attrs.parent() {
- let span = ctx.span(parent).expect("Span not found, this is a bug");
- let mut extensions = span.extensions_mut();
- extensions
- .get_mut::()
- .map(|builder| self.tracer.sampled_context(builder))
- .unwrap_or_default()
- // Else if the span is inferred from context, look up any available current span.
- } else if attrs.is_contextual() {
- ctx.lookup_current()
- .and_then(|span| {
- let mut extensions = span.extensions_mut();
- extensions
- .get_mut::()
- .map(|builder| self.tracer.sampled_context(builder))
- })
- .unwrap_or_else(OtelContext::current)
- // Explicit root spans should have no parent context.
- } else {
- OtelContext::new()
- }
- }
-
- fn get_context(
- dispatch: &tracing::Dispatch,
- id: &span::Id,
- f: &mut dyn FnMut(&mut OtelData, &dyn PreSampledTracer),
- ) {
- let subscriber = dispatch
- .downcast_ref::()
- .expect("subscriber should downcast to expected type; this is a bug!");
- let span = subscriber
- .span(id)
- .expect("registry should have a span for the current ID");
- let subscriber = dispatch
- .downcast_ref::>()
- .expect("subscriber should downcast to expected type; this is a bug!");
-
- let mut extensions = span.extensions_mut();
- if let Some(builder) = extensions.get_mut::() {
- f(builder, &subscriber.tracer);
- }
- }
-
- fn extra_span_attrs(&self) -> usize {
- let mut extra_attrs = 0;
- if self.location {
- extra_attrs += 3;
- }
- if self.with_threads {
- extra_attrs += 2;
- }
- extra_attrs
- }
-}
-
-thread_local! {
- static THREAD_ID: unsync::Lazy = unsync::Lazy::new(|| {
- // OpenTelemetry's semantic conventions require the thread ID to be
- // recorded as an integer, but `std::thread::ThreadId` does not expose
- // the integer value on stable, so we have to convert it to a `usize` by
- // parsing it. Since this requires allocating a `String`, store it in a
- // thread local so we only have to do this once.
- // TODO(eliza): once `std::thread::ThreadId::as_u64` is stabilized
- // (https://github.com/rust-lang/rust/issues/67939), just use that.
- thread_id_integer(thread::current().id())
- });
-}
-
-impl Subscribe for OpenTelemetrySubscriber
-where
- C: Collect + for<'span> LookupSpan<'span>,
- T: otel::Tracer + PreSampledTracer + 'static,
-{
- /// Creates an [OpenTelemetry `Span`] for the corresponding [tracing `Span`].
- ///
- /// [OpenTelemetry `Span`]: opentelemetry::trace::Span
- /// [tracing `Span`]: tracing::Span
- fn on_new_span(&self, attrs: &Attributes<'_>, id: &span::Id, ctx: Context<'_, C>) {
- let span = ctx.span(id).expect("Span not found, this is a bug");
- let mut extensions = span.extensions_mut();
-
- if self.tracked_inactivity && extensions.get_mut::().is_none() {
- extensions.insert(Timings::new());
- }
-
- let parent_cx = self.parent_context(attrs, &ctx);
- let mut builder = self
- .tracer
- .span_builder(attrs.metadata().name())
- .with_start_time(SystemTime::now())
- // Eagerly assign span id so children have stable parent id
- .with_span_id(self.tracer.new_span_id());
-
- // Record new trace id if there is no active parent span
- if !parent_cx.has_active_span() {
- builder.trace_id = Some(self.tracer.new_trace_id());
- }
-
- let builder_attrs = builder.attributes.get_or_insert(OrderMap::with_capacity(
- attrs.fields().len() + self.extra_span_attrs(),
- ));
-
- if self.location {
- let meta = attrs.metadata();
-
- if let Some(filename) = meta.file() {
- builder_attrs.insert("code.filepath".into(), filename.into());
- }
-
- if let Some(module) = meta.module_path() {
- builder_attrs.insert("code.namespace".into(), module.into());
- }
-
- if let Some(line) = meta.line() {
- builder_attrs.insert("code.lineno".into(), (line as i64).into());
- }
- }
-
- if self.with_threads {
- THREAD_ID.with(|id| builder_attrs.insert("thread.id".into(), (**id as i64).into()));
- if let Some(name) = std::thread::current().name() {
- // TODO(eliza): it's a bummer that we have to allocate here, but
- // we can't easily get the string as a `static`. it would be
- // nice if `opentelemetry` could also take `Arc`s as
- // `String` values...
- builder_attrs.insert("thread.name".into(), name.to_owned().into());
- }
- }
-
- attrs.record(&mut SpanAttributeVisitor {
- span_builder: &mut builder,
- exception_config: self.exception_config,
- });
- extensions.insert(OtelData { builder, parent_cx });
- }
-
- fn on_enter(&self, id: &span::Id, ctx: Context<'_, C>) {
- if !self.tracked_inactivity {
- return;
- }
-
- let span = ctx.span(id).expect("Span not found, this is a bug");
- let mut extensions = span.extensions_mut();
-
- if let Some(timings) = extensions.get_mut::() {
- let now = Instant::now();
- timings.idle += (now - timings.last).as_nanos() as i64;
- timings.last = now;
- }
- }
-
- fn on_exit(&self, id: &span::Id, ctx: Context<'_, C>) {
- if !self.tracked_inactivity {
- return;
- }
-
- let span = ctx.span(id).expect("Span not found, this is a bug");
- let mut extensions = span.extensions_mut();
-
- if let Some(timings) = extensions.get_mut::() {
- let now = Instant::now();
- timings.busy += (now - timings.last).as_nanos() as i64;
- timings.last = now;
- }
- }
-
- /// Record OpenTelemetry [`attributes`] for the given values.
- ///
- /// [`attributes`]: opentelemetry::trace::SpanBuilder::attributes
- fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, C>) {
- let span = ctx.span(id).expect("Span not found, this is a bug");
- let mut extensions = span.extensions_mut();
- if let Some(data) = extensions.get_mut::() {
- values.record(&mut SpanAttributeVisitor {
- span_builder: &mut data.builder,
- exception_config: self.exception_config,
- });
- }
- }
-
- fn on_follows_from(&self, id: &Id, follows: &Id, ctx: Context) {
- let span = ctx.span(id).expect("Span not found, this is a bug");
- let mut extensions = span.extensions_mut();
- let data = extensions
- .get_mut::()
- .expect("Missing otel data span extensions");
-
- let follows_span = ctx
- .span(follows)
- .expect("Span to follow not found, this is a bug");
- let mut follows_extensions = follows_span.extensions_mut();
- let follows_data = follows_extensions
- .get_mut::()
- .expect("Missing otel data span extensions");
-
- let follows_context = self
- .tracer
- .sampled_context(follows_data)
- .span()
- .span_context()
- .clone();
- let follows_link = otel::Link::new(follows_context, Vec::new());
- if let Some(ref mut links) = data.builder.links {
- links.push(follows_link);
- } else {
- data.builder.links = Some(vec![follows_link]);
- }
- }
-
- /// Records OpenTelemetry [`Event`] data on event.
- ///
- /// Note: an [`ERROR`]-level event will also set the OpenTelemetry span status code to
- /// [`Error`], signaling that an error has occurred.
- ///
- /// [`Event`]: opentelemetry::trace::Event
- /// [`ERROR`]: tracing::Level::ERROR
- /// [`Error`]: opentelemetry::trace::StatusCode::Error
- fn on_event(&self, event: &Event<'_>, ctx: Context<'_, C>) {
- // Ignore events that have no explicit parent set *and* are not in the context of a span
- if let Some(span) = ctx.event_span(event) {
- // Performing read operations before getting a write lock to avoid a deadlock
- // See https://github.com/tokio-rs/tracing/issues/763
- #[cfg(feature = "tracing-log")]
- let normalized_meta = event.normalized_metadata();
- #[cfg(feature = "tracing-log")]
- let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata());
- #[cfg(not(feature = "tracing-log"))]
- let meta = event.metadata();
-
- let target = Key::new("target");
-
- #[cfg(feature = "tracing-log")]
- let target = if normalized_meta.is_some() {
- target.string(meta.target().to_owned())
- } else {
- target.string(event.metadata().target())
- };
-
- #[cfg(not(feature = "tracing-log"))]
- let target = target.string(meta.target());
-
- let mut extensions = span.extensions_mut();
- let span_builder = extensions
- .get_mut::()
- .map(|data| &mut data.builder);
-
- let mut otel_event = otel::Event::new(
- String::new(),
- SystemTime::now(),
- vec![Key::new("level").string(meta.level().as_str()), target],
- 0,
- );
- event.record(&mut SpanEventVisitor {
- event_builder: &mut otel_event,
- span_builder,
- exception_config: self.exception_config,
- });
-
- if let Some(OtelData { builder, .. }) = extensions.get_mut::() {
- if builder.status == otel::Status::Unset
- && *meta.level() == tracing_core::Level::ERROR
- {
- builder.status = otel::Status::error("")
- }
-
- if self.location {
- #[cfg(not(feature = "tracing-log"))]
- let normalized_meta: Option> = None;
- let (file, module) = match &normalized_meta {
- Some(meta) => (
- meta.file().map(|s| Value::from(s.to_owned())),
- meta.module_path().map(|s| Value::from(s.to_owned())),
- ),
- None => (
- event.metadata().file().map(Value::from),
- event.metadata().module_path().map(Value::from),
- ),
- };
-
- if let Some(file) = file {
- otel_event
- .attributes
- .push(KeyValue::new("code.filepath", file));
- }
- if let Some(module) = module {
- otel_event
- .attributes
- .push(KeyValue::new("code.namespace", module));
- }
- if let Some(line) = meta.line() {
- otel_event
- .attributes
- .push(KeyValue::new("code.lineno", line as i64));
- }
- }
-
- if let Some(ref mut events) = builder.events {
- events.push(otel_event);
- } else {
- builder.events = Some(vec![otel_event]);
- }
- }
- };
- }
-
- /// Exports an OpenTelemetry [`Span`] on close.
- ///
- /// [`Span`]: opentelemetry::trace::Span
- fn on_close(&self, id: span::Id, ctx: Context<'_, C>) {
- let span = ctx.span(&id).expect("Span not found, this is a bug");
- let mut extensions = span.extensions_mut();
-
- if let Some(OtelData {
- mut builder,
- parent_cx,
- }) = extensions.remove::()
- {
- if self.tracked_inactivity {
- // Append busy/idle timings when enabled.
- if let Some(timings) = extensions.get_mut::() {
- let busy_ns = Key::new("busy_ns");
- let idle_ns = Key::new("idle_ns");
-
- let attributes = builder
- .attributes
- .get_or_insert_with(|| OrderMap::with_capacity(2));
- attributes.insert(busy_ns, timings.busy.into());
- attributes.insert(idle_ns, timings.idle.into());
- }
- }
-
- // Assign end time, build and start span, drop span to export
- builder
- .with_end_time(SystemTime::now())
- .start_with_context(&self.tracer, &parent_cx);
- }
- }
-
- // SAFETY: this is safe because the `WithContext` function pointer is valid
- // for the lifetime of `&self`.
- unsafe fn downcast_raw(&self, id: TypeId) -> Option> {
- match id {
- id if id == TypeId::of::() => Some(NonNull::from(self).cast()),
- id if id == TypeId::of::() => {
- Some(NonNull::from(&self.get_context).cast())
- }
- _ => None,
- }
- }
-}
-
-struct Timings {
- idle: i64,
- busy: i64,
- last: Instant,
-}
-
-impl Timings {
- fn new() -> Self {
- Self {
- idle: 0,
- busy: 0,
- last: Instant::now(),
- }
- }
-}
-
-fn thread_id_integer(id: thread::ThreadId) -> u64 {
- let thread_id = format!("{:?}", id);
- thread_id
- .trim_start_matches("ThreadId(")
- .trim_end_matches(')')
- .parse::()
- .expect("thread ID should parse as an integer")
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::OtelData;
- use opentelemetry::{
- trace::{noop, TraceFlags},
- StringValue,
- };
- use std::{
- borrow::Cow,
- collections::HashMap,
- error::Error,
- fmt::Display,
- sync::{Arc, Mutex},
- thread,
- time::SystemTime,
- };
- use tracing_subscriber::prelude::*;
-
- #[derive(Debug, Clone)]
- struct TestTracer(Arc>>);
- impl otel::Tracer for TestTracer {
- type Span = noop::NoopSpan;
- fn start_with_context(&self, _name: T, _context: &OtelContext) -> Self::Span
- where
- T: Into>,
- {
- noop::NoopSpan::new()
- }
- fn span_builder(&self, name: T) -> otel::SpanBuilder
- where
- T: Into>,
- {
- otel::SpanBuilder::from_name(name)
- }
- fn build_with_context(
- &self,
- builder: otel::SpanBuilder,
- parent_cx: &OtelContext,
- ) -> Self::Span {
- *self.0.lock().unwrap() = Some(OtelData {
- builder,
- parent_cx: parent_cx.clone(),
- });
- noop::NoopSpan::new()
- }
- }
-
- impl PreSampledTracer for TestTracer {
- fn sampled_context(&self, _builder: &mut crate::OtelData) -> OtelContext {
- OtelContext::new()
- }
- fn new_trace_id(&self) -> otel::TraceId {
- otel::TraceId::INVALID
- }
- fn new_span_id(&self) -> otel::SpanId {
- otel::SpanId::INVALID
- }
- }
-
- impl TestTracer {
- fn with_data(&self, f: impl FnOnce(&OtelData) -> T) -> T {
- let lock = self.0.lock().unwrap();
- let data = lock.as_ref().expect("no span data has been recorded yet");
- f(data)
- }
- }
-
- #[derive(Debug, Clone)]
- struct TestSpan(otel::SpanContext);
- impl otel::Span for TestSpan {
- fn add_event_with_timestamp>>(
- &mut self,
- _: T,
- _: SystemTime,
- _: Vec,
- ) {
- }
- fn span_context(&self) -> &otel::SpanContext {
- &self.0
- }
- fn is_recording(&self) -> bool {
- false
- }
- fn set_attribute(&mut self, _attribute: KeyValue) {}
- fn set_status(&mut self, _status: otel::Status) {}
- fn update_name>>(&mut self, _new_name: T) {}
- fn end_with_timestamp(&mut self, _timestamp: SystemTime) {}
- }
-
- #[derive(Debug)]
- struct TestDynError {
- msg: &'static str,
- source: Option>,
- }
- impl Display for TestDynError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.msg)
- }
- }
- impl Error for TestDynError {
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- match &self.source {
- Some(source) => Some(source),
- None => None,
- }
- }
- }
- impl TestDynError {
- fn new(msg: &'static str) -> Self {
- Self { msg, source: None }
- }
- fn with_parent(self, parent_msg: &'static str) -> Self {
- Self {
- msg: parent_msg,
- source: Some(Box::new(self)),
- }
- }
- }
-
- #[test]
- fn dynamic_span_names() {
- let dynamic_name = "GET http://example.com".to_string();
- let tracer = TestTracer(Arc::new(Mutex::new(None)));
- let subscriber =
- tracing_subscriber::registry().with(subscriber().with_tracer(tracer.clone()));
-
- tracing::collect::with_default(subscriber, || {
- tracing::debug_span!("static_name", otel.name = dynamic_name.as_str());
- });
-
- let recorded_name = tracer
- .0
- .lock()
- .unwrap()
- .as_ref()
- .map(|b| b.builder.name.clone());
- assert_eq!(recorded_name, Some(dynamic_name.into()))
- }
-
- #[test]
- fn span_kind() {
- let tracer = TestTracer(Arc::new(Mutex::new(None)));
- let subscriber =
- tracing_subscriber::registry().with(subscriber().with_tracer(tracer.clone()));
-
- tracing::collect::with_default(subscriber, || {
- tracing::debug_span!("request", otel.kind = "server");
- });
-
- let recorded_kind = tracer.with_data(|data| data.builder.span_kind.clone());
- assert_eq!(recorded_kind, Some(otel::SpanKind::Server))
- }
-
- #[test]
- fn span_status_code() {
- let tracer = TestTracer(Arc::new(Mutex::new(None)));
- let subscriber =
- tracing_subscriber::registry().with(subscriber().with_tracer(tracer.clone()));
-
- tracing::collect::with_default(subscriber, || {
- tracing::debug_span!("request", otel.status_code = ?otel::Status::Ok);
- });
- let recorded_status = tracer
- .0
- .lock()
- .unwrap()
- .as_ref()
- .unwrap()
- .builder
- .status
- .clone();
-
- assert_eq!(recorded_status, otel::Status::Ok)
- }
-
- #[test]
- fn span_status_message() {
- let tracer = TestTracer(Arc::new(Mutex::new(None)));
- let subscriber =
- tracing_subscriber::registry().with(subscriber().with_tracer(tracer.clone()));
-
- let message = "message";
-
- tracing::collect::with_default(subscriber, || {
- tracing::debug_span!("request", otel.status_message = message);
- });
-
- let recorded_status_message = tracer
- .0
- .lock()
- .unwrap()
- .as_ref()
- .unwrap()
- .builder
- .status
- .clone();
-
- assert_eq!(recorded_status_message, otel::Status::error(message))
- }
-
- #[test]
- fn trace_id_from_existing_context() {
- let tracer = TestTracer(Arc::new(Mutex::new(None)));
- let subscriber =
- tracing_subscriber::registry().with(subscriber().with_tracer(tracer.clone()));
- let trace_id = otel::TraceId::from(42u128.to_be_bytes());
- let existing_cx = OtelContext::current_with_span(TestSpan(otel::SpanContext::new(
- trace_id,
- otel::SpanId::from(1u64.to_be_bytes()),
- TraceFlags::default(),
- false,
- Default::default(),
- )));
- let _g = existing_cx.attach();
-
- tracing::collect::with_default(subscriber, || {
- tracing::debug_span!("request", otel.kind = "server");
- });
-
- let recorded_trace_id =
- tracer.with_data(|data| data.parent_cx.span().span_context().trace_id());
- assert_eq!(recorded_trace_id, trace_id)
- }
-
- #[test]
- fn includes_timings() {
- let tracer = TestTracer(Arc::new(Mutex::new(None)));
- let subscriber = tracing_subscriber::registry().with(
- subscriber()
- .with_tracer(tracer.clone())
- .with_tracked_inactivity(true),
- );
-
- tracing::collect::with_default(subscriber, || {
- tracing::debug_span!("request");
- });
-
- let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
- let keys = attributes
- .iter()
- .map(|(key, _)| key.as_str())
- .collect::>();
- assert!(keys.contains(&"idle_ns"));
- assert!(keys.contains(&"busy_ns"));
- }
-
- #[test]
- fn records_error_fields() {
- let tracer = TestTracer(Arc::new(Mutex::new(None)));
- let subscriber = tracing_subscriber::registry().with(
- subscriber()
- .with_tracer(tracer.clone())
- .with_exception_fields(true),
- );
-
- let err = TestDynError::new("base error")
- .with_parent("intermediate error")
- .with_parent("user error");
-
- tracing::collect::with_default(subscriber, || {
- tracing::debug_span!(
- "request",
- error = &err as &(dyn std::error::Error + 'static)
- );
- });
-
- let attributes = tracer
- .0
- .lock()
- .unwrap()
- .as_ref()
- .unwrap()
- .builder
- .attributes
- .as_ref()
- .unwrap()
- .clone();
-
- let key_values = attributes
- .into_iter()
- .map(|(key, value)| (key.as_str().to_owned(), value))
- .collect::>();
-
- assert_eq!(key_values["error"].as_str(), "user error");
- assert_eq!(
- key_values["error.chain"],
- Value::Array(
- vec![
- StringValue::from("intermediate error"),
- StringValue::from("base error")
- ]
- .into()
- )
- );
-
- assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
- assert_eq!(
- key_values[FIELD_EXCEPTION_STACKTRACE],
- Value::Array(
- vec![
- StringValue::from("intermediate error"),
- StringValue::from("base error")
- ]
- .into()
- )
- );
- }
-
- #[test]
- fn includes_span_location() {
- let tracer = TestTracer(Arc::new(Mutex::new(None)));
- let subscriber = tracing_subscriber::registry()
- .with(subscriber().with_tracer(tracer.clone()).with_location(true));
-
- tracing::collect::with_default(subscriber, || {
- tracing::debug_span!("request");
- });
-
- let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
- let keys = attributes
- .iter()
- .map(|(key, _)| key.as_str())
- .collect::>();
- assert!(keys.contains(&"code.filepath"));
- assert!(keys.contains(&"code.namespace"));
- assert!(keys.contains(&"code.lineno"));
- }
-
- #[test]
- fn excludes_span_location() {
- let tracer = TestTracer(Arc::new(Mutex::new(None)));
- let subscriber = tracing_subscriber::registry().with(
- subscriber()
- .with_tracer(tracer.clone())
- .with_location(false),
- );
-
- tracing::collect::with_default(subscriber, || {
- tracing::debug_span!("request");
- });
-
- let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
- let keys = attributes
- .iter()
- .map(|(key, _)| key.as_str())
- .collect::>();
- assert!(!keys.contains(&"code.filepath"));
- assert!(!keys.contains(&"code.namespace"));
- assert!(!keys.contains(&"code.lineno"));
- }
-
- #[test]
- fn includes_thread() {
- let thread = thread::current();
- let expected_name = thread
- .name()
- .map(|name| Value::String(name.to_owned().into()));
- let expected_id = Value::I64(thread_id_integer(thread.id()) as i64);
-
- let tracer = TestTracer(Arc::new(Mutex::new(None)));
- let subscriber = tracing_subscriber::registry()
- .with(subscriber().with_tracer(tracer.clone()).with_threads(true));
-
- tracing::collect::with_default(subscriber, || {
- tracing::debug_span!("request");
- });
-
- let attributes = tracer
- .with_data(|data| data.builder.attributes.as_ref().unwrap().clone())
- .drain(..)
- .map(|(key, value)| (key.as_str().to_string(), value))
- .collect::>();
- assert_eq!(attributes.get("thread.name"), expected_name.as_ref());
- assert_eq!(attributes.get("thread.id"), Some(&expected_id));
- }
-
- #[test]
- fn excludes_thread() {
- let tracer = TestTracer(Arc::new(Mutex::new(None)));
- let subscriber = tracing_subscriber::registry()
- .with(subscriber().with_tracer(tracer.clone()).with_threads(false));
-
- tracing::collect::with_default(subscriber, || {
- tracing::debug_span!("request");
- });
-
- let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
- let keys = attributes
- .iter()
- .map(|(key, _)| key.as_str())
- .collect::>();
- assert!(!keys.contains(&"thread.name"));
- assert!(!keys.contains(&"thread.id"));
- }
-
- #[test]
- fn propagates_error_fields_from_event_to_span() {
- let tracer = TestTracer(Arc::new(Mutex::new(None)));
- let subscriber = tracing_subscriber::registry().with(
- subscriber()
- .with_tracer(tracer.clone())
- .with_exception_field_propagation(true),
- );
-
- let err = TestDynError::new("base error")
- .with_parent("intermediate error")
- .with_parent("user error");
-
- tracing::collect::with_default(subscriber, || {
- let _guard = tracing::debug_span!("request",).entered();
-
- tracing::error!(
- error = &err as &(dyn std::error::Error + 'static),
- "request error!"
- )
- });
-
- let attributes = tracer
- .0
- .lock()
- .unwrap()
- .as_ref()
- .unwrap()
- .builder
- .attributes
- .as_ref()
- .unwrap()
- .clone();
-
- let key_values = attributes
- .into_iter()
- .map(|(key, value)| (key.as_str().to_owned(), value))
- .collect::>();
-
- assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
- assert_eq!(
- key_values[FIELD_EXCEPTION_STACKTRACE],
- Value::Array(
- vec![
- StringValue::from("intermediate error"),
- StringValue::from("base error")
- ]
- .into()
- )
- );
- }
-}
diff --git a/tracing-opentelemetry/src/tracer.rs b/tracing-opentelemetry/src/tracer.rs
deleted file mode 100644
index 52513c36ad..0000000000
--- a/tracing-opentelemetry/src/tracer.rs
+++ /dev/null
@@ -1,233 +0,0 @@
-use opentelemetry::sdk::trace::{Tracer, TracerProvider};
-use opentelemetry::trace::OrderMap;
-use opentelemetry::{
- trace as otel,
- trace::{
- noop, SamplingDecision, SamplingResult, SpanBuilder, SpanContext, SpanId, SpanKind,
- TraceContextExt, TraceFlags, TraceId, TraceState,
- },
- Context as OtelContext,
-};
-
-/// An interface for authors of OpenTelemetry SDKs to build pre-sampled tracers.
-///
-/// The OpenTelemetry spec does not allow trace ids to be updated after a span
-/// has been created. In order to associate extracted parent trace ids with
-/// existing `tracing` spans, `tracing-opentelemetry` builds up otel span data
-/// using a [`SpanBuilder`] instead, and creates / exports full otel spans only
-/// when the associated `tracing` span is closed. However, in order to properly
-/// inject otel [`Context`] information to downstream requests, the sampling
-/// state must now be known _before_ the otel span has been created.
-///
-/// The logic for coming to a sampling decision and creating an injectable span
-/// context from a [`SpanBuilder`] is encapsulated in the
-/// [`PreSampledTracer::sampled_context`] method and has been implemented
-/// for the standard OpenTelemetry SDK, but this trait may be implemented by
-/// authors of alternate OpenTelemetry SDK implementations if they wish to have
-/// `tracing` compatibility.
-///
-/// See the [`OpenTelemetrySpanExt::set_parent`] and
-/// [`OpenTelemetrySpanExt::context`] methods for example usage.
-///
-/// [`OpenTelemetrySpanExt::set_parent`]: crate::OpenTelemetrySpanExt::set_parent
-/// [`OpenTelemetrySpanExt::context`]: crate::OpenTelemetrySpanExt::context
-/// [`Tracer`]: opentelemetry::trace::Tracer
-/// [`SpanBuilder`]: opentelemetry::trace::SpanBuilder
-/// [`Context`]: opentelemetry::Context
-pub trait PreSampledTracer {
- /// Produce an otel context containing an active and pre-sampled span for
- /// the given span builder data.
- ///
- /// The sampling decision, span context information, and parent context
- /// values must match the values recorded when the tracing span is closed.
- fn sampled_context(&self, data: &mut crate::OtelData) -> OtelContext;
-
- /// Generate a new trace id.
- fn new_trace_id(&self) -> otel::TraceId;
-
- /// Generate a new span id.
- fn new_span_id(&self) -> otel::SpanId;
-}
-
-impl PreSampledTracer for noop::NoopTracer {
- fn sampled_context(&self, data: &mut crate::OtelData) -> OtelContext {
- data.parent_cx.clone()
- }
-
- fn new_trace_id(&self) -> otel::TraceId {
- otel::TraceId::INVALID
- }
-
- fn new_span_id(&self) -> otel::SpanId {
- otel::SpanId::INVALID
- }
-}
-
-impl PreSampledTracer for Tracer {
- fn sampled_context(&self, data: &mut crate::OtelData) -> OtelContext {
- // Ensure tracing pipeline is still installed.
- if self.provider().is_none() {
- return OtelContext::new();
- }
- let provider = self.provider().unwrap();
- let parent_cx = &data.parent_cx;
- let builder = &mut data.builder;
-
- // Gather trace state
- let (trace_id, parent_trace_flags) = current_trace_state(builder, parent_cx, &provider);
-
- // Sample or defer to existing sampling decisions
- let (flags, trace_state) = if let Some(result) = &builder.sampling_result {
- process_sampling_result(result, parent_trace_flags)
- } else {
- builder.sampling_result = Some(provider.config().sampler.should_sample(
- Some(parent_cx),
- trace_id,
- &builder.name,
- builder.span_kind.as_ref().unwrap_or(&SpanKind::Internal),
- builder.attributes.as_ref().unwrap_or(&OrderMap::default()),
- builder.links.as_deref().unwrap_or(&[]),
- self.instrumentation_library(),
- ));
-
- process_sampling_result(
- builder.sampling_result.as_ref().unwrap(),
- parent_trace_flags,
- )
- }
- .unwrap_or_default();
-
- let span_id = builder.span_id.unwrap_or(SpanId::INVALID);
- let span_context = SpanContext::new(trace_id, span_id, flags, false, trace_state);
- parent_cx.with_remote_span_context(span_context)
- }
-
- fn new_trace_id(&self) -> otel::TraceId {
- self.provider()
- .map(|provider| provider.config().id_generator.new_trace_id())
- .unwrap_or(otel::TraceId::INVALID)
- }
-
- fn new_span_id(&self) -> otel::SpanId {
- self.provider()
- .map(|provider| provider.config().id_generator.new_span_id())
- .unwrap_or(otel::SpanId::INVALID)
- }
-}
-
-fn current_trace_state(
- builder: &SpanBuilder,
- parent_cx: &OtelContext,
- provider: &TracerProvider,
-) -> (TraceId, TraceFlags) {
- if parent_cx.has_active_span() {
- let span = parent_cx.span();
- let sc = span.span_context();
- (sc.trace_id(), sc.trace_flags())
- } else {
- (
- builder
- .trace_id
- .unwrap_or_else(|| provider.config().id_generator.new_trace_id()),
- Default::default(),
- )
- }
-}
-
-fn process_sampling_result(
- sampling_result: &SamplingResult,
- trace_flags: TraceFlags,
-) -> Option<(TraceFlags, TraceState)> {
- match sampling_result {
- SamplingResult {
- decision: SamplingDecision::Drop,
- ..
- } => None,
- SamplingResult {
- decision: SamplingDecision::RecordOnly,
- trace_state,
- ..
- } => Some((trace_flags & !TraceFlags::SAMPLED, trace_state.clone())),
- SamplingResult {
- decision: SamplingDecision::RecordAndSample,
- trace_state,
- ..
- } => Some((trace_flags | TraceFlags::SAMPLED, trace_state.clone())),
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::OtelData;
- use opentelemetry::sdk::trace::{config, Sampler, TracerProvider};
- use opentelemetry::trace::{SpanBuilder, SpanId, TracerProvider as _};
-
- #[test]
- fn assigns_default_trace_id_if_missing() {
- let provider = TracerProvider::default();
- let tracer = provider.tracer("test");
- let mut builder = SpanBuilder::from_name("empty".to_string());
- builder.span_id = Some(SpanId::from(1u64.to_be_bytes()));
- builder.trace_id = None;
- let parent_cx = OtelContext::new();
- let cx = tracer.sampled_context(&mut OtelData { builder, parent_cx });
- let span = cx.span();
- let span_context = span.span_context();
-
- assert!(span_context.is_valid());
- }
-
- #[rustfmt::skip]
- fn sampler_data() -> Vec<(&'static str, Sampler, OtelContext, Option, bool)> {
- vec![
- // No parent samples
- ("empty_parent_cx_always_on", Sampler::AlwaysOn, OtelContext::new(), None, true),
- ("empty_parent_cx_always_off", Sampler::AlwaysOff, OtelContext::new(), None, false),
-
- // Remote parent samples
- ("remote_parent_cx_always_on", Sampler::AlwaysOn, OtelContext::new().with_remote_span_context(span_context(TraceFlags::SAMPLED, true)), None, true),
- ("remote_parent_cx_always_off", Sampler::AlwaysOff, OtelContext::new().with_remote_span_context(span_context(TraceFlags::SAMPLED, true)), None, false),
- ("sampled_remote_parent_cx_parent_based", Sampler::ParentBased(Box::new(Sampler::AlwaysOff)), OtelContext::new().with_remote_span_context(span_context(TraceFlags::SAMPLED, true)), None, true),
- ("unsampled_remote_parent_cx_parent_based", Sampler::ParentBased(Box::new(Sampler::AlwaysOn)), OtelContext::new().with_remote_span_context(span_context(TraceFlags::default(), true)), None, false),
-
- // Existing sampling result defers
- ("previous_drop_result_always_on", Sampler::AlwaysOn, OtelContext::new(), Some(SamplingResult { decision: SamplingDecision::Drop, attributes: vec![], trace_state: Default::default() }), false),
- ("previous_record_and_sample_result_always_off", Sampler::AlwaysOff, OtelContext::new(), Some(SamplingResult { decision: SamplingDecision::RecordAndSample, attributes: vec![], trace_state: Default::default() }), true),
-
- // Existing local parent, defers
- ("previous_drop_result_always_on", Sampler::AlwaysOn, OtelContext::new(), Some(SamplingResult { decision: SamplingDecision::Drop, attributes: vec![], trace_state: Default::default() }), false),
- ("previous_record_and_sample_result_always_off", Sampler::AlwaysOff, OtelContext::new(), Some(SamplingResult { decision: SamplingDecision::RecordAndSample, attributes: vec![], trace_state: Default::default() }), true),
- ]
- }
-
- #[test]
- fn sampled_context() {
- for (name, sampler, parent_cx, previous_sampling_result, is_sampled) in sampler_data() {
- let provider = TracerProvider::builder()
- .with_config(config().with_sampler(sampler))
- .build();
- let tracer = provider.tracer("test");
- let mut builder = SpanBuilder::from_name("parent".to_string());
- builder.sampling_result = previous_sampling_result;
- let sampled = tracer.sampled_context(&mut OtelData { builder, parent_cx });
-
- assert_eq!(
- sampled.span().span_context().is_sampled(),
- is_sampled,
- "{}",
- name
- )
- }
- }
-
- fn span_context(trace_flags: TraceFlags, is_remote: bool) -> SpanContext {
- SpanContext::new(
- TraceId::from(1u128.to_be_bytes()),
- SpanId::from(1u64.to_be_bytes()),
- trace_flags,
- is_remote,
- Default::default(),
- )
- }
-}
diff --git a/tracing-opentelemetry/tests/metrics_publishing.rs b/tracing-opentelemetry/tests/metrics_publishing.rs
deleted file mode 100644
index 7443cb140b..0000000000
--- a/tracing-opentelemetry/tests/metrics_publishing.rs
+++ /dev/null
@@ -1,282 +0,0 @@
-use opentelemetry::{
- metrics::MetricsError,
- sdk::{
- export::metrics::{
- aggregation::{self, Histogram, Sum, TemporalitySelector},
- InstrumentationLibraryReader,
- },
- metrics::{
- aggregators::{HistogramAggregator, SumAggregator},
- controllers::BasicController,
- processors,
- sdk_api::{Descriptor, InstrumentKind, Number, NumberKind},
- selectors,
- },
- },
- Context,
-};
-use std::cmp::Ordering;
-use tracing::Collect;
-use tracing_opentelemetry::MetricsSubscriber;
-use tracing_subscriber::prelude::*;
-
-const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
-const INSTRUMENTATION_LIBRARY_NAME: &str = "tracing/tracing-opentelemetry";
-
-#[tokio::test]
-async fn u64_counter_is_exported() {
- let (subscriber, exporter) = init_subscriber(
- "hello_world".to_string(),
- InstrumentKind::Counter,
- NumberKind::U64,
- Number::from(1_u64),
- );
-
- tracing::collect::with_default(subscriber, || {
- tracing::info!(monotonic_counter.hello_world = 1_u64);
- });
-
- exporter.export().unwrap();
-}
-
-#[tokio::test]
-async fn u64_counter_is_exported_i64_at_instrumentation_point() {
- let (subscriber, exporter) = init_subscriber(
- "hello_world2".to_string(),
- InstrumentKind::Counter,
- NumberKind::U64,
- Number::from(1_u64),
- );
-
- tracing::collect::with_default(subscriber, || {
- tracing::info!(monotonic_counter.hello_world2 = 1_i64);
- });
-
- exporter.export().unwrap();
-}
-
-#[tokio::test]
-async fn f64_counter_is_exported() {
- let (subscriber, exporter) = init_subscriber(
- "float_hello_world".to_string(),
- InstrumentKind::Counter,
- NumberKind::F64,
- Number::from(1.000000123_f64),
- );
-
- tracing::collect::with_default(subscriber, || {
- tracing::info!(monotonic_counter.float_hello_world = 1.000000123_f64);
- });
-
- exporter.export().unwrap();
-}
-
-#[tokio::test]
-async fn i64_up_down_counter_is_exported() {
- let (subscriber, exporter) = init_subscriber(
- "pebcak".to_string(),
- InstrumentKind::UpDownCounter,
- NumberKind::I64,
- Number::from(-5_i64),
- );
-
- tracing::collect::with_default(subscriber, || {
- tracing::info!(counter.pebcak = -5_i64);
- });
-
- exporter.export().unwrap();
-}
-
-#[tokio::test]
-async fn i64_up_down_counter_is_exported_u64_at_instrumentation_point() {
- let (subscriber, exporter) = init_subscriber(
- "pebcak2".to_string(),
- InstrumentKind::UpDownCounter,
- NumberKind::I64,
- Number::from(5_i64),
- );
-
- tracing::collect::with_default(subscriber, || {
- tracing::info!(counter.pebcak2 = 5_u64);
- });
-
- exporter.export().unwrap();
-}
-
-#[tokio::test]
-async fn f64_up_down_counter_is_exported() {
- let (subscriber, exporter) = init_subscriber(
- "pebcak_blah".to_string(),
- InstrumentKind::UpDownCounter,
- NumberKind::F64,
- Number::from(99.123_f64),
- );
-
- tracing::collect::with_default(subscriber, || {
- tracing::info!(counter.pebcak_blah = 99.123_f64);
- });
-
- exporter.export().unwrap();
-}
-
-#[tokio::test]
-async fn u64_histogram_is_exported() {
- let (subscriber, exporter) = init_subscriber(
- "abcdefg".to_string(),
- InstrumentKind::Histogram,
- NumberKind::U64,
- Number::from(9_u64),
- );
-
- tracing::collect::with_default(subscriber, || {
- tracing::info!(histogram.abcdefg = 9_u64);
- });
-
- exporter.export().unwrap();
-}
-
-#[tokio::test]
-async fn i64_histogram_is_exported() {
- let (subscriber, exporter) = init_subscriber(
- "abcdefg_auenatsou".to_string(),
- InstrumentKind::Histogram,
- NumberKind::I64,
- Number::from(-19_i64),
- );
-
- tracing::collect::with_default(subscriber, || {
- tracing::info!(histogram.abcdefg_auenatsou = -19_i64);
- });
-
- exporter.export().unwrap();
-}
-
-#[tokio::test]
-async fn f64_histogram_is_exported() {
- let (subscriber, exporter) = init_subscriber(
- "abcdefg_racecar".to_string(),
- InstrumentKind::Histogram,
- NumberKind::F64,
- Number::from(777.0012_f64),
- );
-
- tracing::collect::with_default(subscriber, || {
- tracing::info!(histogram.abcdefg_racecar = 777.0012_f64);
- });
-
- exporter.export().unwrap();
-}
-
-fn init_subscriber(
- expected_metric_name: String,
- expected_instrument_kind: InstrumentKind,
- expected_number_kind: NumberKind,
- expected_value: Number,
-) -> (impl Collect + 'static, TestExporter) {
- let controller = opentelemetry::sdk::metrics::controllers::basic(processors::factory(
- selectors::simple::histogram(vec![-10.0, 100.0]),
- aggregation::cumulative_temporality_selector(),
- ))
- .build();
-
- let exporter = TestExporter {
- expected_metric_name,
- expected_instrument_kind,
- expected_number_kind,
- expected_value,
- controller: controller.clone(),
- };
-
- (
- tracing_subscriber::registry().with(MetricsSubscriber::new(controller)),
- exporter,
- )
-}
-
-#[derive(Clone, Debug)]
-struct TestExporter {
- expected_metric_name: String,
- expected_instrument_kind: InstrumentKind,
- expected_number_kind: NumberKind,
- expected_value: Number,
- controller: BasicController,
-}
-
-impl TestExporter {
- fn export(&self) -> Result<(), MetricsError> {
- self.controller.collect(&Context::current())?;
- self.controller.try_for_each(&mut |library, reader| {
- reader.try_for_each(self, &mut |record| {
- assert_eq!(self.expected_metric_name, record.descriptor().name());
- assert_eq!(
- self.expected_instrument_kind,
- *record.descriptor().instrument_kind()
- );
- assert_eq!(
- self.expected_number_kind,
- *record.descriptor().number_kind()
- );
- match self.expected_instrument_kind {
- InstrumentKind::Counter | InstrumentKind::UpDownCounter => {
- let number = record
- .aggregator()
- .unwrap()
- .as_any()
- .downcast_ref::()
- .unwrap()
- .sum()
- .unwrap();
-
- assert_eq!(
- Ordering::Equal,
- number
- .partial_cmp(&NumberKind::U64, &self.expected_value)
- .unwrap()
- );
- }
- InstrumentKind::Histogram => {
- let histogram = record
- .aggregator()
- .unwrap()
- .as_any()
- .downcast_ref::()
- .unwrap()
- .histogram()
- .unwrap();
-
- let counts = histogram.counts();
- if dbg!(self.expected_value.to_i64(&self.expected_number_kind)) > 100 {
- assert_eq!(counts, &[0.0, 0.0, 1.0]);
- } else if self.expected_value.to_i64(&self.expected_number_kind) > 0 {
- assert_eq!(counts, &[0.0, 1.0, 0.0]);
- } else {
- assert_eq!(counts, &[1.0, 0.0, 0.0]);
- }
- }
- _ => panic!(
- "InstrumentKind {:?} not currently supported!",
- self.expected_instrument_kind
- ),
- };
-
- // The following are the same regardless of the individual metric.
- assert_eq!(INSTRUMENTATION_LIBRARY_NAME, library.name);
- assert_eq!(CARGO_PKG_VERSION, library.version.as_ref().unwrap());
-
- Ok(())
- })
- })
- }
-}
-
-impl TemporalitySelector for TestExporter {
- fn temporality_for(
- &self,
- _descriptor: &Descriptor,
- _kind: &aggregation::AggregationKind,
- ) -> aggregation::Temporality {
- // I don't think the value here makes a difference since
- // we are just testing a single metric.
- aggregation::Temporality::Cumulative
- }
-}
diff --git a/tracing-opentelemetry/tests/trace_state_propagation.rs b/tracing-opentelemetry/tests/trace_state_propagation.rs
deleted file mode 100644
index bc80e1364b..0000000000
--- a/tracing-opentelemetry/tests/trace_state_propagation.rs
+++ /dev/null
@@ -1,171 +0,0 @@
-use futures_util::future::BoxFuture;
-use opentelemetry::{
- propagation::TextMapPropagator,
- sdk::{
- export::trace::{ExportResult, SpanData, SpanExporter},
- propagation::{BaggagePropagator, TextMapCompositePropagator, TraceContextPropagator},
- trace::{Tracer, TracerProvider},
- },
- trace::{SpanContext, TraceContextExt, Tracer as _, TracerProvider as _},
- Context,
-};
-use std::collections::{HashMap, HashSet};
-use std::sync::{Arc, Mutex};
-use tracing::Collect;
-use tracing_opentelemetry::{subscriber, OpenTelemetrySpanExt};
-use tracing_subscriber::prelude::*;
-
-#[test]
-fn trace_with_active_otel_context() {
- let (cx, subscriber, exporter, provider) = build_sampled_context();
- let attached = cx.attach();
-
- tracing::collect::with_default(subscriber, || {
- tracing::debug_span!("child");
- });
-
- drop(attached); // end implicit parent
- drop(provider); // flush all spans
-
- let spans = exporter.0.lock().unwrap();
- assert_eq!(spans.len(), 2);
- assert_shared_attrs_eq(&spans[0].span_context, &spans[1].span_context);
-}
-
-#[test]
-fn trace_with_assigned_otel_context() {
- let (cx, subscriber, exporter, provider) = build_sampled_context();
-
- tracing::collect::with_default(subscriber, || {
- let child = tracing::debug_span!("child");
- child.set_parent(cx);
- });
-
- drop(provider); // flush all spans
- let spans = exporter.0.lock().unwrap();
- assert_eq!(spans.len(), 2);
- assert_shared_attrs_eq(&spans[0].span_context, &spans[1].span_context);
-}
-
-#[test]
-fn trace_root_with_children() {
- let (_tracer, provider, exporter, subscriber) = test_tracer();
-
- tracing::collect::with_default(subscriber, || {
- // Propagate trace information through tracing parent -> child
- let root = tracing::debug_span!("root");
- root.in_scope(|| tracing::debug_span!("child"));
- });
-
- drop(provider); // flush all spans
- let spans = exporter.0.lock().unwrap();
- assert_eq!(spans.len(), 2);
- assert_shared_attrs_eq(&spans[0].span_context, &spans[1].span_context);
-}
-
-#[test]
-fn inject_context_into_outgoing_requests() {
- let (_tracer, _provider, _exporter, subscriber) = test_tracer();
- let propagator = test_propagator();
- let carrier = test_carrier();
- let cx = propagator.extract(&carrier);
- let mut outgoing_req_carrier = HashMap::new();
-
- tracing::collect::with_default(subscriber, || {
- let root = tracing::debug_span!("root");
- root.set_parent(cx);
- let _g = root.enter();
- let child = tracing::debug_span!("child");
- propagator.inject_context(&child.context(), &mut outgoing_req_carrier);
- });
-
- // Ensure all values that should be passed between services are preserved
- assert_carrier_attrs_eq(&carrier, &outgoing_req_carrier);
-}
-
-fn assert_shared_attrs_eq(sc_a: &SpanContext, sc_b: &SpanContext) {
- assert_eq!(sc_a.trace_id(), sc_b.trace_id());
- assert_eq!(sc_a.trace_state(), sc_b.trace_state());
-}
-
-fn assert_carrier_attrs_eq(
- carrier_a: &HashMap,
- carrier_b: &HashMap,
-) {
- // Match baggage unordered
- assert_eq!(
- carrier_a
- .get("baggage")
- .map(|b| b.split_terminator(',').collect::>()),
- carrier_b
- .get("baggage")
- .map(|b| b.split_terminator(',').collect())
- );
- // match trace parent values, except span id
- assert_eq!(
- carrier_a.get("traceparent").unwrap()[0..36],
- carrier_b.get("traceparent").unwrap()[0..36],
- );
- // match tracestate values
- assert_eq!(carrier_a.get("tracestate"), carrier_b.get("tracestate"));
-}
-
-fn test_tracer() -> (Tracer, TracerProvider, TestExporter, impl Collect) {
- let exporter = TestExporter::default();
- let provider = TracerProvider::builder()
- .with_simple_exporter(exporter.clone())
- .build();
- let tracer = provider.tracer("test");
- let subscriber = tracing_subscriber::registry().with(subscriber().with_tracer(tracer.clone()));
-
- (tracer, provider, exporter, subscriber)
-}
-
-fn test_propagator() -> TextMapCompositePropagator {
- let baggage_propagator = BaggagePropagator::new();
- let trace_context_propagator = TraceContextPropagator::new();
-
- TextMapCompositePropagator::new(vec![
- Box::new(baggage_propagator),
- Box::new(trace_context_propagator),
- ])
-}
-
-fn test_carrier() -> HashMap {
- let mut carrier = HashMap::new();
- carrier.insert(
- "baggage".to_string(),
- "key2=value2,key1=value1;property1;property2,key3=value3;propertyKey=propertyValue"
- .to_string(),
- );
- carrier.insert("tracestate".to_string(), "test1=test2".to_string());
- carrier.insert(
- "traceparent".to_string(),
- "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01".to_string(),
- );
-
- carrier
-}
-
-fn build_sampled_context() -> (Context, impl Collect, TestExporter, TracerProvider) {
- let (tracer, provider, exporter, subscriber) = test_tracer();
- let span = tracer.start("sampled");
- let cx = Context::current_with_span(span);
-
- (cx, subscriber, exporter, provider)
-}
-
-#[derive(Clone, Default, Debug)]
-struct TestExporter(Arc>>);
-
-impl SpanExporter for TestExporter {
- fn export(&mut self, mut batch: Vec) -> BoxFuture<'static, ExportResult> {
- let spans = self.0.clone();
- Box::pin(async move {
- if let Ok(mut inner) = spans.lock() {
- inner.append(&mut batch);
- }
- Ok(())
- })
- }
-}
diff --git a/tracing-opentelemetry/trace.png b/tracing-opentelemetry/trace.png
deleted file mode 100644
index 4cb98d1358..0000000000
Binary files a/tracing-opentelemetry/trace.png and /dev/null differ
diff --git a/tracing-serde/Cargo.toml b/tracing-serde/Cargo.toml
index 41154f7c09..7b027fc5ac 100644
--- a/tracing-serde/Cargo.toml
+++ b/tracing-serde/Cargo.toml
@@ -16,7 +16,7 @@ categories = [
"encoding",
]
keywords = ["logging", "tracing", "serialization"]
-rust-version = "1.49.0"
+rust-version = "1.56.0"
[features]
default = ["std"]
diff --git a/tracing-serde/README.md b/tracing-serde/README.md
index f02a880428..b52b9ae88a 100644
--- a/tracing-serde/README.md
+++ b/tracing-serde/README.md
@@ -36,7 +36,7 @@ and tracing data to monitor your services in production.
The `tracing` crate provides the APIs necessary for instrumenting
libraries and applications to emit trace data.
-*Compiler support: [requires `rustc` 1.49+][msrv]*
+*Compiler support: [requires `rustc` 1.56+][msrv]*
[msrv]: #supported-rust-versions
@@ -113,14 +113,14 @@ The following crate feature flags are available:
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.49. The current Tracing version is not guaranteed to build on Rust
+version is 1.56. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
project. The current stable Rust compiler and the three most recent minor
versions before it will always be supported. For example, if the current stable
-compiler version is 1.45, the minimum supported version will not be increased
-past 1.42, three minor versions prior. Increasing the minimum supported compiler
+compiler version is 1.69, the minimum supported version will not be increased
+past 1.66, three minor versions prior. Increasing the minimum supported compiler
version is not considered a semver breaking change as long as doing so complies
with this policy.
diff --git a/tracing-serde/src/lib.rs b/tracing-serde/src/lib.rs
index c01490d5c0..640c8d7886 100644
--- a/tracing-serde/src/lib.rs
+++ b/tracing-serde/src/lib.rs
@@ -32,7 +32,7 @@
//! The `tracing` crate provides the APIs necessary for instrumenting
//! libraries and applications to emit trace data.
//!
-//! *Compiler support: [requires `rustc` 1.49+][msrv]*
+//! *Compiler support: [requires `rustc` 1.56+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//!
@@ -128,14 +128,14 @@
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.49. The current Tracing version is not guaranteed to build on
+//! version is 1.56. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
//! project. The current stable Rust compiler and the three most recent minor
//! versions before it will always be supported. For example, if the current
-//! stable compiler version is 1.45, the minimum supported version will not be
-//! increased past 1.42, three minor versions prior. Increasing the minimum
+//! stable compiler version is 1.69, the minimum supported version will not be
+//! increased past 1.66, three minor versions prior. Increasing the minimum
//! supported compiler version is not considered a semver breaking change as
//! long as doing so complies with this policy.
//!
diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml
index a93d231dd1..100da643ca 100644
--- a/tracing-subscriber/Cargo.toml
+++ b/tracing-subscriber/Cargo.toml
@@ -20,7 +20,7 @@ categories = [
"asynchronous",
]
keywords = ["logging", "tracing", "metrics", "subscriber"]
-rust-version = "1.50.0"
+rust-version = "1.56.0"
[features]
@@ -42,7 +42,7 @@ tracing-core = { path = "../tracing-core", version = "0.2", default-features = f
# only required by the `env-filter` feature
tracing = { optional = true, path = "../tracing", version = "0.2", default-features = false }
matchers = { optional = true, version = "0.1.0" }
-regex = { optional = true, version = "1.6.0", default-features = false, features = ["std"] }
+regex = { optional = true, version = "1.6.0", default-features = false, features = ["std", "unicode-case", "unicode-perl"] }
smallvec = { optional = true, version = "1.9.0" }
once_cell = { optional = true, version = "1.13.0" }
diff --git a/tracing-subscriber/README.md b/tracing-subscriber/README.md
index 5f4d2e91b7..9e5c0b645e 100644
--- a/tracing-subscriber/README.md
+++ b/tracing-subscriber/README.md
@@ -32,21 +32,21 @@ Utilities for implementing and composing [`tracing`][tracing] subscribers.
[discord-url]: https://discord.gg/EeF3cQw
[maint-badge]: https://img.shields.io/badge/maintenance-experimental-blue.svg
-*Compiler support: [requires `rustc` 1.50+][msrv]*
+*Compiler support: [requires `rustc` 1.56+][msrv]*
[msrv]: #supported-rust-versions
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.50. The current Tracing version is not guaranteed to build on Rust
+version is 1.56. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
project. The current stable Rust compiler and the three most recent minor
versions before it will always be supported. For example, if the current stable
-compiler version is 1.45, the minimum supported version will not be increased
-past 1.42, three minor versions prior. Increasing the minimum supported compiler
+compiler version is 1.69, the minimum supported version will not be increased
+past 1.66, three minor versions prior. Increasing the minimum supported compiler
version is not considered a semver breaking change as long as doing so complies
with this policy.
diff --git a/tracing-subscriber/src/filter/targets.rs b/tracing-subscriber/src/filter/targets.rs
index 9710096c9f..d26c428a73 100644
--- a/tracing-subscriber/src/filter/targets.rs
+++ b/tracing-subscriber/src/filter/targets.rs
@@ -325,7 +325,7 @@ impl Targets {
/// assert_eq!(filter.default_level(), Some(LevelFilter::OFF));
/// ```
pub fn default_level(&self) -> Option {
- self.0.directives().into_iter().find_map(|d| {
+ self.0.directives().find_map(|d| {
if d.target.is_none() {
Some(d.level)
} else {
diff --git a/tracing-subscriber/src/fmt/fmt_subscriber.rs b/tracing-subscriber/src/fmt/fmt_subscriber.rs
index 9887b711ff..1b89039261 100644
--- a/tracing-subscriber/src/fmt/fmt_subscriber.rs
+++ b/tracing-subscriber/src/fmt/fmt_subscriber.rs
@@ -274,10 +274,32 @@ impl Subscriber {
}
}
- /// Enable ANSI terminal colors for formatted output.
- #[cfg(feature = "ansi")]
- #[cfg_attr(docsrs, doc(cfg(feature = "ansi")))]
+ /// Sets whether or not the formatter emits ANSI terminal escape codes
+ /// for colors and other text formatting.
+ ///
+ /// Enabling ANSI escapes (calling `with_ansi(true)`) requires the "ansi"
+ /// crate feature flag. Calling `with_ansi(true)` without the "ansi"
+ /// feature flag enabled will panic if debug assertions are enabled, or
+ /// print a warning otherwise.
+ ///
+ /// This method itself is still available without the feature flag. This
+ /// is to allow ANSI escape codes to be explicitly *disabled* without
+ /// having to opt-in to the dependencies required to emit ANSI formatting.
+ /// This way, code which constructs a formatter that should never emit
+ /// ANSI escape codes can ensure that they are not used, regardless of
+ /// whether or not other crates in the dependency graph enable the "ansi"
+ /// feature flag.
pub fn with_ansi(self, ansi: bool) -> Self {
+ #[cfg(not(feature = "ansi"))]
+ if ansi {
+ const ERROR: &str =
+ "tracing-subscriber: the `ansi` crate feature is required to enable ANSI terminal colors";
+ #[cfg(debug_assertions)]
+ panic!("{}", ERROR);
+ #[cfg(not(debug_assertions))]
+ eprintln!("{}", ERROR);
+ }
+
Subscriber {
is_ansi: ansi,
..self
diff --git a/tracing-subscriber/src/fmt/format/mod.rs b/tracing-subscriber/src/fmt/format/mod.rs
index ed0a697a65..c9760138ab 100644
--- a/tracing-subscriber/src/fmt/format/mod.rs
+++ b/tracing-subscriber/src/fmt/format/mod.rs
@@ -1065,15 +1065,14 @@ where
write!(writer, "{:0>2?} ", std::thread::current().id())?;
}
- let bold = writer.bold();
let dimmed = writer.dimmed();
if self.display_target {
- write!(writer, "{}{}", bold.paint(meta.target()), dimmed.paint(":"))?;
+ write!(writer, "{}{}", dimmed.paint(meta.target()), dimmed.paint(":"))?;
}
if self.display_filename {
if let Some(filename) = meta.file() {
- write!(writer, "{}{}", bold.paint(filename), dimmed.paint(":"))?;
+ write!(writer, "{}{}", dimmed.paint(filename), dimmed.paint(":"))?;
}
}
@@ -1082,9 +1081,9 @@ where
write!(
writer,
"{}{}{}{}",
- bold.prefix(),
+ dimmed.prefix(),
line_number,
- bold.suffix(),
+ dimmed.suffix(),
dimmed.paint(":")
)?;
}
diff --git a/tracing-subscriber/src/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs
index 936ecd6400..38acf4d092 100644
--- a/tracing-subscriber/src/fmt/mod.rs
+++ b/tracing-subscriber/src/fmt/mod.rs
@@ -16,7 +16,7 @@
//! tracing-subscriber = "0.3"
//! ```
//!
-//! *Compiler support: [requires `rustc` 1.49+][msrv]*
+//! *Compiler support: [requires `rustc` 1.56+][msrv]*
//!
//! [msrv]: ../index.html#supported-rust-versions
//!
@@ -610,9 +610,21 @@ where
}
}
- /// Enable ANSI terminal colors for formatted output.
- #[cfg(feature = "ansi")]
- #[cfg_attr(docsrs, doc(cfg(feature = "ansi")))]
+ /// Sets whether or not the formatter emits ANSI terminal escape codes
+ /// for colors and other text formatting.
+ ///
+ /// Enabling ANSI escapes (calling `with_ansi(true)`) requires the "ansi"
+ /// crate feature flag. Calling `with_ansi(true)` without the "ansi"
+ /// feature flag enabled will panic if debug assertions are enabled, or
+ /// print a warning otherwise.
+ ///
+ /// This method itself is still available without the feature flag. This
+ /// is to allow ANSI escape codes to be explicitly *disabled* without
+ /// having to opt-in to the dependencies required to emit ANSI formatting.
+ /// This way, code which constructs a formatter that should never emit
+ /// ANSI escape codes can ensure that they are not used, regardless of
+ /// whether or not other crates in the dependency graph enable the "ansi"
+ /// feature flag.
pub fn with_ansi(self, ansi: bool) -> CollectorBuilder, F, W> {
CollectorBuilder {
inner: self.inner.with_ansi(ansi),
diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs
index 6f10eac97f..5e7787028c 100644
--- a/tracing-subscriber/src/lib.rs
+++ b/tracing-subscriber/src/lib.rs
@@ -10,7 +10,7 @@
//! `tracing-subscriber` is intended for use by both `Collector` authors and
//! application authors using `tracing` to instrument their applications.
//!
-//! *Compiler support: [requires `rustc` 1.50+][msrv]*
+//! *Compiler support: [requires `rustc` 1.56+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//!
@@ -106,14 +106,14 @@
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.50. The current Tracing version is not guaranteed to build on
+//! version is 1.56. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
//! project. The current stable Rust compiler and the three most recent minor
//! versions before it will always be supported. For example, if the current
-//! stable compiler version is 1.45, the minimum supported version will not be
-//! increased past 1.42, three minor versions prior. Increasing the minimum
+//! stable compiler version is 1.69, the minimum supported version will not be
+//! increased past 1.66, three minor versions prior. Increasing the minimum
//! supported compiler version is not considered a semver breaking change as
//! long as doing so complies with this policy.
//!
diff --git a/tracing-tower/Cargo.toml b/tracing-tower/Cargo.toml
index 7520c5d7b8..970886ae84 100644
--- a/tracing-tower/Cargo.toml
+++ b/tracing-tower/Cargo.toml
@@ -15,7 +15,7 @@ categories = [
]
keywords = ["logging", "tracing"]
license = "MIT"
-rust-version = "1.49.0"
+rust-version = "1.56.0"
[features]
default = ["tower-layer", "tower-make"]
diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml
index 6c43ff6c70..2d7bf1120e 100644
--- a/tracing/Cargo.toml
+++ b/tracing/Cargo.toml
@@ -28,17 +28,17 @@ categories = [
]
keywords = ["logging", "tracing", "metrics", "async"]
edition = "2018"
-rust-version = "1.49.0"
+rust-version = "1.56.0"
[dependencies]
tracing-core = { path = "../tracing-core", version = "0.2", default-features = false }
log = { version = "0.4.17", optional = true }
tracing-attributes = { path = "../tracing-attributes", version = "0.2", optional = true }
-cfg-if = "1.0.0"
pin-project-lite = "0.2.9"
[dev-dependencies]
criterion = { version = "0.3.6", default_features = false }
+futures = { version = "0.3.21", default_features = false }
log = "0.4.17"
tracing-mock = { path = "../tracing-mock" }
diff --git a/tracing/README.md b/tracing/README.md
index a4dea4f724..8f7f0a6241 100644
--- a/tracing/README.md
+++ b/tracing/README.md
@@ -47,7 +47,7 @@ data as well as textual messages.
The `tracing` crate provides the APIs necessary for instrumenting libraries
and applications to emit trace data.
-*Compiler support: [requires `rustc` 1.49+][msrv]*
+*Compiler support: [requires `rustc` 1.56+][msrv]*
[msrv]: #supported-rust-versions
@@ -425,14 +425,14 @@ undergoing active development. They may be less stable than `tracing` and
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.49. The current Tracing version is not guaranteed to build on Rust
+version is 1.56. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
project. The current stable Rust compiler and the three most recent minor
versions before it will always be supported. For example, if the current stable
-compiler version is 1.45, the minimum supported version will not be increased
-past 1.42, three minor versions prior. Increasing the minimum supported compiler
+compiler version is 1.69, the minimum supported version will not be increased
+past 1.66, three minor versions prior. Increasing the minimum supported compiler
version is not considered a semver breaking change as long as doing so complies
with this policy.
diff --git a/tracing/src/instrument.rs b/tracing/src/instrument.rs
index 98f0da9bd1..208c2bd0cf 100644
--- a/tracing/src/instrument.rs
+++ b/tracing/src/instrument.rs
@@ -1,7 +1,11 @@
use crate::span::Span;
-use core::pin::Pin;
-use core::task::{Context, Poll};
-use core::{future::Future, marker::Sized};
+use core::{
+ future::Future,
+ marker::Sized,
+ mem::{self, ManuallyDrop},
+ pin::Pin,
+ task::{Context, Poll},
+};
use pin_project_lite::pin_project;
#[cfg(feature = "std")]
@@ -18,7 +22,7 @@ pub trait Instrument: Sized {
/// `Instrumented` wrapper.
///
/// The attached [`Span`] will be [entered] every time the instrumented
- /// [`Future`] is polled.
+ /// [`Future`] is polled or [`Drop`]ped.
///
/// # Examples
///
@@ -80,14 +84,17 @@ pub trait Instrument: Sized {
/// [disabled]: super::Span::is_disabled()
/// [`Future`]: std::future::Future
fn instrument(self, span: Span) -> Instrumented {
- Instrumented { inner: self, span }
+ Instrumented {
+ inner: ManuallyDrop::new(self),
+ span,
+ }
}
/// Instruments this type with the [current] [`Span`], returning an
/// `Instrumented` wrapper.
///
/// The attached [`Span`] will be [entered] every time the instrumented
- /// [`Future`] is polled.
+ /// [`Future`] is polled or [`Drop`]ped.
///
/// This can be used to propagate the current span when spawning a new future.
///
@@ -285,13 +292,55 @@ pin_project! {
///
/// [`Future`]: std::future::Future
/// [`Span`]: crate::Span
+ #[project = InstrumentedProj]
+ #[project_ref = InstrumentedProjRef]
#[derive(Debug, Clone)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Instrumented {
+ // `ManuallyDrop` is used here to to enter instrument `Drop` by entering
+ // `Span` and executing `ManuallyDrop::drop`.
#[pin]
- inner: T,
+ inner: ManuallyDrop,
span: Span,
}
+
+ impl PinnedDrop for Instrumented {
+ fn drop(this: Pin<&mut Self>) {
+ let this = this.project();
+ let _enter = this.span.enter();
+ // SAFETY: 1. `Pin::get_unchecked_mut()` is safe, because this isn't
+ // different from wrapping `T` in `Option` and calling
+ // `Pin::set(&mut this.inner, None)`, except avoiding
+ // additional memory overhead.
+ // 2. `ManuallyDrop::drop()` is safe, because
+ // `PinnedDrop::drop()` is guaranteed to be called only
+ // once.
+ unsafe { ManuallyDrop::drop(this.inner.get_unchecked_mut()) }
+ }
+ }
+}
+
+impl<'a, T> InstrumentedProj<'a, T> {
+ /// Get a mutable reference to the [`Span`] a pinned mutable reference to
+ /// the wrapped type.
+ fn span_and_inner_pin_mut(self) -> (&'a mut Span, Pin<&'a mut T>) {
+ // SAFETY: As long as `ManuallyDrop` does not move, `T` won't move
+ // and `inner` is valid, because `ManuallyDrop::drop` is called
+ // only inside `Drop` of the `Instrumented`.
+ let inner = unsafe { self.inner.map_unchecked_mut(|v| &mut **v) };
+ (self.span, inner)
+ }
+}
+
+impl<'a, T> InstrumentedProjRef<'a, T> {
+ /// Get a reference to the [`Span`] a pinned reference to the wrapped type.
+ fn span_and_inner_pin_ref(self) -> (&'a Span, Pin<&'a T>) {
+ // SAFETY: As long as `ManuallyDrop` does not move, `T` won't move
+ // and `inner` is valid, because `ManuallyDrop::drop` is called
+ // only inside `Drop` of the `Instrumented`.
+ let inner = unsafe { self.inner.map_unchecked(|v| &**v) };
+ (self.span, inner)
+ }
}
// === impl Instrumented ===
@@ -300,9 +349,9 @@ impl Future for Instrumented {
type Output = T::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll {
- let this = self.project();
- let _enter = this.span.enter();
- this.inner.poll(cx)
+ let (span, inner) = self.project().span_and_inner_pin_mut();
+ let _enter = span.enter();
+ inner.poll(cx)
}
}
@@ -331,19 +380,30 @@ impl Instrumented {
/// Get a pinned reference to the wrapped type.
pub fn inner_pin_ref(self: Pin<&Self>) -> Pin<&T> {
- self.project_ref().inner
+ self.project_ref().span_and_inner_pin_ref().1
}
/// Get a pinned mutable reference to the wrapped type.
pub fn inner_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
- self.project().inner
+ self.project().span_and_inner_pin_mut().1
}
/// Consumes the `Instrumented`, returning the wrapped type.
///
/// Note that this drops the span.
pub fn into_inner(self) -> T {
- self.inner
+ // To manually destructure `Instrumented` without `Drop`, we save
+ // pointers to the fields and use `mem::forget` to leave those pointers
+ // valid.
+ let span: *const Span = &self.span;
+ let inner: *const ManuallyDrop = &self.inner;
+ mem::forget(self);
+ // SAFETY: Those pointers are valid for reads, because `Drop` didn't
+ // run, and properly aligned, because `Instrumented` isn't
+ // `#[repr(packed)]`.
+ let _span = unsafe { span.read() };
+ let inner = unsafe { inner.read() };
+ ManuallyDrop::into_inner(inner)
}
}
diff --git a/tracing/src/level_filters.rs b/tracing/src/level_filters.rs
index 4181dc3bf7..4e56ada2c5 100644
--- a/tracing/src/level_filters.rs
+++ b/tracing/src/level_filters.rs
@@ -63,32 +63,36 @@ pub use tracing_core::{metadata::ParseLevelFilterError, LevelFilter};
/// determine if those spans or events are enabled.
///
/// [module-level documentation]: self#compile-time-filters
-pub const STATIC_MAX_LEVEL: LevelFilter = MAX_LEVEL;
+pub const STATIC_MAX_LEVEL: LevelFilter = get_max_level_inner();
-cfg_if::cfg_if! {
- if #[cfg(all(not(debug_assertions), feature = "release_max_level_off"))] {
- const MAX_LEVEL: LevelFilter = LevelFilter::OFF;
- } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_error"))] {
- const MAX_LEVEL: LevelFilter = LevelFilter::ERROR;
- } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_warn"))] {
- const MAX_LEVEL: LevelFilter = LevelFilter::WARN;
- } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_info"))] {
- const MAX_LEVEL: LevelFilter = LevelFilter::INFO;
- } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_debug"))] {
- const MAX_LEVEL: LevelFilter = LevelFilter::DEBUG;
- } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_trace"))] {
- const MAX_LEVEL: LevelFilter = LevelFilter::TRACE;
- } else if #[cfg(feature = "max_level_off")] {
- const MAX_LEVEL: LevelFilter = LevelFilter::OFF;
- } else if #[cfg(feature = "max_level_error")] {
- const MAX_LEVEL: LevelFilter = LevelFilter::ERROR;
- } else if #[cfg(feature = "max_level_warn")] {
- const MAX_LEVEL: LevelFilter = LevelFilter::WARN;
- } else if #[cfg(feature = "max_level_info")] {
- const MAX_LEVEL: LevelFilter = LevelFilter::INFO;
- } else if #[cfg(feature = "max_level_debug")] {
- const MAX_LEVEL: LevelFilter = LevelFilter::DEBUG;
+const fn get_max_level_inner() -> LevelFilter {
+ if cfg!(not(debug_assertions)) {
+ if cfg!(feature = "release_max_level_off") {
+ LevelFilter::OFF
+ } else if cfg!(feature = "release_max_level_error") {
+ LevelFilter::ERROR
+ } else if cfg!(feature = "release_max_level_warn") {
+ LevelFilter::WARN
+ } else if cfg!(feature = "release_max_level_info") {
+ LevelFilter::INFO
+ } else if cfg!(feature = "release_max_level_debug") {
+ LevelFilter::DEBUG
+ } else {
+ // Same as branch cfg!(feature = "release_max_level_trace")
+ LevelFilter::TRACE
+ }
+ } else if cfg!(feature = "max_level_off") {
+ LevelFilter::OFF
+ } else if cfg!(feature = "max_level_error") {
+ LevelFilter::ERROR
+ } else if cfg!(feature = "max_level_warn") {
+ LevelFilter::WARN
+ } else if cfg!(feature = "max_level_info") {
+ LevelFilter::INFO
+ } else if cfg!(feature = "max_level_debug") {
+ LevelFilter::DEBUG
} else {
- const MAX_LEVEL: LevelFilter = LevelFilter::TRACE;
+ // Same as branch cfg!(feature = "max_level_trace")
+ LevelFilter::TRACE
}
}
diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs
index 21411c7df4..49b3ffc5a5 100644
--- a/tracing/src/lib.rs
+++ b/tracing/src/lib.rs
@@ -19,7 +19,7 @@
//! The `tracing` crate provides the APIs necessary for instrumenting libraries
//! and applications to emit trace data.
//!
-//! *Compiler support: [requires `rustc` 1.49+][msrv]*
+//! *Compiler support: [requires `rustc` 1.56+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//! # Core Concepts
@@ -880,14 +880,14 @@
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.49. The current Tracing version is not guaranteed to build on
+//! version is 1.56. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
//! project. The current stable Rust compiler and the three most recent minor
//! versions before it will always be supported. For example, if the current
-//! stable compiler version is 1.45, the minimum supported version will not be
-//! increased past 1.42, three minor versions prior. Increasing the minimum
+//! stable compiler version is 1.69, the minimum supported version will not be
+//! increased past 1.66, three minor versions prior. Increasing the minimum
//! supported compiler version is not considered a semver breaking change as
//! long as doing so complies with this policy.
//!
diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs
index 8b93c14546..138a80a40d 100644
--- a/tracing/src/macros.rs
+++ b/tracing/src/macros.rs
@@ -2314,7 +2314,7 @@ macro_rules! valueset {
)
};
- // Remainder is unparseable, but exists --- must be format args!
+ // Remainder is unparsable, but exists --- must be format args!
(@ { $(,)* $($out:expr),* }, $next:expr, $($rest:tt)+) => {
$crate::valueset!(@ { (&$next, Some(&format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, )
};
diff --git a/tracing/tests/instrument.rs b/tracing/tests/instrument.rs
new file mode 100644
index 0000000000..9cb4dfbb3f
--- /dev/null
+++ b/tracing/tests/instrument.rs
@@ -0,0 +1,67 @@
+// These tests require the thread-local scoped dispatcher, which only works when
+// we have a standard library. The behaviour being tested should be the same
+// with the standard lib disabled.
+#![cfg(feature = "std")]
+
+use std::{future::Future, pin::Pin, task};
+
+use futures::FutureExt as _;
+use tracing::{collect::with_default, Instrument as _, Level};
+use tracing_mock::*;
+
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+#[test]
+fn span_on_drop() {
+ #[derive(Clone, Debug)]
+ struct AssertSpanOnDrop;
+
+ impl Drop for AssertSpanOnDrop {
+ fn drop(&mut self) {
+ tracing::info!("Drop");
+ }
+ }
+
+ struct Fut(Option);
+
+ impl Future for Fut {
+ type Output = ();
+
+ fn poll(mut self: Pin<&mut Self>, _: &mut task::Context<'_>) -> task::Poll {
+ self.set(Fut(None));
+ task::Poll::Ready(())
+ }
+ }
+
+ let collector = collector::mock()
+ .enter(expect::span().named("foo"))
+ .event(
+ expect::event()
+ .with_contextual_parent(Some("foo"))
+ .at_level(Level::INFO),
+ )
+ .exit(expect::span().named("foo"))
+ .enter(expect::span().named("foo"))
+ .exit(expect::span().named("foo"))
+ .drop_span(expect::span().named("foo"))
+ .enter(expect::span().named("bar"))
+ .event(
+ expect::event()
+ .with_contextual_parent(Some("bar"))
+ .at_level(Level::INFO),
+ )
+ .exit(expect::span().named("bar"))
+ .drop_span(expect::span().named("bar"))
+ .only()
+ .run();
+
+ with_default(collector, || {
+ // polled once
+ Fut(Some(AssertSpanOnDrop))
+ .instrument(tracing::span!(Level::TRACE, "foo"))
+ .now_or_never()
+ .unwrap();
+
+ // never polled
+ drop(Fut(Some(AssertSpanOnDrop)).instrument(tracing::span!(Level::TRACE, "bar")));
+ });
+}