在附加到改装客户端的 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);