我一直在尝试按照这篇博客文章的指示传递一个ILogger到我的重试策略,以便记录有关重试错误的信息。
博客中的代码不能开箱即用,因为我们正在使用Refit来生成客户端。根据改装文档,它应该只是一个添加属性到我的方法签名的问题,但还没有能够得到它到实际上工作。
尽管我已经在方法签名中添加了这个属性:
Task<UserSubscriptions> GetUserSubscriptions(string userId, [Property("PollyExecutionContext")] Polly.Context context);
我在扩展方法中捕获了记录器管理:
private static readonly string LoggerKey = "LoggerKey";
public static Context WithLogger(this Context context, ILogger logger)
{
context[LoggerKey] = logger;
return context;
}
public static ILogger GetLogger(this Context context)
{
if (context.TryGetValue(LoggerKey, out object logger))
{
return logger as ILogger;
}
return null;
}
我在执行方法时创建了一个新的上下文:
public Context GetPollyContext() => new Context().WithLogger(logger);
public Task<UserSubscriptions> GetUserSubscriptions(UserId userId) {
return restClient.GetUserSubscriptions(userId.UserIdString, GetPollyContext());
}
并尝试访问日志记录器作为重试操作的一部分:
return Policy
.Handle<Exception>()
.OrResult<HttpResponseMessage>(r => CodesToRetry.Contains(r.StatusCode))
.WaitAndRetryAsync(3, retryCount => TimeSpan.FromSeconds(1), (result, timeSpan, retryCount, context) =>
{
var logger = context.GetLogger();
if (logger == null) return;
// do some logging
}
});
当我在重试操作中设置断点时,我看到的上下文是一个新的空上下文,而不是我用附加的记录器创建的上下文。
每个GitHub问题,有一个错别字,属性是PolicyExecutionContext
,而不是PollyExecutionContext
。
虽然我不需要为每个请求生成唯一的上下文,但更好的模式是使用委托注入。
扩展方法
private static readonly string LoggerKey = "LoggerKey";
public static Context WithLogger(this Context context, ILogger logger)
{
context[LoggerKey] = logger;
return context;
}
public static ILogger GetLogger(this Context context)
{
if (context.TryGetValue(LoggerKey, out object logger))
{
return logger as ILogger;
}
return null;
}
委托定义
public class PollyContextInjectingDelegatingHandler<T> : DelegatingHandler
{
private readonly ILogger<T> _logger;
public PollyContextInjectingDelegatingHandler(ILogger<T> logger)
{
_logger = logger;
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
var pollyContext = new Context().WithLogger(_logger);
request.SetPolicyExecutionContext(pollyContext);
return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
}
然后将委托添加到客户端定义
services
.AddTransient<ISubscriptionApi, SubscriptionApi>()
.AddTransient<PollyContextInjectingDelegatingHandler<SubscriptionApi>>()
.AddRefitClient<ISubscriptionApiRest>(EightClientFactory.GetRefitSettings())
.ConfigureHttpClient((s, c) =>
{
...
})
.AddHttpMessageHandler<PollyContextInjectingDelegatingHandler<SubscriptionApi>>()
.ApplyTransientRetryPolicy(retryCount, timeout);