Skip to content

feat: add OTLP integration support#1229

Merged
giortzisg merged 24 commits intomasterfrom
feat/otlp
Apr 8, 2026
Merged

feat: add OTLP integration support#1229
giortzisg merged 24 commits intomasterfrom
feat/otlp

Conversation

@giortzisg
Copy link
Copy Markdown
Contributor

@giortzisg giortzisg commented Mar 18, 2026

Description

This PR adds the OpenTelemetry integration for OTLP, while also separating the error linking from the span exporter. The PR also includes some deviations from the Spec, so that the package follows closely the OTel conventions.

The main changes:

  • Adds a public sentryotel.NewOtelIntegration() for OTel users to link traces with other signals.
  • Decouple linking to work without the sentry span map.
  • Add the sentryotlp package where users can create a NewTraceExporter to send to sentry's otlp endpoint using the otlptracehttp exporter.
  • Keep otlp http client configuration options for the TraceExporter

Deviations from the spec:

  • There is no setup_otlp_traces_exporter flag since users need to manually provide the exporter to the TracerProvider
  • No collector_url. For users that are already using the collector, the only thing they need to set up is the ErrorLinkingIntegration. Having a WithCollectorURL option mixes concerns, since the sentryotlp trace exporter should only forward to sentry. Using the collector with Sentry requires changing the otlp http configuration on the collector and not the integration level.

Issues

Changelog Entry Instructions

To add a custom changelog entry, uncomment the section above. Supports:

  • Single entry: just write text
  • Multiple entries: use bullet points
  • Nested bullets: indent 4+ spaces

For more details: custom changelog entries

Reminders

Changelog Entry

  • Add OTLP trace exporter via new otel/otlp sub-module
    • sentryotlp.NewTraceExporter sends OTel spans directly to Sentry's OTLP endpoint.
    • sentryotel.NewOtelIntegration links Sentry errors, logs, and metrics to the active OTel trace. Works with both direct-to-Sentry and collector-based setups.
    • NewSentrySpanProcessor, NewSentryPropagator, and SentrySpanMap are deprecated and will be removed in 0.47.0. To Migrate use sentryotlp.NewTraceExporter instead:
// Before
sentry.Init(sentry.ClientOptions{Dsn: dsn, EnableTracing: true, TracesSampleRate: 1.0})

tp := sdktrace.NewTracerProvider(
	sdktrace.WithSpanProcessor(sentryotel.NewSentrySpanProcessor()),
)
otel.SetTextMapPropagator(sentryotel.NewSentryPropagator())
otel.SetTracerProvider(tp)

// After:
sentry.Init(sentry.ClientOptions{
	Dsn: dsn, EnableTracing: true, TracesSampleRate: 1.0,
	Integrations: func(i []sentry.Integration) []sentry.Integration {
		return append(i, sentryotel.NewOtelIntegration())
	},
})

exporter, _ := sentryotlp.NewTraceExporter(ctx, dsn)
tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exporter))
otel.SetTracerProvider(tp)

@giortzisg giortzisg requested a review from sl0thentr0py March 18, 2026 13:31
@giortzisg giortzisg self-assigned this Mar 18, 2026
@linear-code
Copy link
Copy Markdown

linear-code bot commented Mar 18, 2026

GO-121 Add OTLP support

@giortzisg giortzisg changed the title Feat/otlp feat: add OTLP integration support Mar 18, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 18, 2026

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


Breaking Changes 🛠

  • Add support for Echo v5 by Scorfly in #1183

New Features ✨

  • Add OTLP trace exporter via new otel/otlp sub-module by giortzisg in #1229
    • sentryotlp.NewTraceExporter sends OTel spans directly to Sentry's OTLP endpoint.
    • sentryotel.NewOtelIntegration links Sentry errors, logs, and metrics to the active OTel trace. Works with both direct-to-Sentry and collector-based setups.
    • NewSentrySpanProcessor, NewSentryPropagator, and SentrySpanMap are deprecated and will be removed in 0.47.0. To Migrate use sentryotlp.NewTraceExporter instead:
    // Before
    sentry.Init(sentry.ClientOptions{Dsn: dsn, EnableTracing: true, TracesSampleRate: 1.0})
    
    tp := sdktrace.NewTracerProvider(
    	sdktrace.WithSpanProcessor(sentryotel.NewSentrySpanProcessor()),
    )
    otel.SetTextMapPropagator(sentryotel.NewSentryPropagator())
    otel.SetTracerProvider(tp)
    
    // After:
    sentry.Init(sentry.ClientOptions{
    	Dsn: dsn, EnableTracing: true, TracesSampleRate: 1.0,
    	Integrations: func(i []sentry.Integration) []sentry.Integration {
    		return append(i, sentryotel.NewOtelIntegration())
    	},
    })
    
    exporter, _ := sentryotlp.NewTraceExporter(ctx, dsn)
    tp := sdktrace.NewTracerProvider(sdktrace.WithBatcher(exporter))
    otel.SetTracerProvider(tp)
  • Add IsSensitiveHeader helper to easily distinguish which headers to scrub for PII. by giortzisg in #1239

Bug Fixes 🐛

  • (ci) Update validate-pr action to remove draft enforcement by stephanie-anderson in #1237
  • (fiber) Use UserContext for transaction to enable OTel trace linking by giortzisg in #1252
  • Race condition when getting envelope identifier by giortzisg in #1250

Internal Changes 🔧

Deps

  • Bump OpenTelemetry SDK to 1.40.0 by giortzisg in #1243
  • Bump changelog-preview.yml from 2.24.1 to 2.25.2 by dependabot in #1247
  • Bump getsentry/craft from 2.24.1 to 2.25.2 by dependabot in #1248
  • Bump codecov/codecov-action from 5.5.2 to 6.0.0 by dependabot in #1245
  • Bump actions/create-github-app-token from 2.2.1 to 3.0.0 by dependabot in #1246
  • Bump actions/setup-go from 6.3.0 to 6.4.0 by dependabot in #1244

Other

  • Update validate-pr workflow by stephanie-anderson in #1242
  • Add PR validation workflow by stephanie-anderson in #1234

🤖 This preview updates automatically when you update the PR.

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Event processor overwrites entire trace context losing fields
    • The event processor now updates trace_id and span_id in the existing event.Contexts["trace"] map (creating it only when absent), preserving SDK-populated trace fields.

Create PR

Or push these changes by commenting:

@cursor push 38eedc31b4
Preview (38eedc31b4)
diff --git a/otel/internal/common/event_processor.go b/otel/internal/common/event_processor.go
--- a/otel/internal/common/event_processor.go
+++ b/otel/internal/common/event_processor.go
@@ -27,9 +27,12 @@
 	if event.Contexts == nil {
 		event.Contexts = make(map[string]map[string]any)
 	}
-	event.Contexts["trace"] = map[string]any{
-		"trace_id": otelSpanContext.TraceID().String(),
-		"span_id":  otelSpanContext.SpanID().String(),
+	traceContext, found := event.Contexts["trace"]
+	if !found || traceContext == nil {
+		traceContext = make(map[string]any)
+		event.Contexts["trace"] = traceContext
 	}
+	traceContext["trace_id"] = otelSpanContext.TraceID().String()
+	traceContext["span_id"] = otelSpanContext.SpanID().String()
 	return event
 }

diff --git a/otel/internal/common/event_processor_test.go b/otel/internal/common/event_processor_test.go
--- a/otel/internal/common/event_processor_test.go
+++ b/otel/internal/common/event_processor_test.go
@@ -22,7 +22,7 @@
 		{name: "without existing trace context"},
 		{
 			name:          "with existing trace context",
-			existingTrace: map[string]any{"trace_id": "123", "parent_span_id": "456"},
+			existingTrace: map[string]any{"trace_id": "123", "parent_span_id": "456", "op": "http.server"},
 		},
 	}
 
@@ -43,10 +43,12 @@
 			}))
 
 			got := linkTraceContextToErrorEvent(event, &sentry.EventHint{Context: ctx})
-			assert.Equal(t, map[string]any{
-				"trace_id": traceID.String(),
-				"span_id":  spanID.String(),
-			}, got.Contexts["trace"])
+			assert.Equal(t, traceID.String(), got.Contexts["trace"]["trace_id"])
+			assert.Equal(t, spanID.String(), got.Contexts["trace"]["span_id"])
+			if tt.existingTrace != nil {
+				assert.Equal(t, "456", got.Contexts["trace"]["parent_span_id"])
+				assert.Equal(t, "http.server", got.Contexts["trace"]["op"])
+			}
 		})
 	}
 }

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

@giortzisg giortzisg changed the base branch from master to deps/bump-otel April 1, 2026 12:46
Base automatically changed from deps/bump-otel to master April 2, 2026 11:58
this needs to be exposed to also update the LastEventID, when capturing
an event with a hint
this adds the externalTraceResolver. In order to correctly extract trace
context information from otel without adding the otel/trace dependency
on the root package, we need to register an externalTraceResolver that
applies for errors on ApplyToEvent and for logs and metrics on
ResolveTrace
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 46af688. Configure here.

@giortzisg giortzisg merged commit c8ccbf3 into master Apr 8, 2026
22 checks passed
@giortzisg giortzisg deleted the feat/otlp branch April 8, 2026 14:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add OTLP support

2 participants