如何使用ILoggerFactory记录Polly的重试日志

10

或:如何从静态方法记录日志。

来自https://github.com/App-vNext/Polly,你可以看到像这样的示例,其中日志记录器是可用的:

Policy
  .Timeout(30, onTimeout: (context, timespan, task) => 
    {
        logger.Warn($"{context.PolicyKey} at {context.ExecutionKey}: execution timed out after {timespan.TotalSeconds} seconds.");
    });

在我的代码中,我正在使用dotnet core 2.1中的新IHttpClientFactory模式,并在我的Startup.cs ConfigureServices方法中像这样添加它:
    services.AddHttpClient<IMySuperHttpClient, MySuperHttpClient>()
        .AddPolicyHandler(MySuperHttpClient.GetRetryPolicy())
        .AddPolicyHandler(MySuperHttpClient.GetCircuitBreakerPolicy());

由于GetRetryPolicy是静态的并且看起来像这样:

internal static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
    return HttpPolicyExtensions
        .HandleTransientHttpError()
        .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
        .WaitAndRetryAsync(
            retryCount: 4,
            sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
            onRetry: OnRetry);
}

OnRetry 方法也必须是静态的:

private static void OnRetry(DelegateResult<HttpResponseMessage> delegateResult, TimeSpan timespan, Context context)
{
    // var logger = ??
    // logger.LogWarning($"API call failed blah blah.");
}

如果可能的话,你如何在这里访问ILoggerFactory


2
您可以使用 Polly.Context 将任何自定义数据传递到 Polly 策略中,包括 ILoggerILoggerFactory。Steve Gordon 的这篇博客文章演示了如何通过 IHttpClientFactory 配置策略时传递 ILogger。同样的原则也适用于 ILoggerFactory - mountain traveller
4
Polly的HttpClientFactory文档现在增加了另一个示例,展示如何在由HttpClientFactory配置的类型化客户端上使用ILogger<T>策略。链接 - mountain traveller
1个回答

6

正如 mountain traveller 在评论中所述,有多种解决该问题的方法:

  • 使用 ContextExecute(Async)onRetry(Async) 委托之间传递任意对象。
  • 使用不同的 AddPolicyHandler 重载。

前一种解决方案需要显式调用 Execute / ExecuteAsync 来提供封装了 ILogger 实例的 Context 对象。对于命名/类型化 Http 客户端,这条路不是可行的选择,因为您会将整个 HttpClient 装饰成一个PolicyHttpMessageHandler。因此,调用 Execute(Async) 的不再是您的代码,而是代表您的处理程序。

后一种解决方案提供了访问 IServiceProvider 的方式,从中您可以检索任何已注册的服务。(还将请求作为参数传递。

services.AddHttpClient<IMySuperHttpClient, MySuperHttpClient>()
        .AddPolicyHandler((provider, _) => MySuperHttpClient.GetRetryPolicy(provider))

因此,您所需做的就是将IServiceProvider传递到您的OnRetry方法中。
internal static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy(IServiceProvider provider)
=> HttpPolicyExtensions
        .HandleTransientHttpError()
        .OrResult(msg => msg.StatusCode == HttpStatusCode.NotFound)
        .WaitAndRetryAsync(4,
            retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
            OnRetry(provider));
}

目前没有重载 WaitAndRetryAsync,使 onRetry 委托可以接受 IServiceProvider。 我们需要引入一个类似这样的帮助方法:

private static Action<DelegateResult<HttpResponseMessage>, TimeSpan> OnRetry(IServiceProvider provider)
    => (dr, ts) => OnRetry(dr, ts, provider);

private static void OnRetry(DelegateResult<HttpResponseMessage> delegateResult, TimeSpan timespan, IServiceProvider provider)
{
    // var logger = ??
    // logger.LogWarning($"API call failed blah blah.");
}
  • 第一个OnRetry接收一个IServiceProvider参数,并返回一个Action<DelegateResult<HttpResponseMessage>, TimeSpan>
    • 这个返回的方法是 WaitAndRetryAsynconRetry 委托很好的代理。
  • 第二个OnRetry与您的一样,只是将您的上下文参数替换为提供程序。

因此,基本上第一个OnRetry充当 WaitAndRetryAsync 和第二个OnRetry 之间传递 provider 的适配器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接