Skip to content
Beta

Get started with Purview Telemetry Source Generator in 5 minutes! This guide walks you through creating your first telemetry-enabled application.

  • .NET 8.0 or higher (or .NET Framework 4.8+)
  • Your favourite IDE (Visual Studio, Visual Studio Code, or Rider)

Add the NuGet package to your project:

Terminal window
dotnet add package Purview.Telemetry.SourceGenerator --version 5.0.0-prerelease.1
Terminal window
Install-Package Purview.Telemetry.SourceGenerator -Version 5.0.0-prerelease.1
<ItemGroup>
<PackageReference Include="Purview.Telemetry.SourceGenerator" Version="5.0.0-prerelease.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>

Step 2: Create Your First Telemetry Interface

Section titled “Step 2: Create Your First Telemetry Interface”

Create a new file IOrderServiceTelemetry.cs:

using System.Diagnostics;
using Purview.Telemetry;
[ActivitySource("QuickStart.OrderService")] // For distributed tracing
[Logger] // For structured logging
[Meter("QuickStart.OrderService")] // For metrics
public interface IOrderServiceTelemetry
{
// Multi-target method: Creates Activity + Logs Info + Increments Counter
[Activity]
[Info]
[AutoCounter]
Activity? ProcessingOrder([Baggage] int orderId, [Tag] string customerName);
// Event + Log
[Event]
[Debug]
void OrderValidated(Activity? activity, [Tag] decimal totalAmount);
// Event + Log
[Event(ActivityStatusCode.Ok)]
[Info]
void OrderCompleted(Activity? activity, [Tag] TimeSpan processingTime);
// Error logging
[Error]
[AutoCounter]
void OrderFailed(Exception ex, int orderId);
}

What just happened?

  • [ActivitySource], [Logger], [Meter] - Tells the generator to create implementations for all three telemetry types
  • [Activity], [Info], [AutoCounter] - Multi-target attributes that generate code for all three types from a single method
  • [Baggage] - Data propagates across service boundaries
  • [Tag] - Local activity data (doesn’t propagate)

Step 3: Register with Dependency Injection

Section titled “Step 3: Register with Dependency Injection”

In your Program.cs or Startup.cs:

using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// Register the telemetry interface
// The generator creates this extension method automatically!
builder.Services.AddOrderServiceTelemetry();
var app = builder.Build();

Create OrderService.cs:

public class OrderService
{
private readonly IOrderServiceTelemetry _telemetry;
private readonly IOrderRepository _repository;
public OrderService(IOrderServiceTelemetry telemetry, IOrderRepository repository)
{
_telemetry = telemetry;
_repository = repository;
}
public async Task<Order?> ProcessOrderAsync(int orderId, string customerName)
{
// Single call creates Activity + Logs + Increments counter!
using var activity = _telemetry.ProcessingOrder(orderId, customerName);
try
{
// Validate order
var order = await _repository.GetOrderAsync(orderId);
if (order == null)
{
_telemetry.OrderFailed(new Exception("Order not found"), orderId);
return null;
}
// Add event to activity + log debug message
_telemetry.OrderValidated(activity, order.TotalAmount);
// Process payment, shipping, etc...
await Task.Delay(100); // Simulated work
var stopwatch = Stopwatch.StartNew();
// ... more processing ...
stopwatch.Stop();
// Add event + log completion
_telemetry.OrderCompleted(activity, stopwatch.Elapsed);
return order;
}
catch (Exception ex)
{
// Log error + increment error counter
_telemetry.OrderFailed(ex, orderId);
throw;
}
}
}
Section titled “Step 5: Configure OpenTelemetry (Optional but Recommended)”

To view your telemetry, add OpenTelemetry exporters:

Terminal window
dotnet add package OpenTelemetry.Exporter.Console
dotnet add package OpenTelemetry.Extensions.Hosting

Configure in Program.cs:

using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
var builder = WebApplication.CreateBuilder(args);
// Add OpenTelemetry
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddSource("QuickStart.OrderService") // Match your ActivitySource name
.AddConsoleExporter())
.WithMetrics(metrics => metrics
.AddMeter("QuickStart.OrderService") // Match your Meter name - this defaults to the assembly name if not specified
.AddConsoleExporter())
.WithLogging(logging => logging
.AddConsoleExporter());
builder.Services.AddOrderServiceTelemetry();
var app = builder.Build();

Run your application and call the ProcessOrderAsync method. You’ll see:

Console Output (Activity Trace):

Activity.TraceId: 80000000-0000-0000-0000-000000000001
Activity.SpanId: 8000000000000001
Activity.DisplayName: ProcessingOrder
Activity.Kind: Internal
Activity.StartTime: 2026-02-14T10:30:00.0000000Z
Activity.Duration: 00:00:00.1234567
Activity.Tags:
order_id: 42
customer_name: John Doe (Baggage)
Activity.Events:
OrderValidated [10:30:00.050]
total_amount: 99.99
OrderCompleted [10:30:00.123]
processing_time: 00:00:00.123

Console Output (Logging):

info: IOrderServiceTelemetry[0]
ProcessingOrder: OrderId = 42, CustomerName = John Doe
dbug: IOrderServiceTelemetry[0]
OrderValidated: TotalAmount = 99.99
info: IOrderServiceTelemetry[0]
OrderCompleted: ProcessingTime = 00:00:00.123

Console Output (Metrics):

Meter: QuickStart.OrderService
Instrument: order_service.processing_order (Counter)
Value: 1
[ActivitySource("QuickStart.PaymentService")]
[Logger]
[Meter("QuickStart.PaymentService")]
public interface IPaymentServiceTelemetry
{
[Activity(ActivityKind.Client)]
[Info]
[AutoCounter]
Activity? ProcessingPayment([Baggage] Guid paymentId, [Tag] decimal amount);
[Event]
[Debug]
void PaymentValidated(Activity? activity);
[Event(ActivityStatusCode.Error)]
[Error]
void PaymentDeclined(Activity? activity, string reason);
[Histogram]
void PaymentDuration([InstrumentMeasurement] int milliseconds, [Tag] bool success);
}
[Logger]
public interface IDataServiceTelemetry
{
// Returns IDisposable for automatic start/end logging
[Info]
IDisposable? FetchingDataFromDatabase(int recordId);
[Debug]
void RecordFetched(string recordType);
}
// Usage
public async Task<Data> GetDataAsync(int id)
{
using (_telemetry.FetchingDataFromDatabase(id)) // Logs at start and end
{
var data = await _repository.GetAsync(id);
_telemetry.RecordFetched(data.Type);
return data;
}
}
[Meter("QuickStart.CacheService")]
public interface ICacheMetrics
{
[AutoCounter]
void CacheHit([Tag] string cacheKey);
[AutoCounter]
void CacheMiss([Tag] string cacheKey);
[Histogram]
void CacheLookupDuration([InstrumentMeasurement] int milliseconds);
}

Replace console exporters with:

Jaeger (Traces):

Terminal window
dotnet add package OpenTelemetry.Exporter.Jaeger
.WithTracing(tracing => tracing
.AddSource("QuickStart.*")
.AddJaegerExporter())

Prometheus (Metrics):

Terminal window
dotnet add package OpenTelemetry.Exporter.Prometheus.AspNetCore
.WithMetrics(metrics => metrics
.AddMeter("QuickStart.*")
.AddPrometheusExporter())
app.MapPrometheusScrapingEndpoint();

Azure Monitor (All):

Terminal window
dotnet add package Azure.Monitor.OpenTelemetry.Exporter
.UseAzureMonitor()

Want to see what code the generator creates?

Add to your .csproj:

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

Generated files appear in:

obj/Debug/net8.0/generated/Purview.Telemetry.SourceGenerator/

You’ll find:

  • {Namespace}.OrderServiceTelemetryCore.Activity.g.cs - Activity implementation
  • {Namespace}.OrderServiceTelemetryCore.Logging.g.cs - Logging implementation
  • {Namespace}.OrderServiceTelemetryCore.Metric.g.cs - Metrics implementation
  • {Namespace}.OrderServiceTelemetryCoreDIExtension.DependencyInjection.g.cs - DI registration

Here’s a complete minimal application you can copy and run:

Program.cs:

using System.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;
using Purview.Telemetry;
var builder = Host.CreateApplicationBuilder(args);
// Configure OpenTelemetry
builder.Services.AddOpenTelemetry()
.WithTracing(t => t.AddSource("QuickStart").AddConsoleExporter())
.WithMetrics(m => m.AddMeter("QuickStart").AddConsoleExporter())
.WithLogging(l => l.AddConsoleExporter());
// Register telemetry
builder.Services.AddOrderServiceTelemetry();
// Register application service
builder.Services.AddSingleton<OrderService>();
var app = builder.Build();
// Run a test
var service = app.Services.GetRequiredService<OrderService>();
await service.ProcessOrderAsync(42, "John Doe");
Console.WriteLine("\nDone! Check the output above for telemetry.");
// Telemetry interface
[ActivitySource("QuickStart")]
[Logger]
[Meter("QuickStart")]
public interface IOrderServiceTelemetry
{
[Activity]
[Info]
[AutoCounter]
Activity? ProcessingOrder([Baggage] int orderId, [Tag] string customerName);
[Event(ActivityStatusCode.Ok)]
[Info]
void OrderCompleted(Activity? activity, [Tag] TimeSpan duration);
}
// Application service
public class OrderService
{
private readonly IOrderServiceTelemetry _telemetry;
public OrderService(IOrderServiceTelemetry telemetry)
{
_telemetry = telemetry;
}
public async Task ProcessOrderAsync(int orderId, string customerName)
{
var sw = Stopwatch.StartNew();
using var activity = _telemetry.ProcessingOrder(orderId, customerName);
// Simulate work
await Task.Delay(100);
_telemetry.OrderCompleted(activity, sw.Elapsed);
}
}

Run it:

Terminal window
dotnet run

“Type or namespace ‘ActivitySourceAttribute’ could not be found”

Section titled ““Type or namespace ‘ActivitySourceAttribute’ could not be found””

Solution: Add the using statement:

using Purview.Telemetry;

“No extension method ‘Add{InterfaceName}’”

Section titled ““No extension method ‘Add{InterfaceName}’””

Solution: Rebuild the project to trigger source generation:

Terminal window
dotnet build

Solution: Ensure OpenTelemetry is configured with the correct source/meter names that match your interface attributes.

“Build warnings about unbounded enumeration”

Section titled ““Build warnings about unbounded enumeration””

Solution: Add [ExpandEnumerable] attribute with maximumValueCount:

[Info]
void LogItems([ExpandEnumerable(maximumValueCount: 100)] string[] items);