Skip to main content

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​

PageDescription
Getting StartedInstall and instrument your first interface
Attribute Reference[Instrument], [Trace], [Count], [Histogram], [CountFromResult], [HistogramFromResult], [MetricTagFromResult], [MetricTag], [MetricTagConstant], [TraceTag], [TraceTagFromResult], [TraceTagConstant]
Source GeneratorWhat the generator emits — input/output examples
TestingAssert spans and metrics with BCL listeners, no exporter needed
AOT & TrimmingNative 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​

AttributeWhat it recordsWhen
[Trace("name")]Activity spanEvery call — Error status and error.type on exception
[Count("metric")]Counter<long> +1Success only; with When, only when the guard is true
[Histogram("metric")]Histogram<double> elapsed msEvery call including on exception, tagged error.type there, unless When is set
[CountFromResult("metric", "Member")]Counter<long> adds the member's valueSuccess only, when the member is not null
[HistogramFromResult("metric", "Member")]Histogram<double> records the member's valueSuccess only, when the member is not null
[MetricTagFromResult("tag", "Member")]A tag on the method's metricsWith each measurement, when a listener has enabled the instrument
[MetricTag("tag")] on a parameterThe argument, or a member of it, as a tag on the method's metricsWith each measurement, when a listener has enabled the instrument
[MetricTagConstant("tag", value)]A constant tag on the method's metricsWith each measurement, when a listener has enabled the instrument
[TraceTag("tag")] on a parameterActivity.SetTag with the argument, or a member of itRight after the span starts
[TraceTagFromResult("tag")]Activity.SetTag with the return value, or a member of itAfter the call returns
[TraceTagConstant("tag", value)]Activity.SetTag with a constantRight after the span starts, before the argument tags