Microsoft Resiliency Extensions and Polly Part 2 - Telemetry with Polly

February 03, 2026#Software Development
Article
Author image.

Jeff Zuerlein, Senior Consultant

Adding telemetry to your resilience pipeline with Polly gives you insight into how your application behaves and interacts with the external services you consume. The whole point of Polly is to define what your application should do when those external services perform badly, fail, or get overloaded. Being able to see, verify, and measure that behavior helps you define the best strategy for your application.

TelemetryOptions

To utilize the telemetry options, you’ll need to add the Polly.Extensions package to your project. For many developers, the first priority is logging. I won’t get into logging options here because the topic has already been covered in other posts.

Structured Logging with Serilog and App Insights

We can begin by creating a TelemetryOptions object and defining what the LoggerFactory will use. In this case, I’ll keep it simple and log the output to the console.

var telemetryOptions = new TelemetryOptions
{
    LoggerFactory = LoggerFactory.Create(builder => builder.AddConsole())
};

When you’re ready to create a Polly pipeline, you just have to invoke the ConfigureTelemetry extension method to pass in the telemetry options you defined.

builder.Services.AddResiliencePipeline("DefaultPipeline", static builder =>
{
    var telemetryOptions = new TelemetryOptions
    {
        LoggerFactory = LoggerFactory.Create(builder => builder.AddConsole())
    };

    builder
        .ConfigureTelemetry(telemetryOptions);
});

Now we have a resilience pipeline that will log telemetry events to the console.

As an example, let’s add a retry strategy and a timeout strategy to our pipeline. If a call takes longer than 2 seconds, it will time out. If a call times out, it will be retried.

builder.Services.AddResiliencePipeline("DefaultPipeline", static builder =>
{
    var telemetryOptions = new TelemetryOptions
    {
        LoggerFactory = LoggerFactory.Create(builder => builder.AddConsole())
    };

    builder
        .AddRetry(new RetryStrategyOptions
        {
            MaxRetryAttempts = 2,
            ShouldHandle = new PredicateBuilder().Handle<TimeoutRejectedException>()
        })
        .AddTimeout(TimeSpan.FromSeconds(2))
        .ConfigureTelemetry(telemetryOptions);
});

The TelemetryOptions class allows you to configure logging, listeners, and metering enrichers. One of the first telemetry options to consider is setting up the SeverityProvider. Each event can be assigned a severity level that will be applied to logging. For example, you might not want an error logged when an HTTP client needs to retry a call—you may only want to see it as a warning. A switch statement can be used to configure the logging severity based on the event name.

var telemetryOptions = new TelemetryOptions
{
    LoggerFactory = LoggerFactory.Create(builder => builder.AddConsole()),
    SeverityProvider = args => args.Event.EventName switch
    {
        "OnRetry" => ResilienceEventSeverity.Warning,
        "ExecutionAttempt" => ResilienceEventSeverity.Debug,
        _ => args.Event.Severity
    }
};

Here’s a list of those event names for the built-in strategies that come with Polly. We’ll cover those strategies in the next article!

Event Name Strategy Description
OnRetry Retry Fired when a retry attempt is about to occur after a failure
OnFallback Fallback Fired when the fallback action is executed after all retries/attempts have failed
OnHedging Hedging Fired when a hedged (parallel) request is initiated
OnTimeout Timeout Fired when an operation exceeds the configured timeout duration
OnCircuitClosed Circuit Breaker Fired when the circuit breaker transitions from Open or HalfOpen to Closed state
OnCircuitOpened Circuit Breaker Fired when the circuit breaker opens due to too many failures
OnCircuitHalfOpened Circuit Breaker Fired when the circuit breaker transitions to HalfOpen state to test if the service has recovered
OnRateLimiterRejected Rate Limiter Fired when a request is rejected because the rate limit has been exceeded

Metrics

Metrics are the observable data that Polly can emit about your application. You can see those metrics in a monitoring dashboard like Prometheus, Grafana, or Application Insights. Those metrics include instruments, which define what to measure, along with the aggregated values, and associated attributes(tags).

Adding the OpenTelemetry nuget packages will allow you to collect and export Polly’s metrics.

  • OpenTelemetry.Extensions.Hosting
  • OpenTelemetry.Exporter.Console (or another exporter)

In the example, I’m sending them to the console. The key is the .AddMeter(“Polly”) method that tells OpenTelemetry to subscribe to all the metrics emitted by Polly’s meter.

// Configure OpenTelemetry Metrics
builder.Services.AddOpenTelemetry()
    .WithMetrics(static metrics =>
    {
        metrics
            .AddMeter("Polly")  // Subscribe to Polly's meter
            .AddConsoleExporter();  // Export to console
    });

Example Console Output

Metric Name: resilience.polly.pipeline.duration, Description: The execution duration of resilience pipelines., Unit: ms, Metric Type: Histogram
Instrumentation scope (Meter):
        Name: Polly
        Version: 1.0
(2026-01-28T01:17:56.4900639Z, 2026-01-28T01:18:45.3017960Z) event.name: PipelineExecuted event.severity: Information pipeline.name: DefaultPipeline
Value: Sum: 455.3441 Count: 2 Min: 74.2997 Max: 381.0444
(-Infinity,0]:0
[0,5]:0
[5,10]:0
[10,25]:0
[25,50]:0
[50,75]:1
[75,100]:0
[100,250]:0
[250,500]:1
[500,750]:0
[750,1000]:0
[1000,2500]:0
[2500,5000]:0
[5000,7500]:0
[7500,10000]:0
[10000,+Infinity]:0

Resource associated with Metrics:
        telemetry.sdk.name: opentelemetry
        telemetry.sdk.language: dotnet
        telemetry.sdk.version: 1.15.0
        service.name: unknown_service:PollyDemoWebApi

In the context of our example, Polly has defined several instruments that would be useful in monitoring. For example, “resilience.polly.strategy.attempt.duration” is a histogram that will keep track of how long an attempt in the retry strategy took. We may also want to add some data about the retry event, like the attempt number, or simply specify a specific name for the retry event. To set the name of the retry event, you can simply set the name property in the RetryStrategyOptions.

builder
    .AddRetry(new RetryStrategyOptions
    {
        Name = "DefaultRetryStrategy",
        MaxRetryAttempts = 2,
        ShouldHandle = new PredicateBuilder().Handle<TimeoutRejectedException>()
    })
    .ConfigureTelemetry(telemetryOptions)
    .AddTimeout(TimeSpan.FromSeconds(2));

To add some data about an event, we need to create a MeteringEnricher and override the Enrich method. This allows us to see what type of arguments are being passed into the method and decide if we can add a tag with the piece of data we want to be added.

internal sealed class RetryMeteringEnricher : MeteringEnricher
{
    public override void Enrich<TResult, TArgs>(in EnrichmentContext<TResult, TArgs> context)
    {
        if (context.TelemetryEvent.Arguments is OnRetryArguments<TResult> retryArgs)
        {
            context.Tags.Add(new("retry.attempt", retryArgs.AttemptNumber));
        }
    }
}

All that is left to do is to add the metering enricher to the telemetry options.

var telemetryOptions = new TelemetryOptions
{
    LoggerFactory = LoggerFactory.Create(builder => builder.AddConsole()),
    SeverityProvider = args => args.Event.EventName switch
    {
        "OnRetry" => ResilienceEventSeverity.Warning,
        "ExecutionAttempt" => ResilienceEventSeverity.Debug,
        _ => args.Event.Severity
    }
};

telemetryOptions.MeteringEnrichers.Add(new RetryMeteringEnricher());

Now when the telemetry data is logged, you can see the new retry.attempt tag was added to the message.

2026-01-28T02:19:38.6749053Z, 2026-01-28T02:27:58.02146677Z
event.name: OnRetry 
event.severity: Warning 
exception.type: System.Net.Http.HttpRequestException 
pipeline.name: DefaultPipeline 
retry.attempt: 1 
strategy.name: DefaultRetryStrategy 
Value: 3

Summary

With telemetry configured, you now have logs showing when resilience strategies activate, metrics tracking performance and failure rates, and custom enrichment adding context-specific data. This visibility is essential for tuning your resilience strategies and understanding how your application responds to service disruptions.

There is tremendous flexibility built into OpenTelemetry and Polly that can help you see and understand how your application reacts to external services. If you would like to learn more about OpenTelemetry, check out this video about Visualizing Performance with Grafana and .NET Aspire.

Resources


Copyright © 2026 NimblePros - All Rights Reserved