tracing_opentelemetry/lib.rs
1//! # Tracing OpenTelemetry
2//!
3//! [`tracing`] is a framework for instrumenting Rust programs to collect
4//! structured, event-based diagnostic information. This crate provides a layer
5//! that connects spans from multiple systems into a trace and emits them to
6//! [OpenTelemetry]-compatible distributed tracing systems for processing and
7//! visualization.
8//!
9//! [OpenTelemetry]: https://opentelemetry.io
10//! [`tracing`]: https://github.com/tokio-rs/tracing
11//!
12//! ### Special Fields
13//!
14//! Fields with an `otel.` prefix are reserved for this crate and have specific
15//! meaning. They are treated as ordinary fields by other layers. The current
16//! special fields are:
17//!
18//! * `otel.name`: Override the span name sent to OpenTelemetry exporters.
19//! Setting this field is useful if you want to display non-static information
20//! in your span name.
21//! * `otel.kind`: Set the span kind to one of the supported OpenTelemetry [span kinds]. These must
22//! be specified as strings such as `"client"` or `"server"`. If it is not specified, the span is
23//! assumed to be internal.
24//! * `otel.status_code`: Set the span status code to one of the supported OpenTelemetry [span status codes].
25//! * `otel.status_description`: Set the span description of the status. This should be used only if
26//! `otel.status_code` is also set.
27//!
28//! [span kinds]: opentelemetry::trace::SpanKind
29//! [span status codes]: opentelemetry::trace::Status
30//!
31//! ### Semantic Conventions
32//!
33//! OpenTelemetry defines conventional names for attributes of common
34//! operations. These names can be assigned directly as fields, e.g.
35//! `trace_span!("request", "server.port" = 80, "url.full" = ..)`, and they
36//! will be passed through to your configured OpenTelemetry exporter. You can
37//! find the full list of the operations and their expected field names in the
38//! [semantic conventions] spec.
39//!
40//! [semantic conventions]: https://github.com/open-telemetry/semantic-conventions
41//!
42//! ### Stability Status
43//!
44//! The OpenTelemetry tracing specification is stable but the underlying [opentelemetry crate] is
45//! not so some breaking changes will still occur in this crate as well. Metrics are not yet fully
46//! stable. You can read the specification via the [spec repository].
47//!
48//! [opentelemetry crate]: https://github.com/open-telemetry/opentelemetry-rust
49//! [spec repository]: https://github.com/open-telemetry/opentelemetry-specification
50//!
51//! ### OpenTelemetry Logging
52//!
53//! Logging to OpenTelemetry collectors is not supported by this crate, only traces and metrics are.
54//! If you need to export logs through OpenTelemetry, consider [`opentelemetry-appender-tracing`].
55//!
56//! [`opentelemetry-appender-tracing`]: https://crates.io/crates/opentelemetry-appender-tracing
57//!
58//! ## Examples
59//!
60//! ```
61//! use opentelemetry_sdk::trace::SdkTracerProvider;
62//! use opentelemetry::trace::{Tracer, TracerProvider as _};
63//! use tracing::{error, span};
64//! use tracing_subscriber::layer::SubscriberExt;
65//! use tracing_subscriber::Registry;
66//!
67//! // Create a new OpenTelemetry trace pipeline that prints to stdout
68//! let provider = SdkTracerProvider::builder()
69//! .with_simple_exporter(opentelemetry_stdout::SpanExporter::default())
70//! .build();
71//! let tracer = provider.tracer("readme_example");
72//!
73//! // Create a tracing layer with the configured tracer
74//! let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
75//!
76//! // Use the tracing subscriber `Registry`, or any other subscriber
77//! // that impls `LookupSpan`
78//! let subscriber = Registry::default().with(telemetry);
79//!
80//! // Trace executed code
81//! tracing::subscriber::with_default(subscriber, || {
82//! // Spans will be sent to the configured OpenTelemetry exporter
83//! let root = span!(tracing::Level::TRACE, "app_start", work_units = 2);
84//! let _enter = root.enter();
85//!
86//! error!("This event will be logged in the root span.");
87//! });
88//! ```
89//!
90//! ## Feature Flags
91//!
92//! - `metrics`: Enables the [`MetricsLayer`] type, a [layer] that
93//! exports OpenTelemetry metrics from specifically-named events. This enables
94//! the `metrics` feature flag on the `opentelemetry` crate. *Enabled by
95//! default*.
96//!
97//! [layer]: tracing_subscriber::layer
98#![warn(unreachable_pub)]
99#![doc(
100 html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png"
101)]
102#![cfg_attr(
103 docsrs,
104 // Allows displaying cfgs/feature flags in the documentation.
105 feature(doc_cfg),
106 // Allows adding traits to RustDoc's list of "notable traits"
107 feature(doc_notable_trait),
108 // Fail the docs build if any intra-docs links are broken
109 deny(rustdoc::broken_intra_doc_links),
110)]
111
112/// Implementation of the trace::Subscriber trait; publishes OpenTelemetry metrics.
113#[cfg(feature = "metrics")]
114mod metrics;
115
116/// Implementation of the trace::Layer as a source of OpenTelemetry data.
117mod layer;
118/// Function which enables OpenTelemetry context extraction from span extensions.
119mod otel_context;
120/// Span extension which enables OpenTelemetry context management.
121mod span_ext;
122
123mod stack;
124
125use std::time::SystemTime;
126
127pub use layer::{layer, FilteredOpenTelemetryLayer, OpenTelemetryLayer};
128
129#[cfg(feature = "metrics")]
130pub use metrics::MetricsLayer;
131use opentelemetry::trace::TraceContextExt as _;
132pub use otel_context::get_otel_context;
133pub use span_ext::{OpenTelemetrySpanExt, SetParentError};
134
135/// Per-span OpenTelemetry data tracked by this crate.
136#[derive(Debug)]
137pub struct OtelData {
138 /// The state of the OtelData, which can either be a builder or a context.
139 state: OtelDataState,
140 /// The end time of the span if it has been exited.
141 end_time: Option<SystemTime>,
142}
143
144impl OtelData {
145 /// Gets the trace ID of the span.
146 ///
147 /// Returns `None` if the context has not been built yet. This can be forced e.g. by calling
148 /// [`context`] on the span (not on `OtelData`) or if [context activation] was not explicitly
149 /// opted-out of, simply entering the span for the first time.
150 ///
151 /// [`context`]: OpenTelemetrySpanExt::context
152 /// [context activation]: OpenTelemetryLayer::with_context_activation
153 pub fn trace_id(&self) -> Option<opentelemetry::TraceId> {
154 if let OtelDataState::Context { current_cx } = &self.state {
155 Some(current_cx.span().span_context().trace_id())
156 } else {
157 None
158 }
159 }
160
161 /// Gets the span ID of the span.
162 ///
163 /// Returns `None` if the context has not been built yet. This can be forced e.g. by calling
164 /// [`context`] on the span (not on `OtelData`) or if [context activation] was not explicitly
165 /// opted-out of, simply entering the span for the first time.
166 ///
167 /// [`context`]: OpenTelemetrySpanExt::context
168 /// [context activation]: OpenTelemetryLayer::with_context_activation
169 pub fn span_id(&self) -> Option<opentelemetry::SpanId> {
170 if let OtelDataState::Context { current_cx } = &self.state {
171 Some(current_cx.span().span_context().span_id())
172 } else {
173 None
174 }
175 }
176}
177
178/// The state of the OpenTelemetry data for a span.
179#[derive(Debug)]
180#[allow(clippy::large_enum_variant)]
181pub(crate) enum OtelDataState {
182 /// The span is being built, with a parent context and a builder.
183 Builder {
184 parent_cx: opentelemetry::Context,
185 builder: opentelemetry::trace::SpanBuilder,
186 status: opentelemetry::trace::Status,
187 },
188 /// The span has been started or accessed and is now in a context.
189 Context { current_cx: opentelemetry::Context },
190}
191
192impl Default for OtelDataState {
193 fn default() -> Self {
194 OtelDataState::Context {
195 current_cx: opentelemetry::Context::default(),
196 }
197 }
198}
199
200pub(crate) mod time {
201 use std::time::SystemTime;
202
203 #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))]
204 pub(crate) fn now() -> SystemTime {
205 SystemTime::now()
206 }
207
208 #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
209 pub(crate) fn now() -> SystemTime {
210 SystemTime::UNIX_EPOCH + std::time::Duration::from_millis(js_sys::Date::now() as u64)
211 }
212}