Skip to content
4 changes: 2 additions & 2 deletions examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,5 @@ inferno = "0.10.0"
tempdir = "0.3.7"

# opentelemetry example
opentelemetry = { version = "0.14", default-features = false, features = ["trace"] }
opentelemetry-jaeger = "0.13"
opentelemetry = { version = "0.15", default-features = false, features = ["trace"] }
opentelemetry-jaeger = "0.14"
142 changes: 137 additions & 5 deletions tracing-core/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ use crate::stdlib::{
/// or event occurred. The `tracing` macros default to using the module
/// path where the span or event originated as the target, but it may be
/// overridden.
/// - A [verbosity level].
/// - A [verbosity level]. This determines how verbose a given span or event
/// is, and allows enabling or disabling more verbose diagnostics
/// situationally. See the documentation for the [`Level`] type for details.
/// - The names of the [fields] defined by the span or event.
/// - Whether the metadata corresponds to a span or event.
///
Expand Down Expand Up @@ -89,20 +91,150 @@ pub struct Metadata<'a> {
/// Indicates whether the callsite is a span or event.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Kind(KindInner);

/// Describes the level of verbosity of a span or event.
///
/// # Comparing Levels
///
/// `Level` implements the [`PartialOrd`] and [`Ord`] traits, allowing two
/// `Level`s to be compared to determine which is considered more or less
/// verbose. Levels which are more verbose are considered "greater than" levels
/// which are less verbose, with [`Level::ERROR`] considered the lowest, and
/// [`Level::TRACE`] considered the highest.
///
/// For example:
/// ```
/// use tracing_core::Level;
///
/// assert!(Level::TRACE > Level::DEBUG);
/// assert!(Level::ERROR < Level::WARN);
/// assert!(Level::INFO <= Level::DEBUG);
/// assert_eq!(Level::TRACE, Level::TRACE);
/// ```
///
/// # Filtering
///
/// `Level`s are typically used to implement filtering that determines which
/// spans and events are enabled. Depending on the use case, more or less
/// verbose diagnostics may be desired. For example, when running in
/// development, [`DEBUG`]-level traces may be enabled by default. When running in
/// production, only [`INFO`]-level and lower traces might be enabled. Libraries
/// may include very verbose diagnostics at the [`DEBUG`] and/or [`TRACE`] levels.
/// Applications using those libraries typically chose to ignore those traces. However, when
/// debugging an issue involving said libraries, it may be useful to temporarily
/// enable the more verbose traces.
///
/// The [`LevelFilter`] type is provided to enable filtering traces by
/// verbosity. `Level`s can be compared against [`LevelFilter`]s, and
/// [`LevelFilter`] has a variant for each `Level`, which compares analogously
/// to that level. In addition, [`LevelFilter`] adds a [`LevelFilter::OFF`]
/// variant, which is considered "less verbose" than every other `Level. This is
/// intended to allow filters to completely disable tracing in a particular context.
///
/// For example:
/// ```
/// use tracing_core::{Level, LevelFilter};
///
/// assert!(LevelFilter::OFF < Level::TRACE);
/// assert!(LevelFilter::TRACE > Level::DEBUG);
/// assert!(LevelFilter::ERROR < Level::WARN);
/// assert!(LevelFilter::INFO <= Level::DEBUG);
/// assert!(LevelFilter::INFO >= Level::INFO);
/// ```
///
/// ## Examples
///
/// Below is a simple example of how a [`Subscriber`] could implement filtering through
/// a [`LevelFilter`]. When a span or event is recorded, the [`Subscriber::enabled`] method
/// compares the span or event's `Level` against the configured [`LevelFilter`].
/// The optional [`Subscriber::max_level_hint`] method can also be implemented to allow spans
/// and events above a maximum verbosity level to be skipped more efficiently,
/// often improving performance in short-lived programs.
///
/// ```
/// use tracing_core::{span, Event, Level, LevelFilter, Subscriber, Metadata};
/// # use tracing_core::span::{Id, Record, Current};
///
/// #[derive(Debug)]
/// pub struct MySubscriber {
/// /// The most verbose level that this subscriber will enable.
/// max_level: LevelFilter,
///
/// // ...
/// }
///
/// impl MySubscriber {
/// /// Returns a new `MySubscriber` which will record spans and events up to
/// /// `max_level`.
/// pub fn with_max_level(max_level: LevelFilter) -> Self {
/// Self {
/// max_level,
/// // ...
/// }
/// }
/// }
/// impl Subscriber for MySubscriber {
/// fn enabled(&self, meta: &Metadata<'_>) -> bool {
/// // A span or event is enabled if it is at or below the configured
/// // maximum level.
/// meta.level() <= &self.max_level
/// }
///
/// // This optional method returns the most verbose level that this
/// // subscriber will enable. Although implementing this method is not
/// // *required*, it permits additional optimizations when it is provided,
/// // allowing spans and events above the max level to be skipped
/// // more efficiently.
/// fn max_level_hint(&self) -> Option<LevelFilter> {
/// Some(self.max_level)
/// }
///
/// // Implement the rest of the subscriber...
/// fn new_span(&self, span: &span::Attributes<'_>) -> span::Id {
/// // ...
/// # drop(span); Id::from_u64(1)
/// }

/// fn event(&self, event: &Event<'_>) {
/// // ...
/// # drop(event);
/// }
///
/// // ...
/// # fn enter(&self, _: &Id) {}
/// # fn exit(&self, _: &Id) {}
/// # fn record(&self, _: &Id, _: &Record<'_>) {}
/// # fn record_follows_from(&self, _: &Id, _: &Id) {}
/// }
/// ```
///
/// It is worth noting that the `tracing-subscriber` crate provides [additional
/// APIs][envfilter] for performing more sophisticated filtering, such as
/// enabling different levels based on which module or crate a span or event is
/// recorded in.
///
/// [`DEBUG`]: Level::DEBUG
/// [`INFO`]: Level::INFO
/// [`TRACE`]: Level::TRACE
/// [`Subscriber::enabled`]: crate::subscriber::Subscriber::enabled
/// [`Subscriber::max_level_hint`]: crate::subscriber::Subscriber::max_level_hint
/// [subscriber]: crate::subscriber::Subscriber
/// [envfilter]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Level(LevelInner);

/// A filter comparable to a verbosity `Level`.
/// A filter comparable to a verbosity [`Level`].
///
/// If a `Level` is considered less than a `LevelFilter`, it should be
/// If a [`Level`] is considered less than a `LevelFilter`, it should be
/// considered disabled; if greater than or equal to the `LevelFilter`, that
/// level is enabled.
///
/// Note that this is essentially identical to the `Level` type, but with the
/// addition of an `OFF` level that completely disables all trace
/// addition of an [`OFF`] level that completely disables all trace
/// instrumentation.
///
/// See the documentation for the [`Level`] type for more details.
///
/// [`OFF`]: LevelFilter::OFF
#[repr(transparent)]
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct LevelFilter(Option<Level>);
Expand Down
6 changes: 3 additions & 3 deletions tracing-opentelemetry/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "tracing-opentelemetry"
version = "0.13.0"
version = "0.14.0"
authors = [
"Julian Tescher <julian@tescher.me>",
"Tokio Contributors <team@tokio.rs>"
Expand All @@ -22,16 +22,16 @@ edition = "2018"
default = ["tracing-log"]

[dependencies]
opentelemetry = { version = "0.14", default-features = false, features = ["trace"] }
opentelemetry = { version = "0.15", default-features = false, features = ["trace"] }
tracing = { path = "../tracing", version = "0.1", default-features = false, features = ["std"] }
tracing-core = { path = "../tracing-core", version = "0.1" }
tracing-subscriber = { path = "../tracing-subscriber", version = "0.2", default-features = false, features = ["registry"] }
tracing-log = { path = "../tracing-log", version = "0.1", default-features = false, optional = true }

[dev-dependencies]
async-trait = "0.1"
opentelemetry-jaeger = "0.13"
criterion = { version = "0.3", default_features = false }
opentelemetry-jaeger = "0.14"

[lib]
bench = false
Expand Down
63 changes: 33 additions & 30 deletions tracing-opentelemetry/src/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,29 +177,28 @@ impl<'a> field::Visit for SpanEventVisitor<'a> {

struct SpanAttributeVisitor<'a>(&'a mut otel::SpanBuilder);

impl<'a> SpanAttributeVisitor<'a> {
fn record(&mut self, attribute: KeyValue) {
debug_assert!(self.0.attributes.is_some());
if let Some(v) = self.0.attributes.as_mut() {
v.push(attribute);
}
}
}

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) {
let attribute = KeyValue::new(field.name(), value);
if let Some(attributes) = &mut self.0.attributes {
attributes.push(attribute);
} else {
self.0.attributes = Some(vec![attribute]);
}
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) {
let attribute = KeyValue::new(field.name(), value);
if let Some(attributes) = &mut self.0.attributes {
attributes.push(attribute);
} else {
self.0.attributes = Some(vec![attribute]);
}
self.record(KeyValue::new(field.name(), value));
}

/// Set attributes on the underlying OpenTelemetry [`Span`] from `&str` values.
Expand All @@ -211,14 +210,7 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
SPAN_KIND_FIELD => self.0.span_kind = str_to_span_kind(value),
SPAN_STATUS_CODE_FIELD => self.0.status_code = str_to_status_code(value),
SPAN_STATUS_MESSAGE_FIELD => self.0.status_message = Some(value.to_owned().into()),
_ => {
let attribute = KeyValue::new(field.name(), value.to_string());
if let Some(attributes) = &mut self.0.attributes {
attributes.push(attribute);
} else {
self.0.attributes = Some(vec![attribute]);
}
}
_ => self.record(KeyValue::new(field.name(), value.to_string())),
}
}

Expand All @@ -236,14 +228,7 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
SPAN_STATUS_MESSAGE_FIELD => {
self.0.status_message = Some(format!("{:?}", value).into())
}
_ => {
let attribute = Key::new(field.name()).string(format!("{:?}", value));
if let Some(attributes) = &mut self.0.attributes {
attributes.push(attribute);
} else {
self.0.attributes = Some(vec![attribute]);
}
}
_ => self.record(Key::new(field.name()).string(format!("{:?}", value))),
}
}
}
Expand Down Expand Up @@ -421,6 +406,24 @@ where
builder.trace_id = Some(self.tracer.new_trace_id());
}

let builder_attrs = builder
.attributes
.get_or_insert(Vec::with_capacity(attrs.fields().len() + 3));

let meta = attrs.metadata();

if let Some(filename) = meta.file() {
builder_attrs.push(KeyValue::new("code.filepath", filename));
}

if let Some(module) = meta.module_path() {
builder_attrs.push(KeyValue::new("code.namespace", module));
}

if let Some(line) = meta.line() {
builder_attrs.push(KeyValue::new("code.lineno", line as i64));
}

attrs.record(&mut SpanAttributeVisitor(&mut builder));
extensions.insert(builder);
}
Expand Down Expand Up @@ -599,7 +602,7 @@ impl Timings {
#[cfg(test)]
mod tests {
use super::*;
use opentelemetry::trace::SpanKind;
use opentelemetry::trace::{SpanKind, TraceFlags};
use std::borrow::Cow;
use std::sync::{Arc, Mutex};
use std::time::SystemTime;
Expand Down Expand Up @@ -728,7 +731,7 @@ mod tests {
let existing_cx = OtelContext::current_with_span(TestSpan(otel::SpanContext::new(
trace_id,
otel::SpanId::from_u64(1),
0,
TraceFlags::default(),
false,
Default::default(),
)));
Expand Down
Loading