Skip to content

feat: Add support to send OTEL traces via OTLP#4899

Draft
jamescrosswell wants to merge 17 commits intomainfrom
oltp-integration
Draft

feat: Add support to send OTEL traces via OTLP#4899
jamescrosswell wants to merge 17 commits intomainfrom
oltp-integration

Conversation

@jamescrosswell
Copy link
Collaborator

@github-actions
Copy link
Contributor

github-actions bot commented Feb 9, 2026

Semver Impact of This PR

None (no version bump detected)

📋 Changelog Preview

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


Features ✨

  • feat: Add support to send OTEL traces via OTLP by jamescrosswell in #4899

Fixes 🐛

  • fix: Log Warning instead of Error when ratelimited by bitsandfoxes in #4927

Dependencies ⬆️

Deps

  • chore(deps): update Native SDK to v0.12.8 by github-actions in #4929
  • Apps built using the Sentry SDK for .NET must now target iOS version 15 or higher. Previously only version 13 or higher was required. (#4781) by github-actions in #4781
  • Bump Cocoa SDK from v8.57.3 to v9.2.0 (#4781) by github-actions in #4781
  • chore(deps): update Native SDK to v0.12.7 by github-actions in #4920

Other

  • test(blazor): Add Playwright E2E tests for navigation breadcrumbs by bruno-garcia in #4908
  • test(android): Use volatile to produce SIGSEGV in native crash test by jpnurmi in #4919

🤖 This preview updates automatically when you update the PR.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 9, 2026

Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against 304c5bc

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is unfortunately a bit more complex for SDK users to initialise because the TracerProviderBuilder.AddOtlpExporter method has no override with an IServiceProvider parameter (so there's no way to read the DSN from the sentry options that get added to the service registry later on).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Without adding an explicit reference to this package, we get an error because the other packages try to load types from a different assembly version. It's the workaround for this problem basically.

@codecov
Copy link

codecov bot commented Feb 9, 2026

Codecov Report

❌ Patch coverage is 55.84416% with 34 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.55%. Comparing base (c999f5a) to head (c8856a6).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
...rc/Sentry.OpenTelemetry/SentryOptionsExtensions.cs 0.00% 13 Missing ⚠️
...y.OpenTelemetry/TracerProviderBuilderExtensions.cs 68.75% 4 Missing and 1 partial ⚠️
...i.CommunityToolkit.Mvvm/SentryOptionsExtensions.cs 0.00% 3 Missing ⚠️
src/Sentry/Internal/Hub.cs 62.50% 2 Missing and 1 partial ⚠️
src/Sentry/SentryGraphQLHttpMessageHandler.cs 0.00% 2 Missing and 1 partial ⚠️
src/Sentry/SentryHttpMessageHandler.cs 0.00% 2 Missing and 1 partial ⚠️
src/Sentry.OpenTelemetry/SentryPropagator.cs 50.00% 1 Missing and 1 partial ⚠️
src/Sentry/SentryMessageHandler.cs 0.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4899      +/-   ##
==========================================
- Coverage   73.86%   73.55%   -0.31%     
==========================================
  Files         496      497       +1     
  Lines       17927    17987      +60     
  Branches     3511     3527      +16     
==========================================
- Hits        13241    13231      -10     
- Misses       3826     3882      +56     
- Partials      860      874      +14     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jamescrosswell
Copy link
Collaborator Author

Please add an entry to CHANGELOG.md to the "Unreleased" section. Make sure the entry includes this PR's number.

Odd that we're still seeing this after having moved to conventional commits.

It doesn't look like the danger workflow is aware of our release.yml file or accounts for this in it's logic.

@Flash0ver any ideas what we might be doing wrong?

@Flash0ver
Copy link
Member

@Flash0ver any ideas what we might be doing wrong?

I'm not sure how to remove the Danger Changelog Missing automation off the top of my head ... I'll look into it tomorrow.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is problematic... the alternative would be to drop the guard here:

// Validate and set required values
if (traceId == SentryId.Empty)
{
throw new ArgumentOutOfRangeException(nameof(traceId), "cannot be empty");
}

...or to replace it with a log rather than throwing an exception. However then I think we risk sending invalid envelopes to Sentry and losing events if Sentry drops these.

I think this is the correct approach. The only code that we have which was using this was in the SentryMessageHandler implementations.

There is one small additional risk because it's also used by the GetBaggage method, which is part of the public SDK. I added some code to log a warning if that method is called when OTEL instrumentation is being used:

public BaggageHeader GetBaggage()
{
if (_options.Instrumenter is Instrumenter.OpenTelemetry)
{
_options.LogWarning("GetBaggage should not be called when using OpenTelemetry - it may throw an exception");
}
var span = GetSpan();
if (span?.GetTransaction().GetDynamicSamplingContext() is { IsEmpty: false } dsc)
{
return dsc.ToBaggageHeader();
}
var propagationContext = CurrentScope.PropagationContext;
return propagationContext.GetOrCreateDynamicSamplingContext(_options, _replaySession).ToBaggageHeader();
}

And I added some info about the possible exception to the public XML docs for that method:

/// <exception cref="ArgumentOutOfRangeException">Can be thrown when using OpenTelemetry instrumentation and
/// System.Diagnostics.Activity.Current is null. This method should not be used when instrumenting with OTEL.
/// </exception>

It's not great, but I can't think of any better ways to handle it. @Flash0ver thoughts?

Copy link
Member

@Flash0ver Flash0ver Feb 13, 2026

Choose a reason for hiding this comment

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

Hmmm .... not sure if ArgumentOutOfRangeException is the correct exception here, as it's usually used when numeric values are greater than or less than expected ... maybe just ArgumentException ... but I'm not really answering the question, aren't I?!

Another thought that I'm having is that we're quite inconsistent with Argument validation:
At some locations we throw, e.g. ArgumentNullException, at other places we don't throw but only log via the DiagnosticLogger if enabled. But again, no solution to the problem.

Perhaps we could solve this issue with an Analyzer.
Throw at run-time, and warn at build-time for invocations of this method when Sentry.OpenTelemetry is referenced. This may cause some false positives in cases where Sentry.OpenTelemetry is referenced, but actually not initialized and the Instrumenter is not Instrumenter.OpenTelemetry at runtime. But it would be a bit of a "best effort" solution. At least there shouldn't be false negatives ... unless I am missing something.

(thought: it seems that every time I'm out of ideas, I suggest some Compiler Extension to fix it 😉)

Copy link
Member

Choose a reason for hiding this comment

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

Follow-up thought:
I suppose the best solution would be to make this method actually not invocable when the is Instrumenter.OpenTelemetry. Well, if the option is not set at runtime, we can't just remove this method at build time.

Just for some brain-storming: Maybe we could re-direct the invocation - via an Interceptor - to another method if Sentry.OpenTelemetry is referenced ... but this solution has the same issue as the Analyzer from above: at build-time, we only know whether Sentry.OpenTelemetry is referenced, not if OpenTelemetry is actually initialized .. well, we could scan for the respective AddSentry/UseSentry invocation, but what if there are more than one Hubs ... we can't really draw the connection there unambiguously.

Copy link
Collaborator Author

@jamescrosswell jamescrosswell Feb 15, 2026

Choose a reason for hiding this comment

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

we're quite inconsistent with Argument validation:
At some locations we throw, e.g. ArgumentNullException, at other places we don't throw but only log via the DiagnosticLogger if enabled. But again, no solution to the problem.

I don't really want to throw an exception here... it's not by design. However:

  • I can't change the signature for the public API (it has a public GetBaggage method that returns a non-nullable type)
  • The only other option would be to suppress the error and return an empty SentryId representing the TraceId... but then we risk Sentry dropping events.

Maybe, if the GetBaggage() method is called when OTEL tracing is enabled, we log a warning and simply return an emply baggage header? That way we avoid the possibility of our code erroring out (we never call this method if OTEL tracing is enabled... we can but an assert in there to ensure this) but we do make it really clear the method shouldn't be used with OTEL tracing?

Edit: I've gone ahead and done this... also added an Assert to the underlying methods in the DSC that throw the exception, to ensure we never call these in our own code when OTEL tracing is enabled.

Copy link
Collaborator Author

@jamescrosswell jamescrosswell Feb 16, 2026

Choose a reason for hiding this comment

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

Hmmm .... not sure if ArgumentOutOfRangeException is the correct exception here, as it's usually used when numeric values are greater than or less than expected ... maybe just ArgumentException ... but I'm not really answering the question, aren't I?!

We could change that... I think the traceId is essentially a GUID and you get an out of range if this is empty (i.e. all zeros)... if you squint, a guid is kind of a number but you have to squint 😜 Not really related to this PR however, since that code was added ages ago. We can create a new issue to address this if you think it's worth doing.

@jamescrosswell
Copy link
Collaborator Author

jamescrosswell commented Feb 18, 2026

Dang...

ILC : AOT analysis error IL3050: OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ActivityExtensions.CreateRepeatedFieldOfSpanSetCountAction(): Using member 'System.Reflection.Emit.DynamicMethod.DynamicMethod(String,Type,Type[],Module,Boolean)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. Creating a DynamicMethod requires dynamic code. [/Users/runner/work/sentry-dotnet/sentry-dotnet/test/Sentry.TrimTest/Sentry.TrimTest.csproj]

Warning

Needs a bump to the Otel package dependencies, including some (minor) breaking changes.

@jamescrosswell jamescrosswell marked this pull request as ready for review February 18, 2026 07:31
Copy link

@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.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Note: this is just a minor refactoring/tweak to make it easier to create tests that want/need to ensure the correct message is logged with the diagnostic logger. It's slightly annoying to do so good to have this be a one liner.

@jamescrosswell jamescrosswell marked this pull request as draft February 19, 2026 10:06
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.

Investigate OTLP integration

3 participants

Comments