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

fix(sentry-tracing): switch sentry spans on enter and exit #686

Merged
merged 3 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions sentry-core/src/hub_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,17 @@ thread_local! {
);
}

pub(crate) struct SwitchGuard {
/// A Hub switch guard used to temporarily swap
/// active hub in thread local storage.
pub struct SwitchGuard {
inner: Option<(Arc<Hub>, bool)>,
}

impl SwitchGuard {
pub(crate) fn new(mut hub: Arc<Hub>) -> Self {
/// Swaps the current thread's Hub by the one provided
/// and returns a guard that, when dropped, replaces it
/// to the previous one.
pub fn new(mut hub: Arc<Hub>) -> Self {
let inner = THREAD_HUB.with(|(thread_hub, is_process_hub)| {
// SAFETY: `thread_hub` will always be a valid thread local hub,
// by definition not shared between threads.
Expand Down
3 changes: 2 additions & 1 deletion sentry-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,9 @@ pub mod metrics;
mod session;
#[cfg(all(feature = "client", feature = "metrics"))]
mod units;

#[cfg(feature = "client")]
pub use crate::client::Client;
pub use crate::{client::Client, hub_impl::SwitchGuard as HubSwitchGuard};

// test utilities
#[cfg(feature = "test")]
Expand Down
6 changes: 6 additions & 0 deletions sentry-core/src/performance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,12 @@ impl Span {
transaction.context.clone()
}

/// Get the current span ID.
pub fn get_span_id(&self) -> protocol::SpanId {
let span = self.span.lock().unwrap();
span.span_id
}

/// Get the status of the Span.
pub fn get_status(&self) -> Option<protocol::SpanStatus> {
let span = self.span.lock().unwrap();
Expand Down
50 changes: 42 additions & 8 deletions sentry-tracing/src/layer.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::sync::Arc;

use sentry_core::protocol::Value;
use sentry_core::{Breadcrumb, TransactionOrSpan};
Expand Down Expand Up @@ -197,6 +198,8 @@ fn record_fields<'a, K: AsRef<str> + Into<Cow<'a, str>>>(
pub(super) struct SentrySpanData {
pub(super) sentry_span: TransactionOrSpan,
parent_sentry_span: Option<TransactionOrSpan>,
hub: Arc<sentry_core::Hub>,
hub_switch_guard: Option<sentry_core::HubSwitchGuard>,
saiintbrisson marked this conversation as resolved.
Show resolved Hide resolved
}

impl<S> Layer<S> for SentryLayer<S>
Expand Down Expand Up @@ -256,7 +259,9 @@ where
}
});

let parent_sentry_span = sentry_core::configure_scope(|s| s.get_span());
let hub = sentry_core::Hub::current();
let parent_sentry_span = hub.configure_scope(|scope| scope.get_span());

let sentry_span: sentry_core::TransactionOrSpan = match &parent_sentry_span {
Some(parent) => parent.start_child(op, &description).into(),
None => {
Expand All @@ -268,15 +273,48 @@ where
// This comes from typically the `fields` in `tracing::instrument`.
record_fields(&sentry_span, data);

sentry_core::configure_scope(|scope| scope.set_span(Some(sentry_span.clone())));

let mut extensions = span.extensions_mut();
extensions.insert(SentrySpanData {
sentry_span,
parent_sentry_span,
hub,
hub_switch_guard: None,
});
}

/// Sets entered span as *current* sentry span. A tracing span can be
/// entered and existed multiple times, for example, when using a `tracing::Instrumented` future.
fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
let span = match ctx.span(id) {
Some(span) => span,
None => return,
};

let mut extensions = span.extensions_mut();
if let Some(data) = extensions.get_mut::<SentrySpanData>() {
data.hub_switch_guard = Some(sentry_core::HubSwitchGuard::new(data.hub.clone()));
data.hub.configure_scope(|scope| {
scope.set_span(Some(data.sentry_span.clone()));
})
}
}

/// Set exited span's parent as *current* sentry span.
fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
let span = match ctx.span(id) {
Some(span) => span,
None => return,
};

let mut extensions = span.extensions_mut();
if let Some(data) = extensions.get_mut::<SentrySpanData>() {
data.hub.configure_scope(|scope| {
scope.set_span(data.parent_sentry_span.clone());
});
data.hub_switch_guard.take();
}
}

/// When a span gets closed, finish the underlying sentry span, and set back
/// its parent as the *current* sentry span.
fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
Expand All @@ -286,16 +324,12 @@ where
};

let mut extensions = span.extensions_mut();
let SentrySpanData {
sentry_span,
parent_sentry_span,
} = match extensions.remove::<SentrySpanData>() {
let SentrySpanData { sentry_span, .. } = match extensions.remove::<SentrySpanData>() {
Some(data) => data,
None => return,
};

sentry_span.finish();
sentry_core::configure_scope(|scope| scope.set_span(parent_sentry_span));
}

/// Implement the writing of extra data to span
Expand Down