在附加到改装客户端的 Polly 策略中使用 ILogger
Using an ILogger in a Polly Policy attached to a Refit Client
我一直在尝试按照 this blog post 的说明将 ILogger 传递给我的重试策略,以便记录有关重试错误的信息。
博客中的代码不是开箱即用的,因为我们使用 Refit 生成客户端。基于 refit docs,它应该只是在我的方法签名中添加一个 属性 的问题,但实际上 还不能让它工作。
即使我已将 属性 添加到我的方法签名中:
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 issues,有一个错字,属性 是 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);
我一直在尝试按照 this blog post 的说明将 ILogger 传递给我的重试策略,以便记录有关重试错误的信息。
博客中的代码不是开箱即用的,因为我们使用 Refit 生成客户端。基于 refit docs,它应该只是在我的方法签名中添加一个 属性 的问题,但实际上 还不能让它工作。
即使我已将 属性 添加到我的方法签名中:
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 issues,有一个错字,属性 是 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);