/
HttpClientBuilderExtensions.Logging.cs
147 lines (135 loc) · 8.4 KB
/
HttpClientBuilderExtensions.Logging.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using Microsoft.Extensions.Http;
using Microsoft.Extensions.Http.Logging;
namespace Microsoft.Extensions.DependencyInjection
{
public static partial class HttpClientBuilderExtensions
{
/// <summary>
/// Adds a delegate that will be used to create an additional logger for a named <see cref="System.Net.Http.HttpClient"/>. The custom logger would be invoked
/// from a dedicated logging DelegatingHandler on every request of the corresponding named <see cref="System.Net.Http.HttpClient"/>.
/// </summary>
/// <param name="builder">The <see cref="IHttpClientBuilder"/>.</param>
/// <param name="httpClientLoggerFactory">A delegate that is used to create a custom logger. The logger should implement
/// <see cref="IHttpClientLogger"/> or <see cref="IHttpClientAsyncLogger"/>.</param>
/// <param name="wrapHandlersPipeline">Whether the logging handler with the custom logger would be added to the top
/// or to the bottom of the additional handlers chains.</param>
/// <returns>An <see cref="IHttpClientBuilder"/> that can be used to configure the client.</returns>
/// <remarks>
/// <para>
/// If the <see paramref="wrapHandlersPipeline"/> is `true`, <see cref="IHttpClientLogger.LogRequestStart"/> and
/// <see cref="IHttpClientAsyncLogger.LogRequestStartAsync"/> would be executed before all
/// other additional handlers in the chain. <see cref="IHttpClientLogger.LogRequestStop"/> and
/// <see cref="IHttpClientAsyncLogger.LogRequestStopAsync"/> would be executed after all
/// other additional handlers, essentially wrapping the whole pipeline.
/// </para>
/// <para>
/// If the <see paramref="wrapHandlersPipeline"/> is `false`, <see cref="IHttpClientLogger.LogRequestStart"/> and
/// <see cref="IHttpClientAsyncLogger.LogRequestStartAsync"/> would be executed after all
/// other additional handlers in the chain, right before the primary handler. <see cref="IHttpClientLogger.LogRequestStop"/> and
/// <see cref="IHttpClientAsyncLogger.LogRequestStopAsync"/> would be executed before all
/// other additional handlers, right after the primary handler.
/// </para>
/// <para>
/// The <see cref="IServiceProvider"/> argument provided to <paramref name="httpClientLoggerFactory"/> will be
/// a reference to a scoped service provider that shares the lifetime of the handler chain being constructed.
/// </para>
/// <para>
/// If <see cref="AddLogger"/> is called multiple times, multiple loggers would be added. If <see cref="RemoveAllLoggers"/> was
/// not called before calling <see cref="AddLogger"/>, then new logger would be added in addition to the default ones.
/// </para>
/// </remarks>
public static IHttpClientBuilder AddLogger(this IHttpClientBuilder builder, Func<IServiceProvider, IHttpClientLogger> httpClientLoggerFactory, bool wrapHandlersPipeline = false)
{
ThrowHelper.ThrowIfNull(builder);
ThrowHelper.ThrowIfNull(httpClientLoggerFactory);
builder.Services.Configure<HttpClientFactoryOptions>(builder.Name, options =>
{
options.LoggingBuilderActions.Add(b =>
{
IHttpClientLogger httpClientLogger = httpClientLoggerFactory(b.Services);
HttpClientLoggerHandler handler = new HttpClientLoggerHandler(httpClientLogger);
if (wrapHandlersPipeline)
{
b.AdditionalHandlers.Insert(0, handler);
}
else
{
b.AdditionalHandlers.Add(handler);
}
});
});
return builder;
}
/// <summary>
/// Adds a delegate that will be used to create an additional logger for a named <see cref="System.Net.Http.HttpClient"/>. The custom logger would be invoked
/// from a dedicated logging DelegatingHandler on every request of the corresponding named <see cref="System.Net.Http.HttpClient"/>.
/// </summary>
/// <param name="builder">The <see cref="IHttpClientBuilder"/>.</param>
/// <param name="wrapHandlersPipeline">Whether the logging handler with the custom logger would be added to the top
/// or to the bottom of the additional handlers chains.</param>
/// <returns>An <see cref="IHttpClientBuilder"/> that can be used to configure the client.</returns>
/// <typeparam name="TLogger">
/// The service type of the custom logger as it was registered in DI. The logger should implement <see cref="IHttpClientLogger"/>
/// or <see cref="IHttpClientAsyncLogger"/>.
/// </typeparam>
/// <remarks>
/// <para>
/// If the <see paramref="wrapHandlersPipeline"/> is `true`, <see cref="IHttpClientLogger.LogRequestStart"/> and
/// <see cref="IHttpClientAsyncLogger.LogRequestStartAsync"/> would be executed before all
/// other additional handlers in the chain. <see cref="IHttpClientLogger.LogRequestStop"/> and
/// <see cref="IHttpClientAsyncLogger.LogRequestStopAsync"/> would be executed after all
/// other additional handlers, essentially wrapping the whole pipeline.
/// </para>
/// <para>
/// If the <see paramref="wrapHandlersPipeline"/> is `false`, <see cref="IHttpClientLogger.LogRequestStart"/> and
/// <see cref="IHttpClientAsyncLogger.LogRequestStartAsync"/> would be executed after all
/// other additional handlers in the chain, right before the primary handler. <see cref="IHttpClientLogger.LogRequestStop"/> and
/// <see cref="IHttpClientAsyncLogger.LogRequestStopAsync"/> would be executed before all
/// other additional handlers, right after the primary handler.
/// </para>
/// <para>
/// The <typeparamref name="TLogger"/> will be resolved from a scoped service provider that shares
/// the lifetime of the handler chain being constructed.
/// </para>
/// <para>
/// If <see cref="AddLogger{TLogger}"/> is called multiple times, multiple loggers would be added. If <see cref="RemoveAllLoggers"/> was
/// not called before calling <see cref="AddLogger{TLogger}"/>, then new logger would be added in addition to the default ones.
/// </para>
/// </remarks>
public static IHttpClientBuilder AddLogger<TLogger>(this IHttpClientBuilder builder, bool wrapHandlersPipeline = false)
where TLogger : IHttpClientLogger
{
ThrowHelper.ThrowIfNull(builder);
return AddLogger(builder, services => services.GetRequiredService<TLogger>(), wrapHandlersPipeline);
}
/// <summary>
/// Removes all previously added loggers for a named <see cref="System.Net.Http.HttpClient"/>, including default ones.
/// </summary>
/// <param name="builder">The <see cref="IHttpClientBuilder"/>.</param>
/// <returns>An <see cref="IHttpClientBuilder"/> that can be used to configure the client.</returns>
public static IHttpClientBuilder RemoveAllLoggers(this IHttpClientBuilder builder)
{
ThrowHelper.ThrowIfNull(builder);
builder.Services.Configure<HttpClientFactoryOptions>(builder.Name, options =>
{
options.LoggingBuilderActions.Clear();
options.SuppressDefaultLogging = true;
});
return builder;
}
/// <summary>
/// Adds back the default logging for a named <see cref="System.Net.Http.HttpClient"/>, if it was removed previously by calling <see cref="RemoveAllLoggers"/>.
/// </summary>
/// <param name="builder">The <see cref="IHttpClientBuilder"/>.</param>
/// <returns>An <see cref="IHttpClientBuilder"/> that can be used to configure the client.</returns>
public static IHttpClientBuilder AddDefaultLogger(this IHttpClientBuilder builder)
{
ThrowHelper.ThrowIfNull(builder);
builder.Services.Configure<HttpClientFactoryOptions>(builder.Name, options => options.SuppressDefaultLogging = false);
return builder;
}
}
}