Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tracing: make it possible to use remote IDs as parent span ID #2953

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions tracing-attributes/tests/parents.rs
@@ -1,4 +1,4 @@
use tracing::{collect::with_default, Id, Level};
use tracing::{collect::with_default, Level, ParentId};
use tracing_attributes::instrument;
use tracing_mock::*;

Expand All @@ -8,7 +8,7 @@ fn with_default_parent() {}
#[instrument(parent = parent_span, skip(parent_span))]
fn with_explicit_parent<P>(parent_span: P)
where
P: Into<Option<Id>>,
P: Into<ParentId>,
{
}

Expand Down
16 changes: 8 additions & 8 deletions tracing-core/src/event.rs
@@ -1,6 +1,6 @@
//! Events represent single points in time during the execution of a program.
use crate::parent::Parent;
use crate::span::Id;
use crate::span::ParentId;
use crate::{field, Metadata};

/// `Event`s represent single points in time where something occurred during the
Expand Down Expand Up @@ -51,13 +51,13 @@ impl<'a> Event<'a> {
/// provided metadata and set of values.
#[inline]
pub fn new_child_of(
parent: impl Into<Option<Id>>,
parent: impl Into<ParentId>,
metadata: &'static Metadata<'static>,
fields: &'a field::ValueSet<'a>,
) -> Self {
let parent = match parent.into() {
Some(p) => Parent::Explicit(p),
None => Parent::Root,
ParentId::None => Parent::Root,
parent => Parent::Explicit(parent),
};
Event {
fields,
Expand All @@ -69,7 +69,7 @@ impl<'a> Event<'a> {
/// Constructs a new `Event` with the specified metadata and set of values,
/// and observes it with the current collector and an explicit parent.
pub fn child_of(
parent: impl Into<Option<Id>>,
parent: impl Into<ParentId>,
metadata: &'static Metadata<'static>,
fields: &'a field::ValueSet<'_>,
) {
Expand Down Expand Up @@ -119,10 +119,10 @@ impl<'a> Event<'a> {
///
/// Otherwise (if the new event is a root or is a child of the current span),
/// returns `None`.
pub fn parent(&self) -> Option<&Id> {
pub fn parent(&self) -> &ParentId {
match self.parent {
Parent::Explicit(ref p) => Some(p),
_ => None,
Parent::Explicit(ref p) => p,
_ => &ParentId::None,
}
}
}
4 changes: 2 additions & 2 deletions tracing-core/src/parent.rs
@@ -1,4 +1,4 @@
use crate::span::Id;
use crate::span::ParentId;

#[derive(Debug)]
pub(crate) enum Parent {
Expand All @@ -7,5 +7,5 @@ pub(crate) enum Parent {
/// The new span will be rooted in the current span.
Current,
/// The new span has an explicitly-specified parent.
Explicit(Id),
Explicit(ParentId),
}
128 changes: 120 additions & 8 deletions tracing-core/src/span.rs
Expand Up @@ -16,6 +16,94 @@ use crate::{field, Metadata};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Id(NonZeroU64);

/// Identifies a span outside the cotext of a collector.
///
/// This ID may have come from outside of the process and should identify a
/// single span created in some other context.
///
/// A concrete implementation may be `traceId` and `spanId` of an OpenTelemetry
/// span which was generated in another application which called an API
/// instrumented with `tracing`.
///
/// Subscribers can handle the remote parent by providing the information or its
/// `Debug` representation to the user, but the `Registry` will not be able to
/// look this span up even if it implements `LookupSpan`. There can also be
/// specialized subscribers which downcast the ID to the correct type and then
/// can handle it in a special way.
///
/// Note that `tracing` does not provide globally unique IDs to spans and
/// therefore these should not be used as a `RemoteId` to be sent to other
/// applications.
pub trait RemoteId: std::fmt::Debug + Send + Sync + 'static {
/// Clones the `RemoteId` into a new allocation. If the type itself is
/// `Clone`, this should be as simple as `Box::new(self.clone())`.
///
/// This clone has to go through a `Box` instead of simply requiring the
/// `RemoteId` to also implement `Clone` because this trait needs to be
/// object-safe.
fn clone_boxed(&self) -> Box<dyn RemoteId>;
}

/// Identifies a span that is parent of the given span or event, if such span exists.
///
/// The parent span may be tracked in the same [`Collect`](crate::Collect) or it
/// may come from a remote context, possibly even from an application not using
/// `tracing` at all.
#[derive(Debug)]
pub enum ParentId {
/// There is no parent. A span without a parent is a root span.
None,
/// The parent is tracked in the same [`Collect`](crate::Collect) and
/// originates in the same context as the child span.
Local(Id),
/// The parent is tracked outside the [`Collect`](crate::Collect) that
/// tracks the child span. The parent may be from different
/// [`Collect`](crate::Collect), different application and possibly even
/// different language.
Remote(Box<dyn RemoteId>),
}

impl ParentId {
/// Returns a reference to a [`span::Id`](crate::span::Id) of the parent
/// span if it is tracked locally. Returns `None` for both root spans and
/// spans that have remote parents.
pub fn local(&self) -> Option<&Id> {
if let ParentId::Local(id) = self {
Some(id)
} else {
None
}
}
}

impl Clone for ParentId {
fn clone(&self) -> Self {
match self {
Self::None => Self::None,
Self::Local(id) => Self::Local(id.clone()),
Self::Remote(remote) => Self::Remote(remote.clone_boxed()),
}
}
}

impl From<Id> for ParentId {
fn from(value: Id) -> Self {
ParentId::Local(value)
}
}

impl From<&Id> for ParentId {
fn from(value: &Id) -> Self {
ParentId::Local(value.clone())
}
}

impl From<Option<Id>> for ParentId {
fn from(value: Option<Id>) -> Self {
value.map_or(ParentId::None, ParentId::Local)
}
}

/// Attributes provided to a collector describing a new span when it is
/// created.
#[derive(Debug)]
Expand Down Expand Up @@ -127,7 +215,7 @@ impl<'a> Attributes<'a> {
/// Returns `Attributes` describing a new child span of the specified
/// parent span, with the provided metadata and values.
pub fn child_of(
parent: Id,
parent: ParentId,
metadata: &'static Metadata<'static>,
values: &'a field::ValueSet<'a>,
) -> Self {
Expand Down Expand Up @@ -169,10 +257,10 @@ impl<'a> Attributes<'a> {
///
/// Otherwise (if the new span is a root or is a child of the current span),
/// returns `None`.
pub fn parent(&self) -> Option<&Id> {
pub fn parent(&self) -> &ParentId {
match self.parent {
Parent::Explicit(ref p) => Some(p),
_ => None,
Parent::Explicit(ref p) => p,
Parent::Current | Parent::Root => &ParentId::None,
}
}

Expand Down Expand Up @@ -293,23 +381,23 @@ impl Current {
pub fn into_inner(self) -> Option<(Id, &'static Metadata<'static>)> {
match self.inner {
CurrentInner::Current { id, metadata } => Some((id, metadata)),
_ => None,
CurrentInner::None | CurrentInner::Unknown => None,
}
}

/// Borrows the `Id` of the current span, if one exists and is known.
pub fn id(&self) -> Option<&Id> {
match self.inner {
CurrentInner::Current { ref id, .. } => Some(id),
_ => None,
CurrentInner::None | CurrentInner::Unknown => None,
}
}

/// Borrows the `Metadata` of the current span, if one exists and is known.
pub fn metadata(&self) -> Option<&'static Metadata<'static>> {
match self.inner {
CurrentInner::Current { metadata, .. } => Some(metadata),
_ => None,
CurrentInner::None | CurrentInner::Unknown => None,
}
}
}
Expand All @@ -330,7 +418,7 @@ impl From<Current> for Option<Id> {
fn from(cur: Current) -> Self {
match cur.inner {
CurrentInner::Current { id, .. } => Some(id),
_ => None,
CurrentInner::None | CurrentInner::Unknown => None,
}
}
}
Expand All @@ -340,3 +428,27 @@ impl<'a> From<&'a Current> for Option<&'static Metadata<'static>> {
cur.metadata()
}
}

// impl<'a> From<&'a Current> for &'a ParentId {
// fn from(cur: &'a Current) -> Self {
// cur.id()
// }
// }

impl<'a> From<&'a Current> for ParentId {
fn from(cur: &'a Current) -> Self {
match cur.id().cloned() {
Some(id) => ParentId::Local(id),
None => ParentId::None,
}
}
}

impl From<Current> for ParentId {
fn from(cur: Current) -> Self {
match cur.inner {
CurrentInner::Current { id, .. } => ParentId::Local(id),
CurrentInner::None | CurrentInner::Unknown => ParentId::None,
}
}
}
14 changes: 8 additions & 6 deletions tracing-mock/src/collector.rs
Expand Up @@ -154,7 +154,7 @@ use std::{
use tracing::{
collect::Interest,
level_filters::LevelFilter,
span::{self, Attributes, Id},
span::{self, Attributes, Id, ParentId},
Collect, Event, Metadata,
};

Expand Down Expand Up @@ -1037,11 +1037,12 @@ where
let get_parent_name = || {
let stack = self.current.lock().unwrap();
let spans = self.spans.lock().unwrap();
event
.parent()
.and_then(|id| spans.get(id))
.or_else(|| stack.last().and_then(|id| spans.get(id)))
.map(|s| s.name.to_string())
let id = match event.parent() {
ParentId::None => stack.last(),
ParentId::Local(id) => Some(id),
ParentId::Remote(_) => None,
};
id.and_then(|id| spans.get(id)).map(|s| s.name.to_string())
};
expected.check(event, get_parent_name, &self.name);
}
Expand Down Expand Up @@ -1103,6 +1104,7 @@ where
let get_parent_name = || {
let stack = self.current.lock().unwrap();
span.parent()
.local()
.and_then(|id| spans.get(id))
.or_else(|| stack.last().and_then(|id| spans.get(id)))
.map(|s| s.name.to_string())
Expand Down
2 changes: 1 addition & 1 deletion tracing-mock/src/event.rs
Expand Up @@ -581,7 +581,7 @@ impl ExpectedEvent {
let actual_parent = get_parent_name();
expected_parent.check_parent_name(
actual_parent.as_deref(),
event.parent().cloned(),
event.parent().local().cloned(),
event.metadata().name(),
collector_name,
)
Expand Down
2 changes: 1 addition & 1 deletion tracing-mock/src/span.rs
Expand Up @@ -715,7 +715,7 @@ impl NewSpan {
let actual_parent = get_parent_name();
expected_parent.check_parent_name(
actual_parent.as_deref(),
span.parent().cloned(),
span.parent().local().cloned(),
format_args!("span `{}`", name),
collector_name,
)
Expand Down
1 change: 1 addition & 0 deletions tracing-mock/src/subscriber.rs
Expand Up @@ -939,6 +939,7 @@ where
if let Expect::NewSpan(mut expected) = expected.pop_front().unwrap() {
let get_parent_name = || {
span.parent()
.local()
.and_then(|id| cx.span(id))
.or_else(|| cx.lookup_current())
.map(|span| span.name().to_string())
Expand Down
33 changes: 30 additions & 3 deletions tracing-serde/src/lib.rs
Expand Up @@ -174,15 +174,18 @@
use core::fmt;

use serde::{
ser::{SerializeMap, SerializeSeq, SerializeStruct, SerializeTupleStruct, Serializer},
ser::{
SerializeMap, SerializeSeq, SerializeStruct, SerializeTupleStruct, SerializeTupleVariant,
Serializer,
},
Serialize,
};

use tracing_core::{
event::Event,
field::{Field, FieldSet, Visit},
metadata::{Level, Metadata},
span::{Attributes, Id, Record},
span::{Attributes, Id, ParentId, Record},
};

pub mod fields;
Expand Down Expand Up @@ -253,6 +256,30 @@ impl<'a> Serialize for SerializeId<'a> {
}
}

#[derive(Debug)]
pub struct SerializeParentId<'a>(&'a ParentId);

impl<'a> Serialize for SerializeParentId<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match &self.0 {
ParentId::None => serializer.serialize_unit_variant("ParentId", 0, "None"),
ParentId::Local(id) => {
let mut state = serializer.serialize_tuple_variant("ParentId", 1, "Local", 1)?;
state.serialize_field(&SerializeId(id))?;
state.end()
}
ParentId::Remote(remote_id) => {
let mut state = serializer.serialize_tuple_variant("ParentId", 2, "Remote", 1)?;
state.serialize_field(&format_args!("{:?}", remote_id))?;
state.end()
}
}
}
}

#[derive(Debug)]
pub struct SerializeMetadata<'a>(&'a Metadata<'a>);

Expand Down Expand Up @@ -306,7 +333,7 @@ impl<'a> Serialize for SerializeAttributes<'a> {
{
let mut serializer = serializer.serialize_struct("Attributes", 3)?;
serializer.serialize_field("metadata", &SerializeMetadata(self.0.metadata()))?;
serializer.serialize_field("parent", &self.0.parent().map(SerializeId))?;
serializer.serialize_field("parent", &SerializeParentId(self.0.parent()))?;
serializer.serialize_field("is_root", &self.0.is_root())?;

let mut visitor = SerdeStructVisitor {
Expand Down
1 change: 1 addition & 0 deletions tracing-subscriber/src/fmt/format/json.rs
Expand Up @@ -237,6 +237,7 @@ where
{
event
.parent()
.local()
.and_then(|id| ctx.span(id))
.or_else(|| ctx.lookup_current())
} else {
Expand Down
1 change: 1 addition & 0 deletions tracing-subscriber/src/fmt/format/pretty.rs
Expand Up @@ -284,6 +284,7 @@ where
let bold = writer.bold();
let span = event
.parent()
.local()
.and_then(|id| ctx.span(id))
.or_else(|| ctx.lookup_current());

Expand Down