Skip to content

Commit 4d83f6d

Browse files
CopilotHazAT
andcommitted
fix(dotnet-sdk): add metrics reference with SENTRY1001 analyzer docs
Add references/metrics.md documenting: - EmitCounter, EmitGauge, EmitDistribution API - Supported numeric types (byte, short, int, long, float, double) - SENTRY1001 Roslyn diagnostic analyzer - SetBeforeSendMetric callback - SentryMetric.TryGetValue<T> - EnableMetrics config option Update SKILL.md to include Metrics in feature recommendations and reference file table. Addresses getsentry/sentry-dotnet#4840 Co-Authored-By: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: HazAT <363802+HazAT@users.noreply.github.com> Agent-Logs-Url: https://github.com/getsentry/sentry-for-ai/sessions/a7f23faf-52d2-4883-89e9-a8846d16dd40
1 parent edfbc5d commit 4d83f6d

File tree

2 files changed

+216
-0
lines changed

2 files changed

+216
-0
lines changed

skills/sentry-dotnet-sdk/SKILL.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ Present a concrete recommendation based on what you found. Lead with a proposal
9393

9494
**Optional (enhanced observability):**
9595
-**Profiling** — CPU profiling; recommend for performance-critical services running on .NET 6+
96+
-**Metrics** — counters, gauges, distributions linked to traces; recommend for apps that need custom business metrics
9697
-**Crons** — detect missed/failed scheduled jobs; recommend when Hangfire, Quartz.NET, or scheduled endpoints detected
9798

9899
**Recommendation logic:**
@@ -103,6 +104,7 @@ Present a concrete recommendation based on what you found. Lead with a proposal
103104
| Tracing | **Always for ASP.NET Core** — request traces, EF Core spans, HttpClient spans are high-value |
104105
| Logging | App uses `ILogger<T>`, Serilog, NLog, or log4net |
105106
| Profiling | Performance-critical service on .NET 6+ |
107+
| Metrics | App needs custom business metrics (request counts, queue depths, response times) |
106108
| Crons | App uses Hangfire, Quartz.NET, or scheduled Azure Functions |
107109

108110
Propose: *"I recommend setting up Error Monitoring + Tracing + Logging. Want me to also add Profiling or Crons?"*
@@ -460,6 +462,7 @@ Load the corresponding reference file and follow its steps:
460462
| Tracing | `references/tracing.md` | Server apps, distributed tracing, EF Core spans, custom instrumentation |
461463
| Profiling | `references/profiling.md` | Performance-critical apps on .NET 6+ |
462464
| Logging | `references/logging.md` | `ILogger<T>`, Serilog, NLog, log4net integration |
465+
| Metrics | `references/metrics.md` | Custom counters, gauges, distributions; `EmitCounter`, `EmitGauge`, `EmitDistribution` |
463466
| Crons | `references/crons.md` | Hangfire, Quartz.NET, or scheduled function monitoring |
464467

465468
For each feature: read the reference file, follow its steps exactly, and verify before moving on.
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
# Metrics — Sentry .NET SDK
2+
3+
> Minimum SDK: `Sentry` ≥ 6.1.0
4+
> Roslyn Analyzer (`SENTRY1001`): `Sentry.Compiler.Extensions` (ships with `Sentry` NuGet)
5+
6+
---
7+
8+
## Overview
9+
10+
Sentry Trace-connected Metrics lets you emit counters, gauges, and distributions that are automatically linked to your Sentry traces. Metrics are batched and flushed periodically (every 5 seconds or 100 items).
11+
12+
Three metric types:
13+
14+
| Type | Method | Purpose |
15+
|------|--------|---------|
16+
| Counter | `EmitCounter` | Increment a value (e.g. request count, items processed) |
17+
| Gauge | `EmitGauge` | Track a current value (e.g. queue depth, active connections) |
18+
| Distribution | `EmitDistribution` | Record a measured value for statistical analysis (e.g. response time, payload size) |
19+
20+
---
21+
22+
## Enabling Metrics
23+
24+
Metrics are enabled via `SentryOptions.EnableMetrics`:
25+
26+
```csharp
27+
SentrySdk.Init(options =>
28+
{
29+
options.Dsn = "___YOUR_DSN___";
30+
options.EnableMetrics = true;
31+
});
32+
```
33+
34+
> **Without `EnableMetrics = true`**, all `EmitCounter` / `EmitGauge` / `EmitDistribution` calls are no-ops.
35+
36+
---
37+
38+
## Emitting Metrics
39+
40+
Access the metrics API via `SentrySdk.Metrics` or `hub.Metrics`:
41+
42+
### Counter
43+
44+
```csharp
45+
// Simple counter — increment by 1
46+
SentrySdk.Metrics.EmitCounter("orders.completed", 1);
47+
48+
// Counter with attributes and scope
49+
SentrySdk.Metrics.EmitCounter("orders.completed", 1,
50+
new Dictionary<string, object> { ["region"] = "eu-west" },
51+
scope);
52+
```
53+
54+
### Gauge
55+
56+
```csharp
57+
// Simple gauge
58+
SentrySdk.Metrics.EmitGauge("queue.depth", 42);
59+
60+
// Gauge with unit
61+
SentrySdk.Metrics.EmitGauge("cpu.usage", 0.85, MeasurementUnit.Fraction.Ratio);
62+
```
63+
64+
### Distribution
65+
66+
```csharp
67+
// Simple distribution
68+
SentrySdk.Metrics.EmitDistribution("response.time", 120.5);
69+
70+
// Distribution with unit and attributes
71+
SentrySdk.Metrics.EmitDistribution("payload.size", 4096L,
72+
MeasurementUnit.Information.Byte,
73+
new Dictionary<string, object> { ["endpoint"] = "/api/orders" },
74+
scope);
75+
```
76+
77+
---
78+
79+
## Supported Numeric Types
80+
81+
The metrics API accepts a generic `T` constrained to `struct`, but only the following numeric types are supported at runtime:
82+
83+
| Type | C# keyword | Supported |
84+
|------|-----------|-----------|
85+
| `System.Byte` | `byte` | ✅ Yes |
86+
| `System.Int16` | `short` | ✅ Yes |
87+
| `System.Int32` | `int` | ✅ Yes |
88+
| `System.Int64` | `long` | ✅ Yes |
89+
| `System.Single` | `float` | ✅ Yes |
90+
| `System.Double` | `double` | ✅ Yes |
91+
| `System.UInt32` | `uint` | ❌ No |
92+
| `System.UInt64` | `ulong` | ❌ No |
93+
| `System.Decimal` | `decimal` | ❌ No |
94+
| `System.Int128` | `Int128` | ❌ No |
95+
96+
Unsupported types are silently dropped at runtime (no-op with a debug diagnostic log message). The Roslyn analyzer `SENTRY1001` catches these at compile time instead.
97+
98+
---
99+
100+
## `SENTRY1001` — Roslyn Diagnostic Analyzer
101+
102+
The SDK ships a compile-time Roslyn analyzer that reports a **warning** when a metrics API is called with an unsupported numeric type.
103+
104+
**Diagnostic ID:** `SENTRY1001`
105+
**Category:** `Sentry`
106+
**Severity:** Warning
107+
**Message:** `{type} is unsupported type for Sentry Metrics. The only supported types are byte, short, int, long, float, and double.`
108+
109+
**Triggers on:**
110+
- `SentryMetricEmitter.EmitCounter<T>()` with unsupported `T`
111+
- `SentryMetricEmitter.EmitGauge<T>()` with unsupported `T`
112+
- `SentryMetricEmitter.EmitDistribution<T>()` with unsupported `T`
113+
- `SentryMetric.TryGetValue<T>()` with unsupported `T`
114+
115+
```csharp
116+
// ✅ No warning — supported types
117+
SentrySdk.Metrics.EmitCounter("my.counter", 1); // int → OK
118+
SentrySdk.Metrics.EmitCounter("my.counter", 1L); // long → OK
119+
SentrySdk.Metrics.EmitCounter("my.counter", 1.5f); // float → OK
120+
SentrySdk.Metrics.EmitCounter("my.counter", 1.5); // double → OK
121+
122+
// ⚠️ SENTRY1001 — unsupported types
123+
SentrySdk.Metrics.EmitCounter("my.counter", 1m); // decimal
124+
SentrySdk.Metrics.EmitCounter("my.counter", (ulong)100); // ulong
125+
SentrySdk.Metrics.EmitCounter("my.counter", (uint)1); // uint
126+
```
127+
128+
**To suppress** (not recommended): Add `#pragma warning disable SENTRY1001` or `[SuppressMessage]`. However, the metric will still be silently dropped at runtime.
129+
130+
---
131+
132+
## `SetBeforeSendMetric` Callback
133+
134+
Filter or modify metrics before they are sent to Sentry. Return `null` to drop a metric.
135+
136+
```csharp
137+
SentrySdk.Init(options =>
138+
{
139+
options.Dsn = "___YOUR_DSN___";
140+
options.EnableMetrics = true;
141+
options.SetBeforeSendMetric(static (SentryMetric metric) =>
142+
{
143+
// Drop metrics with negative values
144+
if (metric.TryGetValue<double>(out var value) && value < 0)
145+
{
146+
return null;
147+
}
148+
149+
return metric;
150+
});
151+
});
152+
```
153+
154+
**Signature:** `void SetBeforeSendMetric(Func<SentryMetric, SentryMetric?> callback)`
155+
156+
---
157+
158+
## `SentryMetric.TryGetValue<T>`
159+
160+
Extract the numeric value from a `SentryMetric` with a type check. Returns `false` if the metric's value type does not match `T`. The same supported-type rules apply — using an unsupported type triggers `SENTRY1001`.
161+
162+
```csharp
163+
options.SetBeforeSendMetric(static (SentryMetric metric) =>
164+
{
165+
// ✅ Supported — double
166+
if (metric.TryGetValue<double>(out var doubleValue))
167+
{
168+
Console.WriteLine($"Metric value: {doubleValue}");
169+
}
170+
171+
// ✅ Supported — long
172+
if (metric.TryGetValue<long>(out var longValue) && longValue > 1000)
173+
{
174+
return null; // drop high-value metrics
175+
}
176+
177+
return metric;
178+
});
179+
```
180+
181+
---
182+
183+
## `SentryMetric` Properties
184+
185+
| Property | Type | Description |
186+
|----------|------|-------------|
187+
| `Timestamp` | `DateTimeOffset` | When the metric was recorded |
188+
| `TraceId` | `SentryId` | Trace ID linking metric to a trace |
189+
| `SpanId` | `SpanId?` | Span that was active when metric was emitted |
190+
| `Type` | `SentryMetricType` | `Counter`, `Gauge`, or `Distribution` |
191+
| `Name` | `string` | Hierarchical name (e.g. `api.response_time`) |
192+
| `Unit` | `string?` | Unit of measurement (only for Gauge/Distribution) |
193+
194+
---
195+
196+
## Config Options
197+
198+
| Option | Type | Default | Notes |
199+
|--------|------|---------|-------|
200+
| `EnableMetrics` | `bool` | `false` | Must be `true` to emit metrics |
201+
| `SetBeforeSendMetric` | `Func<SentryMetric, SentryMetric?>` || Filter/modify metrics before send; return `null` to drop |
202+
203+
---
204+
205+
## Troubleshooting
206+
207+
| Issue | Cause | Solution |
208+
|-------|-------|----------|
209+
| Metrics not appearing in Sentry | `EnableMetrics` not set | Set `options.EnableMetrics = true` in `SentrySdk.Init()` |
210+
| `SENTRY1001` compiler warning | Unsupported numeric type | Use `byte`, `short`, `int`, `long`, `float`, or `double` instead |
211+
| Metric emitted but silently dropped | Unsupported type at runtime (no analyzer) | Ensure the `Sentry.Compiler.Extensions` analyzer is loaded; check build output for `SENTRY1001` |
212+
| `SetBeforeSendMetric` drops all metrics | Callback returns `null` unconditionally | Verify your filter logic; return `metric` for metrics you want to keep |
213+
| `TryGetValue<T>` returns `false` | Type mismatch between emitted type and queried type | Use the same type that was passed to `EmitCounter`/`EmitGauge`/`EmitDistribution` |

0 commit comments

Comments
 (0)