如何使用 ILoggerFactory 记录 Polly 的重试

Ter*_*rje 10 c# logging dependency-injection polly asp.net-core-2.1

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

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.");
    });
Run Code Online (Sandbox Code Playgroud)

在我的代码中,我使用IHttpClientFactorydotnet core 2.1 中的新模式,并将其添加到我的 Startup.csConfigureServices方法中:

    services.AddHttpClient<IMySuperHttpClient, MySuperHttpClient>()
        .AddPolicyHandler(MySuperHttpClient.GetRetryPolicy())
        .AddPolicyHandler(MySuperHttpClient.GetCircuitBreakerPolicy());
Run Code Online (Sandbox Code Playgroud)

静态且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);
}
Run Code Online (Sandbox Code Playgroud)

其中OnRetry方法也必须是静态的:

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

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

Pet*_*ala 6

正如山地旅行者的评论中所述,有多种方法可以解决该问题:

  • 使用 a在和delegateContext之间传递任意对象Execute(Async)onRetry(Async)
  • 使用不同的重载AddPolicyHandler

前一种解决方案需要显式调用Execute/ExecuteAsync才能提供Context封装ILogger实例的对象。对于命名/类型化 Http 客户端,此路由不是一个可行的选择,因为您HttpClientPolicyHttpMessageHandler. Execute(Async)因此,代表您调用 Handler 的不是您的代码。

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

services.AddHttpClient<IMySuperHttpClient, MySuperHttpClient>()
        .AddPolicyHandler((provider, _) => MySuperHttpClient.GetRetryPolicy(provider))
Run Code Online (Sandbox Code Playgroud)

因此,您需要做的就是将其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));
}
Run Code Online (Sandbox Code Playgroud)

代表期望的WaitAndRetryAsync地方不存在这种过载。我们需要引入一个像这样的辅助方法:onRetryIServiceProvider

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.");
}
Run Code Online (Sandbox Code Playgroud)
  • 第一个OnRetry接收IServiceProvider并返回一个Action<DelegateResult<HttpResponseMessage>, TimeSpan>
    • 返回的方法是一个很好的WaitAndRetryAsync委托onRetry
  • 第二个OnRetry与你的相同,我刚刚将你的上下文参数替换为提供程序

因此,基本上第一个充当和第二个OnRetry之间的适配器以通过.WaitAndRetryAsyncOnRetryprovider