Skip to content
Draft
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
2 changes: 1 addition & 1 deletion echo/sentryecho.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func (h *handler) handle(next echo.HandlerFunc) echo.HandlerFunc {
}

options := []sentry.SpanOption{
sentry.ContinueTrace(hub, r.Header.Get(sentry.SentryTraceHeader), r.Header.Get(sentry.SentryBaggageHeader)),
sentry.ContinueTrace(sentry.SetHubOnContext(r.Context(), hub), r.Header.Get(sentry.SentryTraceHeader), r.Header.Get(sentry.SentryBaggageHeader)),
sentry.WithOpName("http.server"),
sentry.WithTransactionSource(transactionSource),
sentry.WithSpanOrigin(sentry.SpanOriginEcho),
Expand Down
5 changes: 3 additions & 2 deletions fasthttp/sentryfasthttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,16 @@ func (h *Handler) Handle(handler fasthttp.RequestHandler) fasthttp.RequestHandle

r := convert(ctx)

hubCtx := sentry.SetHubOnContext(ctx, hub)
options := []sentry.SpanOption{
sentry.ContinueTrace(hub, r.Header.Get(sentry.SentryTraceHeader), r.Header.Get(sentry.SentryBaggageHeader)),
sentry.ContinueTrace(hubCtx, r.Header.Get(sentry.SentryTraceHeader), r.Header.Get(sentry.SentryBaggageHeader)),
sentry.WithOpName("http.server"),
sentry.WithTransactionSource(sentry.SourceURL),
sentry.WithSpanOrigin(sentry.SpanOriginFastHTTP),
}

transaction := sentry.StartTransaction(
sentry.SetHubOnContext(ctx, hub),
hubCtx,
fmt.Sprintf("%s %s", r.Method, string(ctx.Path())),
options...,
)
Expand Down
15 changes: 8 additions & 7 deletions fiber/sentryfiber.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,21 @@ func (h *handler) handle(ctx *fiber.Ctx) error {
transactionName := ctx.Path()
transactionSource := sentry.SourceURL

savedCtx := ctx.UserContext()
requestCtx, cancel := context.WithCancel(savedCtx)
defer cancel()
defer func() { ctx.SetUserContext(savedCtx) }()

hubCtx := sentry.SetHubOnContext(requestCtx, hub)
options := []sentry.SpanOption{
sentry.ContinueTrace(hub, r.Header.Get(sentry.SentryTraceHeader), r.Header.Get(sentry.SentryBaggageHeader)),
sentry.ContinueTrace(hubCtx, r.Header.Get(sentry.SentryTraceHeader), r.Header.Get(sentry.SentryBaggageHeader)),
sentry.WithOpName("http.server"),
sentry.WithTransactionSource(transactionSource),
sentry.WithSpanOrigin(sentry.SpanOriginFiber),
}

savedCtx := ctx.UserContext()
requestCtx, cancel := context.WithCancel(savedCtx)
defer cancel()
defer func() { ctx.SetUserContext(savedCtx) }()

transaction := sentry.StartTransaction(
sentry.SetHubOnContext(requestCtx, hub),
hubCtx,
fmt.Sprintf("%s %s", r.Method, transactionName),
options...,
)
Expand Down
15 changes: 8 additions & 7 deletions fiberv3/sentryfiber.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,21 @@ func (h *handler) handle(ctx fiber.Ctx) error {
transactionName := ctx.Path()
transactionSource := sentry.SourceURL

savedCtx := ctx.Context()
requestCtx, cancel := context.WithCancel(savedCtx)
defer cancel()
defer ctx.SetContext(savedCtx)

hubCtx := sentry.SetHubOnContext(requestCtx, hub)
options := []sentry.SpanOption{
sentry.ContinueTrace(hub, r.Header.Get(sentry.SentryTraceHeader), r.Header.Get(sentry.SentryBaggageHeader)),
sentry.ContinueTrace(hubCtx, r.Header.Get(sentry.SentryTraceHeader), r.Header.Get(sentry.SentryBaggageHeader)),
sentry.WithOpName("http.server"),
sentry.WithTransactionSource(transactionSource),
sentry.WithSpanOrigin(sentry.SpanOriginFiber),
}

savedCtx := ctx.Context()
requestCtx, cancel := context.WithCancel(savedCtx)
defer cancel()
defer ctx.SetContext(savedCtx)

transaction := sentry.StartTransaction(
sentry.SetHubOnContext(requestCtx, hub),
hubCtx,
fmt.Sprintf("%s %s", r.Method, transactionName),
options...,
)
Expand Down
2 changes: 1 addition & 1 deletion gin/sentrygin.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ func (h *handler) handle(c *gin.Context) {
}

options := []sentry.SpanOption{
sentry.ContinueTrace(hub, c.GetHeader(sentry.SentryTraceHeader), c.GetHeader(sentry.SentryBaggageHeader)),
sentry.ContinueTrace(sentry.SetHubOnContext(ctx, hub), c.GetHeader(sentry.SentryTraceHeader), c.GetHeader(sentry.SentryBaggageHeader)),
sentry.WithOpName("http.server"),
sentry.WithTransactionSource(transactionSource),
sentry.WithSpanOrigin(sentry.SpanOriginGin),
Expand Down
2 changes: 1 addition & 1 deletion grpc/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func startServerTransaction(ctx context.Context, fullMethod string) (context.Con
transaction := sentry.StartTransaction(
sentry.SetHubOnContext(ctx, hub),
name,
sentry.ContinueTrace(hub, sentryTraceHeader, sentryBaggageHeader),
sentry.ContinueTrace(sentry.SetHubOnContext(ctx, hub), sentryTraceHeader, sentryBaggageHeader),
sentry.WithOpName(defaultServerOperationName),
sentry.WithDescription(name),
sentry.WithTransactionSource(sentry.SourceRoute),
Expand Down
2 changes: 1 addition & 1 deletion http/sentryhttp.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func (h *Handler) handle(handler http.Handler) http.HandlerFunc {
hub.Client().SetSDKIdentifier(sdkIdentifier)

options := []sentry.SpanOption{
sentry.ContinueTrace(hub, r.Header.Get(sentry.SentryTraceHeader), r.Header.Get(sentry.SentryBaggageHeader)),
sentry.ContinueTrace(ctx, r.Header.Get(sentry.SentryTraceHeader), r.Header.Get(sentry.SentryBaggageHeader)),
sentry.WithOpName("http.server"),
sentry.WithTransactionSource(sentry.SourceURL),
sentry.WithSpanOrigin(sentry.SpanOriginStdLib),
Expand Down
19 changes: 14 additions & 5 deletions httpclient/sentryhttpclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,21 @@ func (s *SentryRoundTripper) RoundTrip(request *http.Request) (*http.Response, e
// Only create the `http.client` span only if there is a parent span.
parentSpan := sentry.SpanFromContext(request.Context())
if parentSpan == nil {
if hub := sentry.GetHubFromContext(request.Context()); hub != nil {
request = request.Clone(request.Context())
request.Header.Add(sentry.SentryBaggageHeader, hub.GetBaggage())
request.Header.Add(sentry.SentryTraceHeader, hub.GetTraceparent())
ctx := request.Context()
baggage := sentry.GetBaggage(ctx)
traceparent := sentry.GetTraceparent(ctx)
if baggage != "" || traceparent != "" {
request = request.Clone(ctx)
if baggage != "" {
request.Header.Add(sentry.SentryBaggageHeader, baggage)
}
if traceparent != "" {
request.Header.Add(sentry.SentryTraceHeader, traceparent)
}
if s.propagateTraceparent {
request.Header.Add(sentry.TraceparentHeader, hub.GetTraceparentW3C())
if w3c := sentry.GetTraceparentW3C(ctx); w3c != "" {
request.Header.Add(sentry.TraceparentHeader, w3c)
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion iris/sentryiris.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (h *handler) handle(ctx iris.Context) {
r := ctx.Request()

options := []sentry.SpanOption{
sentry.ContinueTrace(hub, r.Header.Get(sentry.SentryTraceHeader), r.Header.Get(sentry.SentryBaggageHeader)),
sentry.ContinueTrace(sentry.SetHubOnContext(ctx, hub), r.Header.Get(sentry.SentryTraceHeader), r.Header.Get(sentry.SentryBaggageHeader)),
sentry.WithOpName("http.server"),
sentry.WithTransactionSource(sentry.SourceRoute),
sentry.WithSpanOrigin(sentry.SpanOriginIris),
Expand Down
2 changes: 1 addition & 1 deletion negroni/sentrynegroni.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.Ha
hub.Client().SetSDKIdentifier(sdkIdentifier)

options := []sentry.SpanOption{
sentry.ContinueTrace(hub, r.Header.Get(sentry.SentryTraceHeader), r.Header.Get(sentry.SentryBaggageHeader)),
sentry.ContinueTrace(sentry.SetHubOnContext(r.Context(), hub), r.Header.Get(sentry.SentryTraceHeader), r.Header.Get(sentry.SentryBaggageHeader)),
sentry.WithOpName("http.server"),
sentry.WithTransactionSource(sentry.SourceURL),
sentry.WithSpanOrigin(sentry.SpanOriginNegroni),
Expand Down
93 changes: 49 additions & 44 deletions scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,14 +434,15 @@ func (snapshot *scopeSnapshot) mergeLocked(scope *Scope) {
}

snapshot.propagationContext = scope.propagationContext
if scope.span != nil {
snapshot.span = scope.span
}
}

// ApplyToEvent takes the data from the current scope and attaches it to the event.
func (scope *Scope) ApplyToEvent(event *Event, hint *EventHint, client Client) *Event {
return snapshotScopes(client, scope).applyToEvent(event, hint, client)
snapshot := snapshotScopes(client, scope)
// Preserve Scope.ApplyToEvent and legacy Hub behavior while context capture
// deliberately excludes the legacy scope span from merged snapshots.
snapshot.span = scope.GetSpan()
return snapshot.applyToEvent(event, hint, client)
}

// ApplyToEvent implements [EventModifier] for a pre-built snapshot.
Expand Down Expand Up @@ -485,11 +486,30 @@ func (snapshot scopeSnapshot) applyToEvent(event *Event, hint *EventHint, client
event.Contexts = make(map[string]Context)
}

if snapshot.span != nil {
var ctx context.Context
if hint != nil {
ctx = hint.Context
}
if ctx == nil && snapshot.request != nil {
ctx = snapshot.request.Context()
}

// Context is the source of truth for active spans. snapshot.span is retained
// only for legacy Hub captures, whose capture options build a Hub-only
// snapshot instead of the global/isolation snapshot used by context APIs.
var activeSpan *Span
if ctx != nil {
activeSpan = SpanFromContext(ctx)
}
if activeSpan == nil {
activeSpan = snapshot.span
}

if activeSpan != nil {
if _, ok := event.Contexts["trace"]; !ok {
event.Contexts["trace"] = snapshot.span.traceContext().Map()
event.Contexts["trace"] = activeSpan.traceContext().Map()
}
if transaction := snapshot.span.GetTransaction(); transaction != nil {
if transaction := activeSpan.GetTransaction(); transaction != nil {
event.sdkMetaData.dsc = DynamicSamplingContextFromTransaction(transaction)
}
} else {
Expand All @@ -503,17 +523,12 @@ func (snapshot scopeSnapshot) applyToEvent(event *Event, hint *EventHint, client
event.sdkMetaData.dsc = dsc
}

var ctx context.Context
if hint != nil {
ctx = hint.Context
}
if ctx == nil && snapshot.request != nil {
ctx = snapshot.request.Context()
}
if traceID, spanID, ok := client.externalTraceContextFromContext(ctx); event.Type != transactionType && ok {
traceCtx := event.Contexts["trace"]
traceCtx["trace_id"] = traceID.String()
traceCtx["span_id"] = spanID.String()
if event.Type != transactionType && ctx != nil {
if tid, sid, ok := client.externalTraceContextFromContext(ctx); ok {
traceCtx := event.Contexts["trace"]
traceCtx["trace_id"] = tid.String()
traceCtx["span_id"] = sid.String()
}
}

if event.User.IsEmpty() {
Expand Down Expand Up @@ -618,46 +633,36 @@ func hubFromContexts(ctxs ...context.Context) *Hub {
// resolveTrace resolves trace ID and span ID from the given scope and contexts.
//
// The resolution order follows a most-specific-to-least-specific pattern:
// 1. If an external trace resolver was registered (eg. OTel), we prioritise trace context
// information from that
// 2. Check for span directly in contexts (SpanFromContext) - this is the most specific
// source as it represents a span explicitly attached to the current operation's context
// 3. Check scope's span - provides access to span set on the hub's scope
// 4. Fall back to scope's propagation context trace ID
//
// This ordering ensures we always use the most contextually relevant tracing information.
// For example, if a specific span is active for an operation, we use that span's trace/span IDs
// rather than accidentally using a different span that might be set on the hub's scope.
// 1. external trace resolver (eg. OTel), when configured
// 2. active Sentry span from context (SpanFromContext)
// 3. isolation/propagation context carried by the scope
//
// TODO: this should be removed when the span API is introduced. Currently kept for compatibility. The span
// and trace should only be resolved through context.
// Active spans are not read from Scope.
func resolveTrace(scope *Scope, client Client, ctxs ...context.Context) (traceID TraceID, spanID SpanID) {
client = normalizeClient(client)
var span *Span

// Check every context for an external resolver result before considering
// Sentry spans from any context.
for _, ctx := range ctxs {
if ctx == nil {
continue
}
if traceID, spanID, ok := client.externalTraceContextFromContext(ctx); ok {
return traceID, spanID
if tid, sid, ok := client.externalTraceContextFromContext(ctx); ok {
return tid, sid
}
if span = SpanFromContext(ctx); span != nil {
break
}
for _, ctx := range ctxs {
if ctx == nil {
continue
}
if span := SpanFromContext(ctx); span != nil {
return span.TraceID, span.SpanID
}
}

if scope != nil {
scope.mu.RLock()
if span == nil {
span = scope.span
}
if span != nil {
traceID = span.TraceID
spanID = span.SpanID
} else {
traceID = scope.propagationContext.TraceID
}
traceID = scope.propagationContext.TraceID
scope.mu.RUnlock()
}

Expand Down
Loading
Loading