Authoring a reactive Polly policy (Custom policies Part III)
In this article we'll build our first reactive custom Polly policy: a policy to log exceptions or fault-results.
Polly polices fall into two categories: reactive (which react to configured faults) and non-reactive / proactive (which act on all executions). To author a proactive policy, see Part II: Authoring a proactive custom policy.
For background on custom policies and the Polly.Contrib, see Part I: Introducing custom Polly policies.
If you've already read Part II of this article series, the initial steps to create the reactive policy here are similar to those for the proactive policy covered in Part II. For what's different, skip straight to Steps 3 onwards, where we make the implementation react to the faults the policy handles, and tie the policy to Polly's existing
Policy.Handle<>(...) configuration syntax.
The goal: a policy to log faults
Polly already has configurable delegates hooks such as
onFallback, which many users use for logging with the relevant policies. So why a new policy to log faults? And what exactly will the policy do?
The aim is that the policy will log any exception or fault and then just rethrow or bubble it outwards.
It also makes for a good blog post example :~)
Authoring a reactive custom policy: Log exceptions and fault-results
For simplicity, we'll assume we're developing a custom policy for use with Polly and HttpClientFactory. This means that all executions are async and return
For this, we only need to implement the
IAsyncPolicy<TResult> form of the policy. For other forms, see Part IV of the series.
Step 1: Name your policy and extend the base class
The base class for async generic policies is, as you might expect,
AsyncPolicy<TResult>. So start by declaring:
By extending the base class, your policy is already plugged in to all the benefits of the existing Polly architecture: the execution-dispatch, the integration into
The compiler or IDE will report:
AsyncLoggingPolicy<TResult> does not implement inherited abstract member AsyncPolicy<TResult> .ImplementationAsync(Func<Context, CancellationToken, Task<TResult>>, Context, CancellationToken, bool)
Step 2: Add a 'no-op' implementation
Let's fulfil the abstract method with a stub (your IDE may have a tooltip or shortcut to help):
This method is where your policy defines its implementation!
Let's look at the parameters - these are the information passed when the user executes a call through the policy:
Func<Context, CancellationToken, Task<TResult>> actionrepresents the delegate executed through the policy
Context contextis the context passed to execution
CancellationToken cancellationTokenis (you guessed it) the cancellation token passed to execution
bool continueOnCapturedContext, whether the user asked to continue after
awaits on a captured context.
The parameters map directly to the calling code:
So, let's flesh out our policy to provide a basic 'no-op' implementation. This just executes the code passed to the policy 'as is', using the parameters the user passed to execute the
action. The parameter
continueOnCapturedContext controls continuation context after the
That's it for a no-op implementation!
Aside: To make it easier for you to get started, Polly provides all this as a template starting point for a custom policy. To author your own custom policy, you can grab the template repo and copy/paste/rename the classes.
Step 3: Matching the faults the user configured
aka: Do what the user told you!
The first thing any reactive policy needs to do is honor the exceptions and results which the user configured the policy to handle. Remember, the user specifies this with syntax such as:
Polly wraps that configuration up for you in two properties which are available on the configured policy instance:
ExceptionPredicates- any exceptions the user configured the policy to handle
ResultPredicates- any results the user configured the policy to handle
These are predicates, so essentially the implementation wants to do something like
ExceptionPredicates.Any(predicate => predicate(exception)). The below skeleton for our reactive policy implementation shows the actual syntax:
Let's dive in:
At  we check whether a returned result matches any of our
If an exception is thrown, we check for matches at . What's with the syntax there? Why is
handledException different from
Well, certain call chains can result in messy
InnerExceptions, and writing predicates to match those
InnerExceptions (including if nested) can be messy. So Polly provides in-built
.HandleInner<TException>() syntax to help. This line of code matches those exceptions, whether the user has configured
.HandleInner<>() or straight
.Handle<>() or a mixture:
If Polly plucks a matching
InnerException out of a messy
handledException will be it. If Polly matches a plain non-inner exception, again,
handledException will be it.
Step 4: Add your custom functionality
aka: You hit the jackpot! Do your stuff ... then let's get outta here!
If your code hits  or , the policy has matched a result or exception it was configured to handle - code your custom logic for your policy here!
Finally, your custom policy has to decide how to exit, at  and .
To be clear, the policy could do anything you choose, as its exit - substitute a different result (as Fallback policy does); rethrow a different exception; or just bubble the result or exception it was handling.
And also, you can build any structure you like within
ImplementationAsync(...) - it doesn't have exactly to follow the form above. Polly's Retry policy, for example, as you can guess, builds a loop.
In the logging policy, after we've logged, we'll exit by just returning the matched result or rethrowing the matched exception.
Here's a (near-final) implementation:
At [c], the policy returns the result it just handled.
The lines marked [d] show some special syntax for rethrowing an exception preserving the original call stack, when Polly's predicates have worked their magic to match an
InnerException (as described earlier).
What about the lines marked [a] and [b], where we do the actual logging?
We opted to let the user provide [a] a
Func<Context, ILogger> to specify the logger. Why? Well, policies are typically (particularly with HttpClientFactory) configured in the
StartUp file. On the other hand, loggers in .NET Core local to the particular component are usually only resolved at the call site by DI - for instance, a Controller class might take an
ILogger<FooController> by constructor injection.
Func<Context, ILogger> loggerProvider allows the user to bridge this, by providing a
Func to select the logger at execution time.
Polly.Context is an execution-scoped context which travels with every Polly execution, and has dictionary-like semantics - users can attach any information they like to it, to pass into the execution. The worked example at the end of this blog post shows how we'll pass the local
ILogger instance in to the call in practice; and the Polly wiki covers the general technique here.
Note that the complexity here around obtaining an instance of
ILogger is a factor of the split between early-binding to create the policy on HttpClientFactory and .NET Core's preference for category-specific
ILogger<TCategory> resolved in a late-binding manner by DI, usually by constructor injection into the relevant component. This complexity is a feature of these dimensions of logging in .NET Core rather than Polly per se. An alternative, simpler approach would replace [a] with a simple
ILogger, and use the overloads described here to configure the policy with a fixed
ILogger<TFixed> at startup.
To log to the logger, we chose [b] an
Action<ILogger, Context, DelegateResult<TResult>>. You'll be getting the picture here: It's good practice to make
Context an input parameter to any delegate you let users configure on a policy. We can let the user attach some custom data to
Context which they want to log (or in your own custom policy, use
Context information to influence policy operation some other way). And, in-built,
Context carries metadata about the Polly execution which can be useful for logging - which Policy is in use, which PolicyWrap, and a CorrelationId for the execution.
DelegateResult<TResult> is a discriminated union representing either:
DelegateResult<TResult>.Exception- the exception the underlying execution threw (or null, if not)
DelegateResult<TResult>.Result- the result the underlying execution returned.
Step 5: Add configuration syntax
Finally, add some configuration syntax so that users can create instances of your policy!
The policy constructor will need to take the parameters:
However, for a reactive policy, users will configure the policy with syntax something like:
Or with HttpClientFactory:
How do we make
.AsyncLog(...) follow on from the various
.Or...() clauses? The answer is that handle clauses such as:
return an instance of a special builder type,
PolicyBuilder<TResult>, which encapsulates the choices the user has made about exceptions and results to handle. So the
.AsyncLog(...) overload must be coded as an extension method on this class. Shown combined with the constructor, the pattern looks like this:
policyBuilder instance [i] should be passed down to Polly's
AsyncPolicy<TResult> base class constructor (as shown at [ii]), so that the
ResultPredicates properties can be configured on the policy. These also support Polly's
Let's take it for a spin!
Let's take the new
AsyncLoggingPolicy<TResult> for a spin!
The below example includes a number of classes which feature in the Github repo for this policy:
Program.cs: example program configuring an
ContextExtensions.cs: helper methods on the
Polly.Contextclass, to make selection of
ILoggerat runtime easier
FooClient.cs: a typed client configured by
StubErroringDelegatingHandler.cs: a delegating handler also configured by
FooClient, to randomly generate errors - just to give the
LoggingPolicy<T>some errors for us to log in this demonstration!
Here's some example output:
info: System.Net.Http.HttpClient.FooClient.LogicalHandler Start processing HTTP request GET https://www.google.com/ info: ConsoleAppExampleForBlogPost.FooClient https://www.google.com: InternalServerError info: ConsoleAppExampleForBlogPost.FooClient https://www.google.com: Exception of type 'System.Net.Http.HttpRequestException' was thrown. info: ConsoleAppExampleForBlogPost.FooClient https://www.google.com: InternalServerError info: ConsoleAppExampleForBlogPost.FooClient https://www.google.com: Exception of type 'System.Net.Http.HttpRequestException' was thrown. info: ConsoleAppExampleForBlogPost.FooClient https://www.google.com: Exception of type 'System.Net.Http.HttpRequestException' was thrown. info: System.Net.Http.HttpClient.FooClient.LogicalHandler End processing HTTP request after 47.3552ms - OK Got result: Dummy content from the stub helper class.
Note that this output includes some default logging from
HttpClient as well as that provided by
Where can I get the code?
The full source code for
AsyncLoggingPolicy<TResult> and the test console app is available in this Github repo.
Grab the template
If you want to dive straight in to experimenting with a custom policy, just grab the template repo and start copy/paste/rename/extend-ing the classes.
Authoring a proactive custom policy
If you haven't already looked at it, Part II covers authoring a proactive custom policy. The example chosen captures execution timing for any execution through the policy.
Understanding other forms of Polly policy
Part IV looks at how to author other forms of policy - synchronous policies and non-generic policies - in the most efficient manner.