diff --git a/src/Polly.Core/README.md b/src/Polly.Core/README.md index 179a33918a8..97980f62d4d 100644 --- a/src/Polly.Core/README.md +++ b/src/Polly.Core/README.md @@ -244,3 +244,10 @@ new ResilienceStrategyBuilder() ## Registering Custom Callbacks When setting the delegates, ensure to respect the `ResilienceContext.IsSynchronous` property's value and execute your delegates synchronously for synchronous executions. In addition, use the `ResilienceContext.ContinueOnCapturedContext` property when your user code uses execution with synchronization context (for example, asynchronous calls in UI applications, such as in Windows Forms or WPF applications). + +## Telemetry + +Each individual resilience strategy can emit telemetry by using the [`ResilienceStrategyTelemetry`](Telemetry/ResilienceStrategyTelemetry.cs) API. Polly wraps the arguments as [`TelemetryEventArguments`](Telemetry/TelemetryEventArguments.cs) and emits them using `DiagnosticSource`. +To consume the telemetry, Polly adopters needs to assign an instance of `DiagnosticSource` to `ResilienceStrategyBuilder.DiagnosticSource` and consume `TelemetryEventArguments`. + +For common use-cases, it is anticipated that Polly users would leverage `Polly.Extensions`. This allows all of the aforementioned functionalities by invoking the `ResilienceStrategyBuilder.ConfigureTelemetry(...)` extension method. `ConfigureTelemetry` processes `TelemetryEventArguments` and generates logs and metrics from it. diff --git a/src/Polly.Extensions/README.md b/src/Polly.Extensions/README.md index a4eb701cf61..c080ec51675 100644 --- a/src/Polly.Extensions/README.md +++ b/src/Polly.Extensions/README.md @@ -1,22 +1,18 @@ -# About Polly.Hosting +# Polly.Extensions Overview -The `Polly.Hosting` enables the following features: +`Polly.Extensions` provides a set of features that streamline the integration of Polly with the standard `IServiceCollection` Dependency Injection (DI) container. It further enhances telemetry by exposing a `ConfigureTelemetry` extension method that enables [logging](https://learn.microsoft.com/dotnet/core/extensions/logging?tabs=command-line) and [metering](https://learn.microsoft.com/dotnet/core/diagnostics/metrics) for all strategies created via DI extension points. Note that telemetry is enabled by default when utilizing the `AddResilienceStrategy` extension method. - -- Integrates Polly with the standard `IServiceCollection` Dependency Injection (DI) container. -- Implements `ResilienceTelemetryFactory` that adds [logging](https://learn.microsoft.com/dotnet/core/extensions/logging?tabs=command-line) and [metering](https://learn.microsoft.com/dotnet/core/diagnostics/metrics) for all strategies created using the DI extension points. - -Example: +Below is an example illustrating these capabilities: ``` csharp var services = new ServiceCollection(); -// Define your strategy +// Define a strategy services.AddResilienceStrategy( "my-key", - context => context.Builder.AddTimeout(TimeSpan.FromSeconds(10))); + context => context.Builder.AddTimeout(TimeSpan.FromSeconds(10))); -// Define your strategy using custom options +// Define a strategy with custom options services.AddResilienceStrategy( "my-timeout", context => @@ -25,9 +21,136 @@ services.AddResilienceStrategy( context.Builder.AddTimeout(myOptions.Timeout); }); -// Use your strategy +// Utilize the strategy var serviceProvider = services.BuildServiceProvider(); var strategyProvider = serviceProvider.GetRequiredService>(); var resilienceStrategy = strategyProvider.Get("my-key"); ``` +## Telemetry Features + +Upon invoking the `ConfigureTelemetry` extension method, Polly begins to emit logs and metrics. Here's an example: + +``` csharp +var telemetryOptions = new TelemetryOptions(); + +// Configure logging +telemetryOptions.LoggerFactory = LoggerFactory.Create(builder => builder.AddConsole()); + +// Configure enrichers +telemetryOptions.Enrichers.Add(context => +{ + context.Tags.Add(new("my-custom-tag", "custom-value")); +}); + +// Manually handle the event +telemetryOptions.OnTelemetryEvent = args => +{ + Console.WriteLine($"Telemetry event occurred: {args.Event.EventName}"); +}); + +var builder = new ResilienceStrategyBuilder() + .AddTimeout(TimeSpan.FromSeconds(1)) + .ConfigureTelemetry(telemetryOptions) // This method enables telemetry in the builder + .Build(); +``` + +Alternatively, you can use the `AddResilienceStrategy` extension which automatically adds telemetry: + +``` csharp +var serviceCollection = new ServiceCollection() + .AddLogging(builder => builder.AddConsole()) + .AddResilienceStrategy("my-strategy", builder => builder.AddTimeout(TimeSpan.FromSeconds(1))) + // Configure the default settings for TelemetryOptions + .Configure(options => + { + // Configure enrichers + options.Enrichers.Add(context => context.Tags.Add(new("my-custom-tag", "custom-value"))); + + // Manually handle the event + options.OnTelemetryEvent = args => + { + Console.WriteLine($"Telemetry event occurred: {args.Event.EventName}"); + }; + }); +``` + +### Emitted Metrics + +The emitted metrics are emitted under the `Polly` meter name. The subsequent sections provide insights into the metrics produced by Polly. Please note that any custom enriched dimensions are not depicted in the following tables. + +#### resilience-events + +- Type: *Counter* +- Description: Emitted upon the occurrence of a resilience event. + +Dimensions: + +|Name|Description| +|---| ---| +|`event-name`| The name of the emitted event.| +|`event-severity`| The severity of the event (`Debug`, `Information`, `Warning`, `Error`, `Critical`).| +|`builder-name`| The name of the builder corresponding to the resilience strategy.| +|`builder-instance`| The instance name of the builder corresponding to the resilience strategy.| +|`strategy-name`| The name of the strategy generating this event.| +|`strategy-type`| The type of the strategy generating this event.| +|`operation-key`| The operation key associated with the call site. | +|`result-type`| The result type (`string`, `HttpResponseMessage`). | +|`exception-name`| The full name of the exception assigned to the execution result (`System.InvalidOperationException`). | + +#### execution-attempt-duration + +- Type: *Histogram* +- Unit: *milliseconds* +- Description: Tracks the duration of execution attempts, produced by `Retry` and `Hedging` resilience strategies. + +Dimensions: + +|Name|Description| +|---| ---| +|`event-name`| The name of the emitted event.| +|`event-severity`| The severity of the event (`Debug`, `Information`, `Warning`, `Error`, `Critical`).| +|`builder-name`| The name of the builder corresponding to the resilience strategy.| +|`builder-instance`| The instance name of the builder corresponding to the resilience strategy.| +|`strategy-name`| The name of the strategy generating this event.| +|`strategy-type`| The type of the strategy generating this event.| +|`operation-key`| The operation key associated with the call site. | +|`result-type`| The result type (`string`, `HttpResponseMessage`). | +|`exception-name`| The full name of the exception assigned to the execution result (`System.InvalidOperationException`). | +|`attempt-number`| The execution attempt number, starting at 0 (0, 1, 2). | +|`attempt-handled`| Indicates if the execution outcome was handled. A handled outcome indicates execution failure and the need for retry (`true`, `false`). | + +#### strategy-execution-duration + +- Type: *Histogram* +- Unit: *milliseconds* +- Description: Measures the duration and results of resilience strategy executions. + +Dimensions: + +|Name|Description| +|---| ---| +|`builder-name`| The name of the builder corresponding to the resilience strategy.| +|`builder-instance`| The instance name of the builder corresponding to the resilience strategy.| +|`operation-key`| The operation key associated with the call site. | +|`result-type`| The result type (`string`, `HttpResponseMessage`). | +|`exception-name`| The full name of the exception assigned to the execution result (`System.InvalidOperationException`). | +|`execution-health`| Indicates whether the execution was healthy or not (`Healthy`, `Unhealthy`). | + +### Logs + +Logs are registered under the `Polly` logger name. Here are some examples of the logs: + +``` text +// This log is recorded whenever a resilience event occurs. EventId = 0 +Resilience event occurred. EventName: '{EventName}', Source: '{BuilderName}[{BuilderInstance}]/{StrategyType}[{StrategyName}]', Operation Key: '{OperationKey}', Result: '{Result}' + +// This log is recorded when a resilience strategy begins executing. EventId = 1 +Resilience strategy executing. Source: '{BuilderName}[{BuilderInstance}]', Operation Key: '{OperationKey}', Result Type: '{ResultType}' + +// This log is recorded when a resilience strategy finishes execution. EventId = 2 +Resilience strategy executed. Source: '{BuilderName}[{BuilderInstance}]', Operation Key: '{OperationKey}', Result Type: '{ResultType}', Result: '{Result}', Execution Health: '{ExecutionHealth}', Execution Time: {ExecutionTime}ms + +// This log is recorded upon the completion of every execution attempt. EventId = 3 +Execution attempt. Source: '{BuilderName}[{BuilderInstance}]/{StrategyType}[{StrategyName}]', Operation Key: '{OperationKey}', Result: '{Result}', Handled: '{Handled}', Attempt: '{Attempt}', Execution Time: '{ExecutionTimeMs}' +``` diff --git a/src/Polly.Extensions/Telemetry/TelemetryResilienceStrategy.cs b/src/Polly.Extensions/Telemetry/TelemetryResilienceStrategy.cs index 733def18ffc..f871ca680c3 100644 --- a/src/Polly.Extensions/Telemetry/TelemetryResilienceStrategy.cs +++ b/src/Polly.Extensions/Telemetry/TelemetryResilienceStrategy.cs @@ -40,7 +40,7 @@ public TelemetryResilienceStrategy( ExecutionDuration = ResilienceTelemetryDiagnosticSource.Meter.CreateHistogram( "strategy-execution-duration", unit: "ms", - description: "The execution duration and execution result of resilience strategies."); + description: "The execution duration and execution results of resilience strategies."); } public Histogram ExecutionDuration { get; } diff --git a/test/Polly.Extensions.Tests/Telemetry/TelemetryResilienceStrategyTests.cs b/test/Polly.Extensions.Tests/Telemetry/TelemetryResilienceStrategyTests.cs index 953e412591d..698d6b90c23 100644 --- a/test/Polly.Extensions.Tests/Telemetry/TelemetryResilienceStrategyTests.cs +++ b/test/Polly.Extensions.Tests/Telemetry/TelemetryResilienceStrategyTests.cs @@ -27,7 +27,7 @@ public void Ctor_Ok() var duration = CreateStrategy().ExecutionDuration; duration.Unit.Should().Be("ms"); - duration.Description.Should().Be("The execution duration and execution result of resilience strategies."); + duration.Description.Should().Be("The execution duration and execution results of resilience strategies."); } [InlineData(true)]