Skip to content

Commit 0e16702

Browse files
authored
Add an option to log unsupported route (#6077)
* Add options to log unsupported routes * Refactor * Refactor * Refactor
1 parent 2bb24da commit 0e16702

File tree

3 files changed

+58
-1
lines changed

3 files changed

+58
-1
lines changed

src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/HttpLoggingRedactionInterceptor.cs

+6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ internal sealed class HttpLoggingRedactionInterceptor : IHttpLoggingInterceptor
3333
private readonly HeaderReader _responseHeadersReader;
3434
private readonly string[] _excludePathStartsWith;
3535
private readonly FrozenDictionary<string, DataClassification> _parametersToRedactMap;
36+
private readonly bool _includeUnmatchedRoutes;
3637

3738
public HttpLoggingRedactionInterceptor(
3839
IOptions<LoggingRedactionOptions> options,
@@ -59,6 +60,7 @@ public HttpLoggingRedactionInterceptor(
5960
_responseHeadersReader = new(optionsValue.ResponseHeadersDataClasses, redactorProvider, HttpLoggingTagNames.ResponseHeaderPrefix);
6061

6162
_excludePathStartsWith = optionsValue.ExcludePathStartsWith.ToArray();
63+
_includeUnmatchedRoutes = optionsValue.IncludeUnmatchedRoutes;
6264
}
6365

6466
public ValueTask OnRequestAsync(HttpLoggingInterceptorContext logContext)
@@ -115,6 +117,10 @@ public ValueTask OnRequestAsync(HttpLoggingInterceptorContext logContext)
115117
}
116118
}
117119
}
120+
else if (_includeUnmatchedRoutes)
121+
{
122+
path = context.Request.Path.ToString();
123+
}
118124
}
119125
else if (request.Path.HasValue)
120126
{

src/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware/Logging/LoggingRedactionOptions.cs

+9
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ public class LoggingRedactionOptions
100100
#pragma warning disable CA2227 // Collection properties should be read only
101101
public ISet<string> ExcludePathStartsWith { get; set; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
102102
#pragma warning restore CA2227 // Collection properties should be read only
103+
104+
/// <summary>
105+
/// Gets or sets a value indicating whether to report unmatched routes.
106+
/// </summary>
107+
/// <remarks>
108+
/// If set to true, instead of logging <i>unknown</i> value for path attribute it will log whole path of routes not identified by ASP.NET Routing.
109+
/// </remarks>
110+
/// <value>Defaults to <see langword="false"/>.</value>
111+
public bool IncludeUnmatchedRoutes { get; set; }
103112
}
104113

105114
#endif

test/Libraries/Microsoft.AspNetCore.Diagnostics.Middleware.Tests/Logging/AcceptanceTests.cs

+43-1
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ await RunAsync(
230230
const string Content = "Client: hello!";
231231

232232
using var content = new StringContent(Content, null, requestContentType);
233-
using var response = await client.PostAsync("/", content).ConfigureAwait(false);
233+
using var response = await client.PostAsync("/myroute/123", content).ConfigureAwait(false);
234234
Assert.True(response.IsSuccessStatusCode);
235235

236236
await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout);
@@ -267,6 +267,48 @@ await RunAsync(
267267
});
268268
}
269269

270+
[Fact]
271+
public async Task HttpLogging_WhenIncludeUnmatchedRoutes_LogRequestPath()
272+
{
273+
await RunAsync(
274+
LogLevel.Information,
275+
services => services.AddHttpLogging(x =>
276+
{
277+
x.MediaTypeOptions.Clear();
278+
x.MediaTypeOptions.AddText("text/*");
279+
x.LoggingFields |= HttpLoggingFields.RequestBody;
280+
}).AddHttpLoggingRedaction(options => options.IncludeUnmatchedRoutes = true),
281+
async (logCollector, client) =>
282+
{
283+
const string Content = "Client: hello!";
284+
285+
using var content = new StringContent(Content, null, MediaTypeNames.Text.Html);
286+
using var response = await client.PostAsync("/myroute/123", content).ConfigureAwait(false);
287+
Assert.True(response.IsSuccessStatusCode);
288+
289+
await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout);
290+
291+
Assert.Equal(1, logCollector.Count);
292+
Assert.Null(logCollector.LatestRecord.Exception);
293+
Assert.Equal(LogLevel.Information, logCollector.LatestRecord.Level);
294+
Assert.Equal(LoggingCategory, logCollector.LatestRecord.Category);
295+
296+
var responseStatus = ((int)response.StatusCode).ToInvariantString();
297+
var state = logCollector.LatestRecord.StructuredState!;
298+
299+
Assert.DoesNotContain(state, x => x.Key == HttpLoggingTagNames.ResponseBody);
300+
Assert.DoesNotContain(state, x => x.Key.StartsWith(HttpLoggingTagNames.RequestHeaderPrefix));
301+
Assert.DoesNotContain(state, x => x.Key.StartsWith(HttpLoggingTagNames.ResponseHeaderPrefix));
302+
Assert.Single(state, x => x.Key == HttpLoggingTagNames.Host && !string.IsNullOrEmpty(x.Value));
303+
Assert.Single(state, x => x.Key == HttpLoggingTagNames.Path && x.Value == "/myroute/123");
304+
Assert.Single(state, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == responseStatus);
305+
Assert.Single(state, x => x.Key == HttpLoggingTagNames.Method && x.Value == HttpMethod.Post.ToString());
306+
Assert.Single(state, x => x.Key == HttpLoggingTagNames.Duration &&
307+
x.Value != null &&
308+
int.Parse(x.Value, CultureInfo.InvariantCulture) == SlashRouteProcessingTimeMs);
309+
});
310+
}
311+
270312
[Fact]
271313
public async Task HttpLogging_WhenLogLevelInfo_LogRequestStart()
272314
{

0 commit comments

Comments
 (0)