ZeroAlloc.Telemetry
1.12.4
dotnet add package ZeroAlloc.Telemetry --version 1.12.4
NuGet\Install-Package ZeroAlloc.Telemetry -Version 1.12.4
<PackageReference Include="ZeroAlloc.Telemetry" Version="1.12.4" />
<PackageVersion Include="ZeroAlloc.Telemetry" Version="1.12.4" />
<PackageReference Include="ZeroAlloc.Telemetry" />
paket add ZeroAlloc.Telemetry --version 1.12.4
#r "nuget: ZeroAlloc.Telemetry, 1.12.4"
#:package ZeroAlloc.Telemetry@1.12.4
#addin nuget:?package=ZeroAlloc.Telemetry&version=1.12.4
#tool nuget:?package=ZeroAlloc.Telemetry&version=1.12.4
ZeroAlloc.Telemetry
Put [Instrument] on an interface and get OpenTelemetry spans and metrics for every call, generated at compile time: no hand-written ActivitySource boilerplate, no reflection, and nothing allocated when nobody is listening.
Why ZeroAlloc.Telemetry
- 0 B per call when nothing listens, the common production case after sampling. Tests assert it for
Task,ValueTaskandValueTask<T>methods, tagged metrics included. - 6.7× faster than a straightforward hand-written
ActivitySource+Meterwrapper with no listener attached, measured in the same run: the proxy skips the instrumented path entirely until something subscribes. - Works under NativeAOT and trimming. Plain generated code over BCL
ActivitySourceandMeter, no reflection and noparams object[]; CI publishes a NativeAOT app. - Mistakes surface at compile time. A result member that is not numeric, or a metric attribute on an interface without
[Instrument], is a compiler diagnostic.
Install
dotnet add package ZeroAlloc.Telemetry
No OpenTelemetry SDK dependency: add the exporters you want, OTLP, Prometheus or Console, in your application.
Quick start
using System.Diagnostics;
using ZeroAlloc.Telemetry;
using var listener = new ActivityListener
{
ShouldListenTo = source => source.Name == "MyApp.Orders",
Sample = (ref ActivityCreationOptions<ActivityContext> _) => ActivitySamplingResult.AllData,
ActivityStopped = activity => Console.WriteLine($"span {activity.DisplayName}: {activity.Duration.TotalMilliseconds:F1} ms"),
};
ActivitySource.AddActivityListener(listener);
IOrderService orders = new OrderServiceInstrumented(new OrderService()); // generated proxy
await orders.PlaceOrderAsync("book", CancellationToken.None); // prints the order.place span
[Instrument("MyApp.Orders")]
public interface IOrderService
{
[Trace("order.place")]
[Count("orders.placed")]
[Histogram("order.place_ms")]
ValueTask<int> PlaceOrderAsync(string product, CancellationToken ct);
}
public sealed class OrderService : IOrderService
{
public async ValueTask<int> PlaceOrderAsync(string product, CancellationToken ct)
{
await Task.Delay(10, ct);
return 42;
}
}
In an application, register the proxy in DI in place of the implementation, and subscribe with OpenTelemetry's AddSource("MyApp.Orders") and AddMeter("MyApp.Orders"). Your code keeps calling IOrderService.
What you get
- Spans per method, with error status and
error.typeon exceptions, or on a failedResultyou choose to treat as an error. - Counters and duration histograms per method, recorded on success or on every call, optionally only when a result guard such as
IsSuccessholds. - Metrics from the return value, such as tokens consumed or a cost, recorded straight from a member of the result.
- Tags on spans and metrics from arguments, from the result, or as constants, built only when a listener needs them.
- Consistent instrumentation: every method gets the same span, counter and histogram pattern, with no Activity left undisposed and no metric forgotten.
- Testable with BCL listeners, no exporter needed: Testing.
- Every attribute and option: attribute reference. Compile-time diagnostics: the full list.
Performance
No listener attached, the production case after sampling. .NET 10.0.12, i9-12900HK, BenchmarkDotNet v0.15.8. Time is relative to the uninstrumented call in the same run.
| Method | Time vs direct call | Allocated |
|---|---|---|
| Direct call, no instrumentation | 1× | 0 B |
Hand-written ActivitySource + Counter + Histogram |
30.5× | 0 B |
| ZA.Telemetry generated proxy | 4.6× | 0 B |
Neither wrapper allocates. The hand-written version still runs its async state machine, takes timestamps and calls instruments nobody listens to; the proxy checks for listeners and hands back the inner task as is. Hand-written code can do the same, but every method has to remember to. Methodology: docs/performance.md.
Learn more
- Documentation: telemetry.zeroalloc.net
- Getting started, what the generator emits and AOT and trimming
- Related packages: ZeroAlloc.Cache and ZeroAlloc.Resilience, proxies generated the same way; ZeroAlloc.Results for
Result-aware metrics - Contributing: issues and pull requests are welcome on GitHub
- License: MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages (21)
Showing the top 5 NuGet packages that depend on ZeroAlloc.Telemetry:
| Package | Downloads |
|---|---|
|
ZeroAlloc.Scheduling.Telemetry
ZeroAlloc.Telemetry bridge for ZeroAlloc.Scheduling — wraps IJobTypeExecutor with a source-generated OpenTelemetry proxy. |
|
|
AI.Sentinel
Security monitoring middleware for IChatClient — prompt injection, hallucination, and operational anomaly detection with an intervention engine. |
|
|
Thalos.NET.Abstractions
Ports and models for Thalos.NET. Depends only on ZeroAlloc primitives and Microsoft.Extensions.AI.Abstractions. |
|
|
ZeroAlloc.EventSourcing
Source-generated event-sourced aggregates for .NET. Zero-allocation event dispatch, AOT-safe. |
|
|
ZeroAlloc.Outbox.Telemetry
ZeroAlloc.Telemetry bridge for ZeroAlloc.Outbox — wraps IOutboxTypeDispatcher with a source-generated OpenTelemetry proxy. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.12.4 | 1,531 | 10/16/2026 |
| 1.12.3 | 1,973 | 10/15/2026 |
| 1.12.2 | 2,623 | 10/12/2026 |
| 1.12.1 | 1,363 | 10/11/2026 |
| 1.12.0 | 3,471 | 10/10/2026 |
| 1.11.0 | 5,552 | 10/1/2026 |
| 1.10.0 | 979 | 10/1/2026 |
| 1.9.0 | 107 | 10/1/2026 |
| 1.8.0 | 148 | 9/30/2026 |
| 1.7.0 | 4,580 | 9/27/2026 |
| 1.6.4 | 1,025 | 9/26/2026 |
| 1.6.3 | 4,389 | 9/20/2026 |
| 1.6.2 | 1,568 | 9/19/2026 |
| 1.6.1 | 5,078 | 8/8/2026 |
| 1.6.0 | 269 | 8/7/2026 |
| 1.5.0 | 199 | 8/7/2026 |
| 1.4.2 | 210 | 8/7/2026 |
| 1.4.1 | 191 | 8/7/2026 |
| 1.4.0 | 124 | 8/7/2026 |
| 1.3.0 | 4,394 | 5/13/2026 |