diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1c37fa40ff..fade89af20 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,8 +8,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 6dfd3e3f71..bced9c4933 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -133,17 +133,11 @@ jobs: - tracing-macros - tracing-serde - tracing-tower - - tracing-opentelemetry - tracing - tracing-subscriber steps: - uses: actions/checkout@v3 - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true - + - uses: dtolnay/rust-toolchain@stable - name: install cargo-hack uses: taiki-e/install-action@cargo-hack - name: cargo hack check @@ -194,7 +188,6 @@ jobs: - tracing-serde - tracing-subscriber - tracing-tower - - tracing-opentelemetry - tracing toolchain: - 1.49.0 @@ -295,7 +288,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 41e9cf40a9..2bc566ed6e 100644 --- a/README.md +++ b/README.md @@ -305,11 +305,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). @@ -337,7 +332,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/examples/Cargo.toml b/examples/Cargo.toml index 76d663a894..510a0680b8 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -21,9 +21,8 @@ tracing-futures = { version = "0.2.1", path = "../tracing-futures", features = [ tracing-attributes = { path = "../tracing-attributes", version = "0.1.22" } tracing-log = { path = "../tracing-log", version = "0.1.3", features = ["env_logger"] } tracing-serde = { path = "../tracing-serde" } -tracing-opentelemetry = { path = "../tracing-opentelemetry" } -tracing-journald = { path = "../tracing-journald" } tracing-appender = { path = "../tracing-appender", version = "0.2.0" } +tracing-journald = { path = "../tracing-journald" } # serde example serde_json = "1.0.82" @@ -51,10 +50,6 @@ log = "0.4.17" inferno = "0.11.6" tempfile = "3" -# 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 f9998f2684..496fd5f75c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -72,12 +72,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 0213631ea2..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::layer::SubscriberExt; -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::layer()); - - tracing::subscriber::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 e00cd3a500..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::layer().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/tracing-opentelemetry/CHANGELOG.md b/tracing-opentelemetry/CHANGELOG.md deleted file mode 100644 index 2f06fe9b60..0000000000 --- a/tracing-opentelemetry/CHANGELOG.md +++ /dev/null @@ -1,310 +0,0 @@ -# 0.18.0 (September 18, 2022) - -### Breaking Changes - -- Upgrade to `v0.18.0` of `opentelemetry` ([#2303]) - For list of breaking changes in OpenTelemetry, see the - [v0.18.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0180). - -### Fixed - -- `on_event` respects event's explicit parent ([#2296]) - -Thanks to @wprzytula for contributing to this release! - -[#2303]: https://github.com/tokio-rs/tracing/pull/2303 -[#2296]: https://github.com/tokio-rs/tracing/pull/2296 - -# 0.17.4 (July 1, 2022) - -This release adds optional support for recording `std::error::Error`s using -[OpenTelemetry's semantic conventions for exceptions][exn-semconv]. - -### Added - -- `Layer::with_exception_fields` to enable emitting `exception.message` and - `exception.backtrace` semantic-convention fields when an `Error` is recorded - as a span or event field ([#2135]) -- `Layer::with_exception_field_propagation` to enable setting `exception.message` and - `exception.backtrace` semantic-convention fields on the current span when an - event with an `Error` field is recorded ([#2135]) - -Thanks to @lilymara-onesignal for contributing to this release! - -[thread-semconv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/exceptions/ -[#2135]: https://github.com/tokio-rs/tracing/pull/2135 - -# 0.17.3 (June 7, 2022) - -This release adds support for emitting thread names and IDs to OpenTelemetry, as -well as recording `std::error::Error` values in a structured manner with their -source chain included. Additionally, this release fixes issues related to event -and span source code locations. - -### Added - -- `Layer::with_threads` to enable recording thread names/IDs according to - [OpenTelemetry semantic conventions][thread-semconv] ([#2134]) -- `Error::source` chain when recording `std::error::Error` values ([#2122]) -- `Layer::with_location` method (replaces `Layer::with_event_location`) - ([#2124]) - -### Changed - -- `std::error::Error` values are now recorded using `fmt::Display` rather than - `fmt::Debug` ([#2122]) - -### Fixed - -- Fixed event source code locations overwriting the parent span's source - location ([#2099]) -- Fixed `Layer::with_event_location` not controlling whether locations are - emitted for spans as well as events ([#2124]) - -### Deprecated - -- `Layer::with_event_location`: renamed to `Layer::with_location`, as it now - controls both span and event locations ([#2124]) - -Thanks to new contributors @lilymara-onesignal, @hubertbudzynski, and @DevinCarr -for contributing to this release! - -[thread-semconv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#source-code-attributes -[#2134]: https://github.com/tokio-rs/tracing/pull/2134 -[#2122]: https://github.com/tokio-rs/tracing/pull/2122 -[#2124]: https://github.com/tokio-rs/tracing/pull/2124 -[#2099]: https://github.com/tokio-rs/tracing/pull/2099 - -# 0.17.2 (February 21, 2022) - -This release fixes [an issue][#1944] introduced in v0.17.1 where -`tracing-opentelemetry` could not be compiled with `default-features = false`. - -### Fixed - -- Compilation failure with `tracing-log` feature disabled ([#1949]) - -[#1949]: https://github.com/tokio-rs/tracing/pull/1917 -[#1944]: https://github.com/tokio-rs/tracing/issues/1944 - -# 0.17.1 (February 11, 2022) (YANKED) - -### Added - -- `OpenTelemetryLayer` can now add detailed location information to - forwarded events (defaults to on) ([#1911]) -- `OpenTelemetryLayer::with_event_location` to control whether source locations - are recorded ([#1911]) -### Changed - -- Avoid unnecessary allocations to improve performance when recording events - ([#1917]) - -Thanks to @djc for contributing to this release! - -[#1917]: https://github.com/tokio-rs/tracing/pull/1917 -[#1911]: https://github.com/tokio-rs/tracing/pull/1911 - -# 0.17.0 (February 3, 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.16.1 (October 23, 2021) - -### Breaking Changes - -- Upgrade to `v0.3.0` of `tracing-subscriber` ([#1677]) - For list of breaking changes in `tracing-subscriber`, see the - [v0.3.0 changelog]. - -### Added - -- `OpenTelemetrySpanExt::add_link` method for adding a link between a `tracing` - span and a provided OpenTelemetry `Context` ([#1516]) - -Thanks to @LehMaxence for contributing to this release! - -[v0.3.0 changelog]: https://github.com/tokio-rs/tracing/releases/tag/tracing-subscriber-0.3.0 -[#1516]: https://github.com/tokio-rs/tracing/pull/1516 -[#1677]: https://github.com/tokio-rs/tracing/pull/1677 - -# 0.15.0 (August 7, 2021) - -### Breaking Changes - -- Upgrade to `v0.17.1` of `opentelemetry` (#1497) - For list of breaking changes in OpenTelemetry, see the - [v0.17.1 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.14.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0140). - -### Added - -- Spans now include Opentelemetry `code.namespace`, `code.filepath`, and - `code.lineno` attributes ([#1411]) - -### Changed - -- Improve performance by pre-allocating attribute `Vec`s ([#1327]) - -Thanks to @Drevoed, @lilymara-onesignal, and @Folyd for contributing -to this release! - -[#1441]: https://github.com/tokio-rs/tracing/pull/1441 -[#1411]: https://github.com/tokio-rs/tracing/pull/1411 -[#1327]: https://github.com/tokio-rs/tracing/pull/1327 - -# 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 1ba06c8cd0..0000000000 --- a/tracing-opentelemetry/Cargo.toml +++ /dev/null @@ -1,56 +0,0 @@ -[package] -name = "tracing-opentelemetry" -version = "0.18.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.1.35", default-features = false, features = ["std"] } -tracing-core = { path = "../tracing-core", version = "0.1.28" } -tracing-subscriber = { path = "../tracing-subscriber", version = "0.3.0", default-features = false, features = ["registry", "std"] } -tracing-log = { path = "../tracing-log", version = "0.1.3", default-features = false, optional = true } -once_cell = "1.13.0" - -# Fix minimal-versions -async-trait = { version = "0.1.56", optional = true } -thiserror = { version = "1.0.31", optional = true } - -[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", default-features = false } -tokio = { version = "1", features = ["full"] } -tokio-stream = "0.1" - -[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 4ccbb4a2aa..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.18.0 -[docs-badge]: https://docs.rs/tracing-opentelemetry/badge.svg -[docs-url]: https://docs.rs/tracing-opentelemetry/0.18.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: - -* [`OpenTelemetryLayer`] adds OpenTelemetry context to all `tracing` [span]s. -* [`OpenTelemetrySpanExt`] allows OpenTelemetry parent trace information to be - injected and extracted from a `tracing` [span]. - -[`OpenTelemetryLayer`]: https://docs.rs/tracing-opentelemetry/latest/tracing_opentelemetry/struct.OpenTelemetryLayer.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::sdk::export::trace::stdout; -use tracing::{error, span}; -use tracing_subscriber::layer::SubscriberExt; -use tracing_subscriber::Registry; - -fn main() { - // Install a new OpenTelemetry trace pipeline - let tracer = stdout::new_pipeline().install_simple(); - - // Create a tracing layer with the configured tracer - let telemetry = tracing_opentelemetry::layer().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::subscriber::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."); - }); -} -``` - -### 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 8dbc96eaa5..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(RegistryAccessLayer) - .set_default(); - group.bench_function("no_data_baseline", |b| b.iter(tracing_harness)); - } - - { - let _subscriber = tracing_subscriber::registry() - .with(OtelDataLayer) - .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::layer() - .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 RegistryAccessLayer; - -impl tracing_subscriber::Layer for RegistryAccessLayer -where - S: tracing_core::Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span>, -{ - fn on_new_span( - &self, - _attrs: &tracing_core::span::Attributes<'_>, - id: &tracing::span::Id, - ctx: tracing_subscriber::layer::Context<'_, S>, - ) { - 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::layer::Context<'_, S>) { - let span = ctx.span(&id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - - extensions.remove::(); - } -} - -struct OtelDataLayer; - -impl tracing_subscriber::Layer for OtelDataLayer -where - S: tracing_core::Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span>, -{ - fn on_new_span( - &self, - attrs: &tracing_core::span::Attributes<'_>, - id: &tracing::span::Id, - ctx: tracing_subscriber::layer::Context<'_, S>, - ) { - 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::layer::Context<'_, S>) { - 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/layer.rs b/tracing-opentelemetry/src/layer.rs deleted file mode 100644 index dc8a1c608d..0000000000 --- a/tracing-opentelemetry/src/layer.rs +++ /dev/null @@ -1,1405 +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::any::TypeId; -use std::fmt; -use std::marker; -use std::thread; -use std::time::{Instant, SystemTime}; -use tracing_core::span::{self, Attributes, Id, Record}; -use tracing_core::{field, Event, Subscriber}; -#[cfg(feature = "tracing-log")] -use tracing_log::NormalizeEvent; -use tracing_subscriber::layer::Context; -use tracing_subscriber::registry::LookupSpan; -use tracing_subscriber::Layer; - -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 layer for use in a project that uses -/// [tracing]. -/// -/// [OpenTelemetry]: https://opentelemetry.io -/// [tracing]: https://github.com/tokio-rs/tracing -pub struct OpenTelemetryLayer { - tracer: T, - location: bool, - tracked_inactivity: bool, - with_threads: bool, - exception_config: ExceptionFieldConfig, - get_context: WithContext, - _registry: marker::PhantomData, -} - -impl Default for OpenTelemetryLayer -where - S: Subscriber + for<'span> LookupSpan<'span>, -{ - fn default() -> Self { - OpenTelemetryLayer::new(noop::NoopTracer::new()) - } -} - -/// Construct a layer to track spans via [OpenTelemetry]. -/// -/// [OpenTelemetry]: https://opentelemetry.io -/// -/// # Examples -/// -/// ```rust,no_run -/// use tracing_subscriber::layer::SubscriberExt; -/// use tracing_subscriber::Registry; -/// -/// // Use the tracing subscriber `Registry`, or any other subscriber -/// // that impls `LookupSpan` -/// let subscriber = Registry::default().with(tracing_opentelemetry::layer()); -/// # drop(subscriber); -/// ``` -pub fn layer() -> OpenTelemetryLayer -where - S: Subscriber + for<'span> LookupSpan<'span>, -{ - OpenTelemetryLayer::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( - &self, - dispatch: &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 OpenTelemetryLayer -where - S: Subscriber + for<'span> LookupSpan<'span>, - T: otel::Tracer + PreSampledTracer + 'static, -{ - /// Set the [`Tracer`] that this layer will use to produce and track - /// OpenTelemetry [`Span`]s. - /// - /// [`Tracer`]: opentelemetry::trace::Tracer - /// [`Span`]: opentelemetry::trace::Span - /// - /// # Examples - /// - /// ```no_run - /// use tracing_opentelemetry::OpenTelemetryLayer; - /// use tracing_subscriber::layer::SubscriberExt; - /// 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 layer with the configured tracer - /// let otel_layer = OpenTelemetryLayer::new(tracer); - /// - /// // Use the tracing subscriber `Registry`, or any other subscriber - /// // that impls `LookupSpan` - /// let subscriber = Registry::default().with(otel_layer); - /// # drop(subscriber); - /// ``` - pub fn new(tracer: T) -> Self { - OpenTelemetryLayer { - 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 layer will use to produce and track - /// OpenTelemetry [`Span`]s. - /// - /// [`Tracer`]: opentelemetry::trace::Tracer - /// [`Span`]: opentelemetry::trace::Span - /// - /// # Examples - /// - /// ```no_run - /// use tracing_subscriber::layer::SubscriberExt; - /// 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 layer with the configured tracer - /// let otel_layer = tracing_opentelemetry::layer().with_tracer(tracer); - /// - /// // Use the tracing subscriber `Registry`, or any other subscriber - /// // that impls `LookupSpan` - /// let subscriber = Registry::default().with(otel_layer); - /// # drop(subscriber); - /// ``` - pub fn with_tracer(self, tracer: Tracer) -> OpenTelemetryLayer - where - Tracer: otel::Tracer + PreSampledTracer + 'static, - { - OpenTelemetryLayer { - tracer, - location: self.location, - tracked_inactivity: self.tracked_inactivity, - with_threads: self.with_threads, - exception_config: self.exception_config, - get_context: WithContext(OpenTelemetryLayer::::get_context), - _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 attributes are not recorded. - /// - /// [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, these attributes are not propagated to the span. - /// - /// [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<'_, S>) -> 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 layer = dispatch - .downcast_ref::>() - .expect("layer should downcast to expected type; this is a bug!"); - - let mut extensions = span.extensions_mut(); - if let Some(builder) = extensions.get_mut::() { - f(builder, &layer.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 Layer for OpenTelemetryLayer -where - S: Subscriber + 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<'_, S>) { - 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<'_, S>) { - 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<'_, S>) { - 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<'_, S>) { - 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<'_, S>) { - // 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<'_, S>) { - 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<*const ()> { - match id { - id if id == TypeId::of::() => Some(self as *const _ as *const ()), - id if id == TypeId::of::() => { - Some(&self.get_context as *const _ as *const ()) - } - _ => 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(layer().with_tracer(tracer.clone())); - - tracing::subscriber::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(layer().with_tracer(tracer.clone())); - - tracing::subscriber::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(layer().with_tracer(tracer.clone())); - - tracing::subscriber::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(layer().with_tracer(tracer.clone())); - - let message = "message"; - - tracing::subscriber::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(layer().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::subscriber::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( - layer() - .with_tracer(tracer.clone()) - .with_tracked_inactivity(true), - ); - - tracing::subscriber::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( - layer() - .with_tracer(tracer.clone()) - .with_exception_fields(true), - ); - - let err = TestDynError::new("base error") - .with_parent("intermediate error") - .with_parent("user error"); - - tracing::subscriber::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(layer().with_tracer(tracer.clone()).with_location(true)); - - tracing::subscriber::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(layer().with_tracer(tracer.clone()).with_location(false)); - - tracing::subscriber::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(layer().with_tracer(tracer.clone()).with_threads(true)); - - tracing::subscriber::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(layer().with_tracer(tracer.clone()).with_threads(false)); - - tracing::subscriber::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( - layer() - .with_tracer(tracer.clone()) - .with_exception_field_propagation(true), - ); - - let err = TestDynError::new("base error") - .with_parent("intermediate error") - .with_parent("user error"); - - tracing::subscriber::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/lib.rs b/tracing-opentelemetry/src/lib.rs deleted file mode 100644 index 56044a4408..0000000000 --- a/tracing-opentelemetry/src/lib.rs +++ /dev/null @@ -1,145 +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]: opentelemetry::trace::SpanKind -//! [span status codes]: opentelemetry::trace::StatusCode -//! -//! ### 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::layer::SubscriberExt; -//! 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::layer().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::subscriber::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", - 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; - -/// Implementation of the trace::Layer as a source of OpenTelemetry data. -mod layer; -/// Span extension which enables OpenTelemetry context management. -mod span_ext; -/// Protocols for OpenTelemetry Tracers that are compatible with Tracing -mod tracer; - -pub use layer::{layer, OpenTelemetryLayer}; - -#[cfg(feature = "metrics")] -pub use metrics::MetricsLayer; -pub use span_ext::OpenTelemetrySpanExt; -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 42c90baecb..0000000000 --- a/tracing-opentelemetry/src/metrics.rs +++ /dev/null @@ -1,367 +0,0 @@ -use std::{collections::HashMap, fmt, sync::RwLock}; -use tracing::{field::Visit, Subscriber}; -use tracing_core::Field; - -use opentelemetry::{ - metrics::{Counter, Histogram, Meter, MeterProvider, UpDownCounter}, - sdk::metrics::controllers::BasicController, - Context as OtelContext, -}; -use tracing_subscriber::{layer::Context, registry::LookupSpan, Layer}; - -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 layer that publishes metrics via the OpenTelemetry SDK. -/// -/// # Usage -/// -/// No configuration is needed for this Layer, 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::MetricsLayer; -/// use tracing_subscriber::layer::SubscriberExt; -/// 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 = MetricsLayer::new(controller); -/// let subscriber = Registry::default().with(opentelemetry_metrics); -/// tracing::subscriber::set_global_default(subscriber).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 -/// -/// `MetricsLayer` 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 MetricsLayer { - meter: Meter, - instruments: Instruments, -} - -impl MetricsLayer { - /// Create a new instance of MetricsLayer. - pub fn new(controller: BasicController) -> Self { - let meter = - controller.versioned_meter(INSTRUMENTATION_LIBRARY_NAME, Some(CARGO_PKG_VERSION), None); - MetricsLayer { - meter, - instruments: Default::default(), - } - } -} - -impl Layer for MetricsLayer -where - S: Subscriber + for<'span> LookupSpan<'span>, -{ - fn on_event(&self, event: &tracing::Event<'_>, _ctx: Context<'_, S>) { - 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 ade736815a..0000000000 --- a/tracing-opentelemetry/src/span_ext.rs +++ /dev/null @@ -1,170 +0,0 @@ -use crate::layer::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_subscriber(move |(id, subscriber)| { - if let Some(get_context) = subscriber.downcast_ref::() { - get_context.with_context(subscriber, 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_subscriber(move |(id, subscriber)| { - if let Some(get_context) = subscriber.downcast_ref::() { - get_context.with_context(subscriber, 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_subscriber(|(id, subscriber)| { - if let Some(get_context) = subscriber.downcast_ref::() { - get_context.with_context(subscriber, id, |builder, tracer| { - cx = Some(tracer.sampled_context(builder)); - }) - } - }); - - cx.unwrap_or_default() - } -} diff --git a/tracing-opentelemetry/src/tracer.rs b/tracing-opentelemetry/src/tracer.rs deleted file mode 100644 index 66c52fea21..0000000000 --- a/tracing-opentelemetry/src/tracer.rs +++ /dev/null @@ -1,234 +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. -/// -/// [`Tracer`]: opentelemetry::trace::Tracer -/// [`SpanBuilder`]: opentelemetry::trace::SpanBuilder -/// [`PreSampledTracer::sampled_span_context`]: crate::PreSampledTracer::sampled_span_context -/// [`OpenTelemetrySpanExt::set_parent`]: crate::OpenTelemetrySpanExt::set_parent -/// [`OpenTelemetrySpanExt::context`]: crate::OpenTelemetrySpanExt::context -/// [`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 2f9c20ef7f..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::Subscriber; -use tracing_opentelemetry::MetricsLayer; -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::subscriber::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::subscriber::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::subscriber::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::subscriber::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::subscriber::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::subscriber::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::subscriber::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::subscriber::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::subscriber::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 Subscriber + '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(MetricsLayer::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 49200b4fc9..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::Subscriber; -use tracing_opentelemetry::{layer, 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::subscriber::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::subscriber::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::subscriber::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::subscriber::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 Subscriber) { - 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(layer().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 Subscriber, 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