Skip to content

feat(metrics): Trace-connected Metrics (Analyzers)#4840

Open
Flash0ver wants to merge 26 commits intomainfrom
feat/trace-connected-metrics-analyzers
Open

feat(metrics): Trace-connected Metrics (Analyzers)#4840
Flash0ver wants to merge 26 commits intomainfrom
feat/trace-connected-metrics-analyzers

Conversation

@Flash0ver
Copy link
Member

@Flash0ver Flash0ver commented Jan 15, 2026

based on

My little weekend-project:
Add a Diagnostic-Analyzer to our Compiler-Extensions, that warn users when a metric would not be emitted due to an unsupported numeric value type.

Context:
For the numeric type of a Metric, we currently allow 64-bit sized integral (signed) and floating-point numbers.
That means that e.g. ulong, System.Int128, or decimal are currently not supported by Sentry.
The compile-time constraint of Sentry.SentryMetric<T> only constrains to non-nullable value types.
Alternatively, we could have implemented respectively types overloads for the method groups:

public void EmitCounter(string name, long value);
public void EmitCounter(string name, float value);
public void EmitCounter(string name, double value);
public void EmitCounter(string name, long value, Scope? scope)
public void EmitCounter(string name, float value, Scope? scope)
public void EmitCounter(string name, double value, Scope? scope)
public void EmitCounter ..

public void EmitGauge ..
public void EmitDistribution ..

To avoid this explosion of overloads per method group,
and be similar to the implementation of System.Diagnostics.Metrics,
we are not compile-time constraining unsupported types,
but are instead run-time constraining unsupported types (no-op and Debug-Diagnostic-Logging).

To still warn users unfamiliar with the System.Diagnostics.Metrics.Meter-based APIs,
I built an Analyzer over some weekends to guide new users.

options.Experimental.SetBeforeSendMetric(static SentryMetric? (SentryMetric metric) =>
{
    if (metric.TryGetValue<int>(out int int32) && int32 < 0)
    {
        return null;
    }

    if (metric.TryGetValue<uint>(out uint uint32) && uint32 == 0) //SENTRY1001
    {
        return null;
    }

    return metric;
});

SentrySdk.Experimental.Metrics.EmitCounter("my.counter", 1);
SentrySdk.Experimental.Metrics.EmitCounter("my.counter", 1m); //SENTRY1001
SentrySdk.Experimental.Metrics.EmitCounter("my.counter", StringComparison.CurrentCultureIgnoreCase); //SENTRY1001
SentrySdk.Experimental.Metrics.EmitCounter<ulong>("my.counter", default); //SENTRY1001

@Flash0ver Flash0ver self-assigned this Jan 15, 2026
@Flash0ver Flash0ver added Roslyn The .NET Compiler Platform, Roslyn Components and Extensions, Microsoft.CodeAnalysis Metrics labels Jan 15, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Jan 15, 2026

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

Generated by 🚫 dangerJS against e8b9376

@codecov
Copy link

codecov bot commented Jan 15, 2026

Codecov Report

❌ Patch coverage is 76.47059% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.90%. Comparing base (1753b23) to head (e8b9376).

Files with missing lines Patch % Lines
...ensions/Analyzers/TraceConnectedMetricsAnalyzer.cs 73.91% 6 Missing and 6 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4840      +/-   ##
==========================================
+ Coverage   73.87%   73.90%   +0.02%     
==========================================
  Files         496      498       +2     
  Lines       17951    18002      +51     
  Branches     3516     3527      +11     
==========================================
+ Hits        13262    13304      +42     
  Misses       3831     3831              
- Partials      858      867       +9     

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

Flash0ver and others added 5 commits January 16, 2026 13:21
Provides hierarchical constants for metric units supported by Sentry Relay,
organized into Duration, Information, and Fraction categories.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@Flash0ver Flash0ver marked this pull request as ready for review January 16, 2026 14:41
@Flash0ver Flash0ver marked this pull request as draft January 19, 2026 15:26
Base automatically changed from feat/trace-connected-metrics to main February 8, 2026 21:37
@github-actions
Copy link
Contributor

github-actions bot commented Feb 13, 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(metrics): Trace-connected Metrics (Analyzers) by Flash0ver in #4840
  • feat: Network calls for Session Replay on Android by jamescrosswell in #4860

Fixes 🐛

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

Dependencies ⬆️

Deps

  • chore(deps): update Cocoa SDK to v9.5.0 by github-actions in #4944
  • chore(deps): update Native SDK to v0.13.0 by github-actions in #4941
  • chore(deps): update CLI to v3.2.2 by github-actions in #4943
  • chore(deps): update Java SDK to v8.33.0 by github-actions in #4933
  • chore(deps): update CLI to v3.2.0 by github-actions in #4805
    • NOTE: Sentry CLI v3 removed support for the legacy API key authentication method. Sentry CLI now only supports authenticating with Auth Tokens. If you are using API key authentication via SentryApiKey, you need to generate an Auth Token and use SentryAuthToken, instead.
  • chore(deps): update Cocoa SDK to v9.4.1 by github-actions in #4928
  • 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.


Rule ID | Category | Severity | Notes
--------|----------|----------|-------
SENTRY1001 | Support | Warning | TraceConnectedMetricsAnalyzer No newline at end of file
Copy link
Member Author

Choose a reason for hiding this comment

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

question: ID

I'm a a bit certain about the ID.
Also, I'm not sure if it should be two separate IDs, one for the Emit* methods, and one for SentryMetric.TryGetValue<TValue>(out TValue value) method.

https://github.com/SimonCropp/Polyfill
-->
<ItemGroup>
<PackageReference Include="Polyfill" Version="9.8.1" PrivateAssets="all" />
Copy link
Member Author

Choose a reason for hiding this comment

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

note: see also #4879

defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: Description,
helpLinkUri: null
Copy link
Member Author

Choose a reason for hiding this comment

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

question: docs

Should we document the Analyzer?
If so, we could provide a link to the documentation page here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Will do after the merge / after releasing it via #4962.

private static readonly string Description = "Integers should be a 64-bit signed integer, while doubles should be a 64-bit floating point number.";

private static readonly DiagnosticDescriptor Rule = new(
id: DiagnosticIds.Sentry1001,
Copy link
Member Author

Choose a reason for hiding this comment

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

question: CHANGELOG

Should we mention this Analyzer, and Analyzer in general, in the CHANGELOG?
If so, in the Features category, or a separate category?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the features section... they're basically there to improve the developer experience for SDK users right?

Copy link
Member Author

Choose a reason for hiding this comment

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

added CHANGELOG entry via 6c5ca23

/// Guide consumers to use the public API of <see href="https://develop.sentry.dev/sdk/telemetry/metrics/">Sentry Trace-connected Metrics</see> correctly.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class TraceConnectedMetricsAnalyzer : DiagnosticAnalyzer
Copy link
Member Author

Choose a reason for hiding this comment

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

question: do we want to do this in general?

This started off as a weekend-project for me.
Do we want to provide this Analyzer in the first place?

Copy link
Collaborator

Choose a reason for hiding this comment

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

To avoid this explosion of overloads per method group,
and be similar to the implementation of System.Diagnostics.Metrics,
we are not compile-time constraining unsupported types,
but are instead run-time constraining unsupported types (no-op and Debug-Diagnostic-Logging).

Maybe not... we're trying to create a compile time constraint here (using analysers) because we don't want to create a compile time constraint using the typing system???

It might be a better experience for SDK users and less effort for us to maintain, simply to add some overloads that convert the other numeric types automatically to supported types.

Copy link
Member Author

Choose a reason for hiding this comment

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

We drafted an alternative API shape, with explicit overloads.

But the devil is in the "API explosion":

-public void EmitCounter<T>(string name, T value) where T : struct
+public void EmitCounter(string name, int value)
+public void EmitCounter(string name, long value)
+// for all EmitCounter overloads
+// for EmitGauge and EmitDistribution method groups

And a follow-up uncertainty is about the Before-Send-Callback:

options.Experimental.SetBeforeSendMetric(static metric =>
{
    if (metric.TryGetValue(out int integer)) // if not emitted as `Int32`, do we want to cast to `Int32`?
    if (metric.TryGetInt32(out int integer)) // similar "explosion" of methods

    return metric;
});

So we decided to keep the API shape as is and actually do go with the Analyzer.
Should we have made a mistake, we can change both the API and the Analyzer for the next major version.


internal static class DiagnosticCategories
{
internal const string Sentry = nameof(Sentry);
Copy link
Member Author

Choose a reason for hiding this comment

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

question: Category

I'm a bit uncertain about the Category.

The Category can be used to e.g. configure all Diagnostic of a category, rather than or in addition to configuring specific diagnostics per ID,
e.g. via an .globalconfig file

is_global = true

# Configure this new rule only
dotnet_diagnostic.SENTRY1001.severity = none

# Configure all rules within the "Sentry" Category
dotnet_analyzer_diagnostic.category-Sentry.severity = none

The ideas / variants I am having are

@Flash0ver Flash0ver marked this pull request as ready for review February 27, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Metrics Roslyn The .NET Compiler Platform, Roslyn Components and Extensions, Microsoft.CodeAnalysis

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants