Skip to content
Purview Dev logo

Telemetry SourceGenerator

Stable Telemetry SourceGenerator Reviewed 2026-10-07 purview-dev/telemetry-sourcegenerator Star on GitHub 36 activity c-sharp distributed-tracing dotnet events high-performance-logging instrumentation logging melt metrics nuget observability open-telemetry open-telemetry-csharp otel roslyn source-generator spans telemetry tracing llms.txt

This guide gets you up and running with the Purview Telemetry Source Generator in minutes. You will add the package, define a telemetry interface, register it with dependency injection, and start emitting Activities, structured logs, and Metrics.

Add the analyzer package to your project:

Terminal window
dotnet add package Purview.Telemetry.SourceGenerator

Or reference it in your .csproj or Directory.Build.props:

<PackageReference Include="Purview.Telemetry.SourceGenerator">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>analyzers</IncludeAssets>
</PackageReference>

You may also need runtime dependencies depending on which telemetry types you use:

  • System.Diagnostics.DiagnosticSource for Activities
  • Microsoft.Extensions.Logging.Abstractions for ILogger
  • System.Diagnostics.Metrics for metrics (built into .NET 8+)

See Installation for the full dependency matrix.

Create a public interface and decorate it with class-level attributes. The generator creates the implementation and a DI registration extension.

using Purview.Telemetry;
[Logger]
public interface IOrderServiceLogs
{
[Info]
void OrderPlaced(int orderId, string customerName);
[Warning]
void OrderNotFound(int orderId);
}
[ActivitySource]
public interface IOrderServiceTracing
{
[Activity]
Activity? PlacingOrder(int orderId);
[Event]
void OrderValidated(Activity? activity, int orderId);
}
[Meter]
public interface IOrderServiceMetrics
{
[Counter]
void OrderPlaced(int itemsInOrder);
[Histogram]
void OrderProcessingTime(long milliseconds);
}

One interface can generate Activities, Logging, and Metrics from the same methods:

[ActivitySource]
[Logger]
[Meter]
public interface IOrderServiceTelemetry
{
[Activity]
[Info]
[AutoCounter]
Activity? PlacingOrder(int orderId, [Baggage] string region);
[Event]
[Trace]
void OrderProcessed(Activity? activity, long durationMs);
[Warning]
void OrderFailed(int orderId, Exception exception);
}

The generator creates an extension method named Add{InterfaceNameWithoutI}():

services.AddOrderServiceTelemetry();

Inject the interface into your service:

public class OrderService(IOrderServiceTelemetry telemetry)
{
public void PlaceOrder(int orderId)
{
using var activity = telemetry.PlacingOrder(orderId, "EMEA");
// ...
telemetry.OrderProcessed(activity, stopwatch.ElapsedMilliseconds);
}
}

A single method call can emit an Activity, a log entry, and a metric simultaneously. See Generation for how registration helpers are produced and how to control them.

The generator records the generated meter and activity source names in every assembly’s metadata. In the project that registers them, opt into the TelemetryNames class, which aggregates its own names with those of every assembly it references:

// Program.cs or Properties/AssemblyInfo.cs
[assembly: TelemetryGeneration(GenerateTelemetryNamesClass = true)]
builder.AddServiceDefaults(TelemetryNames.MeterNames, TelemetryNames.ActivitySourceNames);

See Generation for the full behaviour across multi-project solutions.

Return IDisposable? from a log method to create scoped log entries:

[Logger]
interface IOrderTelemetry
{
[Info]
IDisposable? ProcessingOrder(Guid orderId); // Logs at start and end of scope
[Error]
void OrderFailed(Exception ex, Guid orderId);
}
public async Task ProcessOrderAsync(Guid orderId)
{
using (telemetry.ProcessingOrder(orderId))
{
// Processing logic here
}
}

Track multiple stages within a single activity:

[ActivitySource("ShippingService")]
interface IShippingTelemetry
{
[Activity]
Activity? ShippingPackage([Baggage]string trackingNumber);
[Event]
void PackageLabeled(Activity? activity);
[Event]
void PackageWeighed(Activity? activity, [Tag]decimal weight);
[Event]
void PackageShipped(Activity? activity, [Tag]string carrier);
}

Use [AutoCounter] for simple counting without a measurement parameter — every call increments by 1:

[Meter("ApiService")]
interface IApiMetrics
{
[AutoCounter]
void RequestReceived([Tag]string endpoint, [Tag]string method);
[AutoCounter]
void RequestFailed([Tag]string endpoint, [Tag]int statusCode);
}

To inspect the generated source, add this to your .csproj:

<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>

Generated files appear under obj/Debug|Release/<tfm>/generated/Purview.Telemetry.SourceGenerator/Purview.Telemetry.SourceGenerator.TelemetrySourceGenerator/.

  1. Return Activity? from Activity-starting methods, not Activity or void, so callers can dispose and reuse the activity.
  2. Pass activities explicitly — pass the Activity? from the starting method to event/context methods rather than relying on Activity.Current.
  3. One namespace — use the single using Purview.Telemetry; import.
  4. OpenTelemetry naming — v5 defaults to OpenTelemetry conventions (snake_case tags, hierarchical metrics). See Naming conventions.
  5. DI registration — use the generated Add{InterfaceName}() extension methods.
  6. Testing — telemetry interfaces are easy to mock in unit tests. See Testing.
  • Activities — deep dive into distributed tracing with Activities, Events, and Context
  • Logging — structured logging and the v1/v2 generation modes
  • Metrics — counters, histograms, and observable instruments
  • Multi-Targeting — combine multiple telemetry types in one interface
  • Generation Options — control code generation, DI, and naming
  • Generated Output — see what code is actually generated
  • Migration — convert existing ILogger/ActivitySource/metrics code using the IDE refactorings