ZeroAlloc.Telemetry
Source-generated OpenTelemetry instrumentation for .NET.
Add [Instrument] to any interface and the generator emits a sealed proxy class that records Activity spans and Meter instruments per method — without reflection, params object[] boxing, or runtime attribute inspection.
Contents
| Page | Description |
|---|---|
| Getting Started | Install and instrument your first interface |
| Attribute Reference | [Instrument], [Trace], [Count], [Histogram], [CountFromResult], [HistogramFromResult], [MetricTagFromResult], [MetricTag], [MetricTagConstant], [TraceTag], [TraceTagFromResult], [TraceTagConstant] |
| Source Generator | What the generator emits — input/output examples |
| Testing | Assert spans and metrics with BCL listeners, no exporter needed |
| AOT & Trimming | Native AOT compatibility |
Quick Example
[Instrument("MyApp.Orders")]
public interface IOrderService
{
[Trace("order.create")]
[Count("orders.created")]
ValueTask<OrderId> CreateOrderAsync(CreateOrderCommand cmd, CancellationToken ct);
[Trace("order.get")]
[Histogram("order.get_ms")]
ValueTask<Order> GetOrderAsync(OrderId id, CancellationToken ct);
}
The generator emits OrderServiceInstrumented : IOrderService. Wrap your implementation at the DI layer:
services.AddSingleton<OrderServiceImpl>();
services.AddSingleton<IOrderService>(sp =>
new OrderServiceInstrumented(sp.GetRequiredService<OrderServiceImpl>()));
That's all. No OpenTelemetry SDK is required — ActivitySource and Meter are BCL types (net8.0+). Add exporters in your application startup independently.
Instruments at a Glance
| Attribute | What it records | When |
|---|---|---|
[Trace("name")] | Activity span | Every call — Error status and error.type on exception |
[Count("metric")] | Counter<long> +1 | Success only; with When, only when the guard is true |
[Histogram("metric")] | Histogram<double> elapsed ms | Every call including on exception, tagged error.type there, unless When is set |
[CountFromResult("metric", "Member")] | Counter<long> adds the member's value | Success only, when the member is not null |
[HistogramFromResult("metric", "Member")] | Histogram<double> records the member's value | Success only, when the member is not null |
[MetricTagFromResult("tag", "Member")] | A tag on the method's metrics | With each measurement, when a listener has enabled the instrument |
[MetricTag("tag")] on a parameter | The argument, or a member of it, as a tag on the method's metrics | With each measurement, when a listener has enabled the instrument |
[MetricTagConstant("tag", value)] | A constant tag on the method's metrics | With each measurement, when a listener has enabled the instrument |
[TraceTag("tag")] on a parameter | Activity.SetTag with the argument, or a member of it | Right after the span starts |
[TraceTagFromResult("tag")] | Activity.SetTag with the return value, or a member of it | After the call returns |
[TraceTagConstant("tag", value)] | Activity.SetTag with a constant | Right after the span starts, before the argument tags |