Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ internal sealed class HttpLoggingRedactionInterceptor : IHttpLoggingInterceptor
private readonly HeaderReader _responseHeadersReader;
private readonly string[] _excludePathStartsWith;
private readonly FrozenDictionary<string, DataClassification> _parametersToRedactMap;
private readonly bool _includeUnmatchedRoutes;

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

_excludePathStartsWith = optionsValue.ExcludePathStartsWith.ToArray();
_includeUnmatchedRoutes = optionsValue.IncludeUnmatchedRoutes;
}

public ValueTask OnRequestAsync(HttpLoggingInterceptorContext logContext)
Expand Down Expand Up @@ -115,6 +117,10 @@ public ValueTask OnRequestAsync(HttpLoggingInterceptorContext logContext)
}
}
}
else if (_includeUnmatchedRoutes)
{
path = context.Request.Path.ToString();
}
}
else if (request.Path.HasValue)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ public class LoggingRedactionOptions
#pragma warning disable CA2227 // Collection properties should be read only
public ISet<string> ExcludePathStartsWith { get; set; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
#pragma warning restore CA2227 // Collection properties should be read only

/// <summary>
/// Gets or sets a value indicating whether to report unmatched routes.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <value>Defaults to <see langword="false"/>.</value>
public bool IncludeUnmatchedRoutes { get; set; }
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ await RunAsync(
const string Content = "Client: hello!";

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

await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout);
Expand Down Expand Up @@ -267,6 +267,48 @@ await RunAsync(
});
}

[Fact]
public async Task HttpLogging_WhenIncludeUnmatchedRoutes_LogRequestPath()
{
await RunAsync(
LogLevel.Information,
services => services.AddHttpLogging(x =>
{
x.MediaTypeOptions.Clear();
x.MediaTypeOptions.AddText("text/*");
x.LoggingFields |= HttpLoggingFields.RequestBody;
}).AddHttpLoggingRedaction(options => options.IncludeUnmatchedRoutes = true),
async (logCollector, client) =>
{
const string Content = "Client: hello!";

using var content = new StringContent(Content, null, MediaTypeNames.Text.Html);
using var response = await client.PostAsync("/myroute/123", content).ConfigureAwait(false);
Assert.True(response.IsSuccessStatusCode);

await WaitForLogRecordsAsync(logCollector, _defaultLogTimeout);

Assert.Equal(1, logCollector.Count);
Assert.Null(logCollector.LatestRecord.Exception);
Assert.Equal(LogLevel.Information, logCollector.LatestRecord.Level);
Assert.Equal(LoggingCategory, logCollector.LatestRecord.Category);

var responseStatus = ((int)response.StatusCode).ToInvariantString();
var state = logCollector.LatestRecord.StructuredState!;

Assert.DoesNotContain(state, x => x.Key == HttpLoggingTagNames.ResponseBody);
Assert.DoesNotContain(state, x => x.Key.StartsWith(HttpLoggingTagNames.RequestHeaderPrefix));
Assert.DoesNotContain(state, x => x.Key.StartsWith(HttpLoggingTagNames.ResponseHeaderPrefix));
Assert.Single(state, x => x.Key == HttpLoggingTagNames.Host && !string.IsNullOrEmpty(x.Value));
Assert.Single(state, x => x.Key == HttpLoggingTagNames.Path && x.Value == "/myroute/123");
Assert.Single(state, x => x.Key == HttpLoggingTagNames.StatusCode && x.Value == responseStatus);
Assert.Single(state, x => x.Key == HttpLoggingTagNames.Method && x.Value == HttpMethod.Post.ToString());
Assert.Single(state, x => x.Key == HttpLoggingTagNames.Duration &&
x.Value != null &&
int.Parse(x.Value, CultureInfo.InvariantCulture) == SlashRouteProcessingTimeMs);
});
}

[Fact]
public async Task HttpLogging_WhenLogLevelInfo_LogRequestStart()
{
Expand Down
Loading