Getting Started
Getting Started with Purview Telemetry Source Generator
Section titled “Getting Started with Purview Telemetry Source Generator”This guide will help you get started with the Purview Telemetry Source Generator in minutes.
Installation
Section titled “Installation”Add the NuGet package to your project:
Package Manager Console
Section titled “Package Manager Console”Install-Package Purview.Telemetry.SourceGenerator -Version 5.0.0-prerelease.1.NET CLI
Section titled “.NET CLI”dotnet add package Purview.Telemetry.SourceGenerator --version 5.0.0-prerelease.1Project File (.csproj or Directory.Build.props)
Section titled “Project File (.csproj or Directory.Build.props)”<PackageReference Include="Purview.Telemetry.SourceGenerator" Version="5.0.0-prerelease.1"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>analyzers</IncludeAssets></PackageReference>Quick Start Examples
Section titled “Quick Start Examples”Example 1: Simple Activity Tracking
Section titled “Example 1: Simple Activity Tracking”Track operations with distributed tracing:
using Purview.Telemetry;
[ActivitySource("OrderService")]interface IOrderTelemetry{ [Activity] Activity? ProcessingOrder([Baggage]int orderId, [Tag]string customerName);
[Event] void OrderValidated(Activity? activity, [Tag]decimal totalAmount);
[Event] void OrderCompleted(Activity? activity, [Tag]TimeSpan processingTime);}
// Register with DIservices.AddOrderTelemetry();
// Use in your codepublic class OrderService(IOrderTelemetry telemetry){ public async Task ProcessOrderAsync(int orderId, string customerName) { using var activity = telemetry.ProcessingOrder(orderId, customerName);
var order = await ValidateOrderAsync(orderId); telemetry.OrderValidated(activity, order.TotalAmount);
await SaveOrderAsync(order);
telemetry.OrderCompleted(activity, stopwatch.Elapsed); }}Example 2: Structured Logging
Section titled “Example 2: Structured Logging”Generate structured logs with compile-time safety:
using Purview.Telemetry;
[Logger]interface IPaymentTelemetry{ [Info] void ProcessingPayment(Guid paymentId, decimal amount);
[Warning] void PaymentRetry(Guid paymentId, int attemptNumber);
[Error] void PaymentFailed(Exception ex, Guid paymentId);
[Debug] void PaymentDetails(string processor, string transactionId);}
// Register with DIservices.AddPaymentTelemetry();
// Use in your codepublic class PaymentService(IPaymentTelemetry telemetry){ public async Task<bool> ProcessPaymentAsync(Guid paymentId, decimal amount) { telemetry.ProcessingPayment(paymentId, amount);
try { var result = await _processor.ChargeAsync(paymentId, amount); telemetry.PaymentDetails(result.Processor, result.TransactionId); return true; } catch (Exception ex) { telemetry.PaymentFailed(ex, paymentId); return false; } }}Example 3: Performance Metrics
Section titled “Example 3: Performance Metrics”Track performance metrics with counters and histograms:
using Purview.Telemetry;
[Meter("InventoryService")]interface IInventoryMetrics{ [AutoCounter] void InventoryChecked([Tag]string warehouse);
[Histogram] void InventoryLookupDuration([InstrumentMeasurement]int milliseconds, [Tag]string warehouse);
[Counter] void ItemsProcessed([InstrumentMeasurement]int count, [Tag]string itemType);}
// Register with DIservices.AddInventoryMetrics();
// Use in your codepublic class InventoryService(IInventoryMetrics metrics){ public async Task<int> CheckInventoryAsync(string warehouse) { metrics.InventoryChecked(warehouse);
var sw = Stopwatch.StartNew(); var count = await _repository.GetCountAsync(warehouse); metrics.InventoryLookupDuration((int)sw.ElapsedMilliseconds, warehouse);
return count; }
public void ProcessItems(string itemType, int count) { // ... processing logic ... metrics.ItemsProcessed(count, itemType); }}Example 4: Multi-Target (Combined Telemetry)
Section titled “Example 4: Multi-Target (Combined Telemetry)”Generate Activities, Logs, AND Metrics from a single interface:
using Purview.Telemetry;
[ActivitySource("UserService")][Logger][Meter("UserService")]interface IUserServiceTelemetry{ // Multi-target: Creates Activity + Logs Info + Increments Counter [Activity] [Info] [AutoCounter] Activity? AuthenticatingUser([Baggage]string username, [Tag]string ipAddress);
// Logs only [Warning] void InvalidLoginAttempt(string username, string reason);
// Activity event + Log [Event] [Debug] void UserAuthenticated(Activity? activity, [Tag]Guid userId, [Tag]string role);
// Metric only [Histogram] void AuthenticationDuration([InstrumentMeasurement]int milliseconds, [Tag]bool success);}
// Register with DI (single registration for all telemetry types)services.AddUserServiceTelemetry();
// Use in your code - single call emits multiple telemetry types!public class AuthService(IUserServiceTelemetry telemetry){ public async Task<User?> AuthenticateAsync(string username, string ipAddress) { // Single call creates Activity AND logs AND increments counter! using var activity = telemetry.AuthenticatingUser(username, ipAddress);
var sw = Stopwatch.StartNew(); var user = await _userRepository.FindAsync(username);
if (user == null) { telemetry.InvalidLoginAttempt(username, "User not found"); telemetry.AuthenticationDuration((int)sw.ElapsedMilliseconds, false); return null; }
// Adds event to Activity AND logs telemetry.UserAuthenticated(activity, user.Id, user.Role);
// Records histogram telemetry.AuthenticationDuration((int)sw.ElapsedMilliseconds, true);
return user; }}Next Steps
Section titled “Next Steps”Now that you’ve seen the basics, explore more advanced features:
- Activities - Deep dive into distributed tracing with Activities, Events, and Context
- Logging - Structured logging with Generation v2 features
- Metrics - Counters, Histograms, and Observable metrics
- Multi-Targeting - Combine multiple telemetry types in one interface
- Generation Options - Control code generation, dependency injection, and naming
- Generated Output - See what code is actually generated
- Sample Application - Full .NET Aspire sample with all features
Common Patterns
Section titled “Common Patterns”Pattern 1: Scoped Logging
Section titled “Pattern 1: Scoped Logging”Return IDisposable? to create scoped log entries:
[Logger]interface IOrderTelemetry{ [Info] IDisposable? ProcessingOrder(Guid orderId); // Logs at start and end
[Error] void OrderFailed(Exception ex, Guid orderId);}
public async Task ProcessOrderAsync(Guid orderId){ using (telemetry.ProcessingOrder(orderId)) { // Processing logic here // Automatically logs duration when disposed }}Pattern 2: Activity with Multiple Events
Section titled “Pattern 2: Activity with Multiple Events”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);}
public async Task ShipAsync(string trackingNumber, Package package){ using var activity = telemetry.ShippingPackage(trackingNumber);
await LabelPackageAsync(package); telemetry.PackageLabeled(activity);
var weight = await WeighPackageAsync(package); telemetry.PackageWeighed(activity, weight);
var carrier = await SchedulePickupAsync(package); telemetry.PackageShipped(activity, carrier);}Pattern 3: Auto-Incrementing Counters
Section titled “Pattern 3: Auto-Incrementing Counters”Use [AutoCounter] for simple counting without measurement parameters:
[Meter("ApiService")]interface IApiMetrics{ [AutoCounter] void RequestReceived([Tag]string endpoint, [Tag]string method);
[AutoCounter] void RequestFailed([Tag]string endpoint, [Tag]int statusCode);}
// Every call automatically increments by 1public IActionResult Get(string id){ metrics.RequestReceived("/api/items/{id}", "GET");
var item = _repository.Find(id); if (item == null) { metrics.RequestFailed("/api/items/{id}", 404); return NotFound(); }
return Ok(item);}Viewing Generated Code
Section titled “Viewing Generated Code”To inspect the generated source code, add this to your .csproj:
<PropertyGroup> <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles></PropertyGroup>Generated files will appear in obj/Debug|Release/generated/Purview.Telemetry.SourceGenerator/.
Tips and Best Practices
Section titled “Tips and Best Practices”- Use explicit return types - Always return
Activity?from activity methods, notvoid - Pass activities explicitly - Don’t rely on
Activity.Current; pass activity parameters - Namespace - Use single
using Purview.Telemetry;import (v4+) - Naming convention - v4 defaults to OpenTelemetry conventions (snake_case tags, hierarchical metrics)
- DI registration - Use generated
Add{InterfaceName}()extension methods - Testing - Mock telemetry interfaces easily in unit tests
- Multi-targeting - Combine Activities + Logs + Metrics when you need complete observability
Troubleshooting
Section titled “Troubleshooting”Build Errors
Section titled “Build Errors”“Type or namespace ‘ActivitySourceAttribute’ could not be found”
- Ensure you added
using Purview.Telemetry;(v4) or correct namespace (v3) - Check that the NuGet package is installed correctly
“No implementation found for interface”
- Verify the interface has required attributes (
[ActivitySource],[Logger], or[Meter]) - Check that methods have appropriate method-level attributes
- Rebuild the project to trigger source generation
Runtime Issues
Section titled “Runtime Issues”“ActivitySource not producing traces”
- Ensure you’ve called the
Add{InterfaceName}()DI registration method - Configure OpenTelemetry to listen to your ActivitySource name
- Check that your tracing exporter is configured correctly
“Logs not appearing”
- Verify
ILoggeris configured in your application - Check log level filters aren’t excluding your log messages
- Ensure DI registration was called
“Metrics not collected”
- Configure a metrics exporter in your application
- Verify the Meter name matches what your collector expects
- Check that metrics are being recorded with valid measurement values
Migration from v3
Section titled “Migration from v3”If you’re upgrading from v3, see the Breaking Changes guide for detailed migration instructions, especially: