Polly 记录所有带有 URL、Headers、内容和响应的请求
Polly log all requests with URL, Headers, Content and Response
我有一个项目调用了其他项目的许多 rest API,我遇到了一些问题,不仅要识别来自这些 API 的错误,还要识别正确的响应,但信息在另一个系统上不正确。
我做了这部分,但它只记录重试,我还需要记录成功。
services.AddHttpClient<IClient, Client>("AuthClient", x =>
{
x.BaseAddress = new Uri(urlAn);
}).AddPolicyHandler((services, request) =>
HttpPolicyExtensions.HandleTransientHttpError().WaitAndRetryAsync(
new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
},
onRetry: (outcome, timespan, retryAttempt, context) =>
{
services.GetService<ILogger>()
.LogWarning("Delaying for {delay}ms, then making retry {retry}.", timespan.TotalMilliseconds, retryAttempt);
}));
onRetry
方法仅在出现错误时执行,由策略处理。
HandleTransientHttpError
触发政策
- 当有
HttpRequestException
- 或者当响应代码是 408 或 5xxx 时。
要注入在每种情况下都应执行的逻辑,您需要使用自定义 DelegatingHandler
。这个扩展点让您可以将自定义代码注入 HttpClient 的管道 (1)。
这是 LoggerHandler
:
的简单实现
class LoggerHandler: DelegatingHandler
{
private readonly ILogger<LoggerHandler> _logger;
public LoggerHandler(ILogger<LoggerHandler> logger)
{
_logger = logger;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
var response = await base.SendAsync(request, cancellationToken);
_logger.LogInformation(response.StatusCode.ToString());
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "Request has failed after several retries");
throw;
}
}
}
- 如您所见,我们已将记录器注入到处理程序中
- 如果下游请求完美无缺,我们会在信息级别记录一些事实
- 如果下游请求错误,我们会在错误级别记录异常
现在,让我们连接所有的东西:
var retryPolicy = HttpPolicyExtensions.HandleTransientHttpError().WaitAndRetryAsync(
new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
});
services.AddHttpClient<IClient, Client>("AuthClient", x => { x.BaseAddress = new Uri(urlAn); })
.AddPolicyHandler(retryPolicy)
.AddHttpMessageHandler<LoggerHandler>();
请注意报名顺序事项
- 请查看此 SO topic 了解更多详情。
还有几个小问题可以改进:
- 您不必为
HttpClient
指定名称,因为您使用的是 Typed-Client。
services.AddHttpClient<IClient, Client>(x => ...)
- 我强烈建议使用比
IClient
和 Client
更好的命名。想象一种情况,您需要向您的应用程序再添加一个客户端。怎么命名呢? AuthClient
可能是一个更好的名字:
services.AddHttpClient<IAuthClient, AuthClient>(x => ...)
- 我还鼓励您使用 jitter 为您的重试睡眠持续时间添加随机性。如果所有客户端都尝试对过载的服务器执行重试,那么它对下游没有帮助。
- 尝试分配有抖动的重试。
- 我还建议阅读这篇关于重试、超时和 DelegatingHandler 的 article。
我有一个项目调用了其他项目的许多 rest API,我遇到了一些问题,不仅要识别来自这些 API 的错误,还要识别正确的响应,但信息在另一个系统上不正确。 我做了这部分,但它只记录重试,我还需要记录成功。
services.AddHttpClient<IClient, Client>("AuthClient", x =>
{
x.BaseAddress = new Uri(urlAn);
}).AddPolicyHandler((services, request) =>
HttpPolicyExtensions.HandleTransientHttpError().WaitAndRetryAsync(
new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
},
onRetry: (outcome, timespan, retryAttempt, context) =>
{
services.GetService<ILogger>()
.LogWarning("Delaying for {delay}ms, then making retry {retry}.", timespan.TotalMilliseconds, retryAttempt);
}));
onRetry
方法仅在出现错误时执行,由策略处理。
HandleTransientHttpError
触发政策
- 当有
HttpRequestException
- 或者当响应代码是 408 或 5xxx 时。
要注入在每种情况下都应执行的逻辑,您需要使用自定义 DelegatingHandler
。这个扩展点让您可以将自定义代码注入 HttpClient 的管道 (1)。
这是 LoggerHandler
:
class LoggerHandler: DelegatingHandler
{
private readonly ILogger<LoggerHandler> _logger;
public LoggerHandler(ILogger<LoggerHandler> logger)
{
_logger = logger;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
var response = await base.SendAsync(request, cancellationToken);
_logger.LogInformation(response.StatusCode.ToString());
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "Request has failed after several retries");
throw;
}
}
}
- 如您所见,我们已将记录器注入到处理程序中
- 如果下游请求完美无缺,我们会在信息级别记录一些事实
- 如果下游请求错误,我们会在错误级别记录异常
现在,让我们连接所有的东西:
var retryPolicy = HttpPolicyExtensions.HandleTransientHttpError().WaitAndRetryAsync(
new[]
{
TimeSpan.FromSeconds(1),
TimeSpan.FromSeconds(5),
TimeSpan.FromSeconds(10)
});
services.AddHttpClient<IClient, Client>("AuthClient", x => { x.BaseAddress = new Uri(urlAn); })
.AddPolicyHandler(retryPolicy)
.AddHttpMessageHandler<LoggerHandler>();
请注意报名顺序事项
- 请查看此 SO topic 了解更多详情。
还有几个小问题可以改进:
- 您不必为
HttpClient
指定名称,因为您使用的是 Typed-Client。services.AddHttpClient<IClient, Client>(x => ...)
- 我强烈建议使用比
IClient
和Client
更好的命名。想象一种情况,您需要向您的应用程序再添加一个客户端。怎么命名呢?AuthClient
可能是一个更好的名字:services.AddHttpClient<IAuthClient, AuthClient>(x => ...)
- 我还鼓励您使用 jitter 为您的重试睡眠持续时间添加随机性。如果所有客户端都尝试对过载的服务器执行重试,那么它对下游没有帮助。
- 尝试分配有抖动的重试。
- 我还建议阅读这篇关于重试、超时和 DelegatingHandler 的 article。