-
Notifications
You must be signed in to change notification settings - Fork 3
Getting Started
This guide will help you get started with the Purview Telemetry Source Generator in minutes.
Add the NuGet package to your project:
Install-Package Purview.Telemetry.SourceGenerator -Version 4.1.0dotnet add package Purview.Telemetry.SourceGenerator --version 4.1.0<PackageReference Include="Purview.Telemetry.SourceGenerator" Version="4.1.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>analyzers</IncludeAssets>
</PackageReference>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 DI
services.AddOrderTelemetry();
// Use in your code
public 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);
}
}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 DI
services.AddPaymentTelemetry();
// Use in your code
public 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;
}
}
}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 DI
services.AddInventoryMetrics();
// Use in your code
public 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);
}
}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;
}
}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
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
}
}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);
}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 1
public 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);
}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/.
-
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
"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
"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
If you're upgrading from v3, see the Breaking Changes guide for detailed migration instructions, especially:
Important
Consider helping children around the world affected by conflict. You can donate any amount to War Child here - any amount can help save a life.
Purview Telemetry Source Generator v4.0.0-prerelease.1 | Home | Getting Started | FAQ | Breaking Changes | GitHub