-
Notifications
You must be signed in to change notification settings - Fork 10.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HttpLogging redaction and enrichment #50163
Merged
Merged
Changes from 21 commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
8addb4f
Proof of concept for extending http logging
Tratcher a40b2d8
Feedback, cleanup
Tratcher affbe55
Nits
Tratcher f051458
Add duration
Tratcher dedc429
Fix log, test
Tratcher 429137a
Pool log contexts
Tratcher c01fa22
API review
Tratcher c99ea47
Cleanup, TimeProvider
Tratcher 60b503f
Duration
Tratcher cc1fc7c
Write temp files to the current dir, it might be on a different/faste…
Tratcher 613f38b
Cleanup
Tratcher 2a67078
Tests
Tratcher 9dec914
Fix CopyTo/Async #49989
Tratcher fb37ff9
Apply suggestions from code review
Tratcher ca1b2ec
Clarify code check error
Tratcher be501ae
Comments, copyto, TryOverride
Tratcher 3e3a8ca
Usings
Tratcher 6734921
Code review, comments, tests
Tratcher 8f732ec
Restructuring
Tratcher 98d8261
Note when the request body was not fully logged #49063
Tratcher 607e628
Powershell fix?
Tratcher d319c7d
Revert "Clarify code check error"
Tratcher a846c79
Feedback
Tratcher e09a349
Feedback
Tratcher 9c847fd
Combine logs, log even if an exception happens
Tratcher bf48e46
Clarify
Tratcher b73c3f7
Don't log empty response body
Tratcher 0678c83
Getting cozy
Tratcher 80b0a80
Simplify Duration
Tratcher 40e35f0
Less pooling
Tratcher cee71fb
Clean up comments
Tratcher ec5aad6
Even less re-use
Tratcher c9b6c46
Clean up Unshipped.txt
Tratcher d12739d
Re-fix Unshipped
Tratcher File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
src/Middleware/HttpLogging/samples/HttpLogging.Sample/SampleHttpLoggingInterceptor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using Microsoft.AspNetCore.HttpLogging; | ||
|
||
namespace HttpLogging.Sample; | ||
|
||
internal sealed class SampleHttpLoggingInterceptor : IHttpLoggingInterceptor | ||
{ | ||
public ValueTask OnRequestAsync(HttpLoggingInterceptorContext logContext) | ||
{ | ||
// Compare to ExcludePathStartsWith | ||
if (!logContext.HttpContext.Request.Path.StartsWithSegments("/api")) | ||
{ | ||
logContext.LoggingFields = HttpLoggingFields.None; | ||
} | ||
|
||
// Don't enrich if we're not going to log any part of the request | ||
if (!logContext.IsAnyEnabled(HttpLoggingFields.Request)) | ||
{ | ||
return default; | ||
} | ||
|
||
if (logContext.TryDisable(HttpLoggingFields.RequestPath)) | ||
{ | ||
RedactPath(logContext); | ||
} | ||
|
||
if (logContext.TryDisable(HttpLoggingFields.RequestHeaders)) | ||
{ | ||
RedactRequestHeaders(logContext); | ||
} | ||
|
||
EnrichRequest(logContext); | ||
|
||
return default; | ||
} | ||
|
||
private void RedactRequestHeaders(HttpLoggingInterceptorContext logContext) | ||
{ | ||
foreach (var header in logContext.HttpContext.Request.Headers) | ||
{ | ||
logContext.AddParameter(header.Key, "RedactedHeader"); // TODO: Redact header value | ||
} | ||
} | ||
|
||
private void RedactResponseHeaders(HttpLoggingInterceptorContext logContext) | ||
{ | ||
foreach (var header in logContext.HttpContext.Response.Headers) | ||
{ | ||
logContext.AddParameter(header.Key, "RedactedHeader"); // TODO: Redact header value | ||
} | ||
} | ||
|
||
public ValueTask OnResponseAsync(HttpLoggingInterceptorContext logContext) | ||
{ | ||
// Don't enrich if we're not going to log any part of the response | ||
if (!logContext.IsAnyEnabled(HttpLoggingFields.Response)) | ||
{ | ||
return default; | ||
} | ||
|
||
if (logContext.TryDisable(HttpLoggingFields.ResponseHeaders)) | ||
{ | ||
RedactResponseHeaders(logContext); | ||
} | ||
|
||
EnrichResponse(logContext); | ||
|
||
return default; | ||
} | ||
|
||
private void EnrichResponse(HttpLoggingInterceptorContext logContext) | ||
{ | ||
logContext.AddParameter("ResponseEnrichment", "Stuff"); | ||
} | ||
|
||
private void EnrichRequest(HttpLoggingInterceptorContext logContext) | ||
{ | ||
logContext.AddParameter("RequestEnrichment", "Stuff"); | ||
} | ||
|
||
private void RedactPath(HttpLoggingInterceptorContext logContext) | ||
{ | ||
logContext.AddParameter(nameof(logContext.HttpContext.Request.Path), "RedactedPath"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
147 changes: 147 additions & 0 deletions
147
src/Middleware/HttpLogging/src/HttpLoggingInterceptorContext.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using Microsoft.AspNetCore.Builder; | ||
using Microsoft.AspNetCore.Http; | ||
|
||
namespace Microsoft.AspNetCore.HttpLogging; | ||
|
||
/// <summary> | ||
/// The context used for <see cref="IHttpLoggingInterceptor"/>. | ||
/// </summary> | ||
/// <remarks> | ||
/// Settings will be pre-initialized with the relevant values from <see cref="HttpLoggingOptions" /> and updated with endpoint specific | ||
/// values from <see cref="HttpLoggingAttribute"/> or | ||
/// <see cref="HttpLoggingEndpointConventionBuilderExtensions.WithHttpLogging{TBuilder}(TBuilder, HttpLoggingFields, int?, int?)" />. | ||
/// All settings can be modified per request. All settings will carry over from | ||
/// <see cref="IHttpLoggingInterceptor.OnRequestAsync(HttpLoggingInterceptorContext)"/> | ||
/// to <see cref="IHttpLoggingInterceptor.OnResponseAsync(HttpLoggingInterceptorContext)"/> except the <see cref="Parameters"/> | ||
/// which are cleared after logging the request. | ||
/// </remarks> | ||
public sealed class HttpLoggingInterceptorContext | ||
{ | ||
private HttpContext? _httpContext; | ||
|
||
/// <summary> | ||
/// The request context. | ||
/// </summary> | ||
/// <remarks> | ||
/// This property should not be set by user code except for testing purposes. | ||
/// </remarks> | ||
public HttpContext HttpContext | ||
{ | ||
get => _httpContext ?? throw new InvalidOperationException("HttpContext was not initialized"); | ||
Tratcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Public for 3rd party testing of interceptors. | ||
// We'd make this a required constructor/init parameter but ObjectPool requires a parameterless constructor. | ||
set => _httpContext = value ?? throw new ArgumentNullException(nameof(value)); | ||
} | ||
|
||
/// <summary> | ||
/// What parts of the request and response to log. | ||
Tratcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// </summary> | ||
/// <remarks> | ||
/// This is pre-populated with the value from <see cref="HttpLoggingOptions.LoggingFields"/>, | ||
/// <see cref="HttpLoggingAttribute.LoggingFields"/>, or | ||
/// <see cref="HttpLoggingEndpointConventionBuilderExtensions.WithHttpLogging{TBuilder}(TBuilder, HttpLoggingFields, int?, int?)"/>. | ||
/// </remarks> | ||
public HttpLoggingFields LoggingFields { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the maximum number of bytes of the request body to log. | ||
/// </summary> | ||
/// <remarks> | ||
/// This is pre-populated with the value from <see cref="HttpLoggingOptions.RequestBodyLogLimit"/>, | ||
/// <see cref="HttpLoggingAttribute.RequestBodyLogLimit"/>, or | ||
/// <see cref="HttpLoggingEndpointConventionBuilderExtensions.WithHttpLogging{TBuilder}(TBuilder, HttpLoggingFields, int?, int?)"/>. | ||
/// </remarks> | ||
public int RequestBodyLogLimit { get; set; } | ||
Tratcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// <summary> | ||
/// Gets or sets the maximum number of bytes of the response body to log. | ||
/// </summary> | ||
/// <remarks> | ||
/// This is pre-populated with the value from <see cref="HttpLoggingOptions.ResponseBodyLogLimit"/>, | ||
/// <see cref="HttpLoggingAttribute.ResponseBodyLogLimit"/>, or | ||
/// <see cref="HttpLoggingEndpointConventionBuilderExtensions.WithHttpLogging{TBuilder}(TBuilder, HttpLoggingFields, int?, int?)"/>. | ||
/// </remarks> | ||
public int ResponseBodyLogLimit { get; set; } | ||
|
||
internal long StartTimestamp { get; set; } | ||
internal TimeProvider TimeProvider { get; set; } = null!; | ||
|
||
/// <summary> | ||
/// Data that will be logged as part of the request or response. Values specified in <see cref="LoggingFields"/> | ||
Tratcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// will be added automatically after all interceptors run. These values are cleared after logging the request. | ||
Tratcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// All other relevant settings will carry over to the response. | ||
/// </summary> | ||
public IList<KeyValuePair<string, object?>> Parameters { get; } = new List<KeyValuePair<string, object?>>(); | ||
Tratcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/// <summary> | ||
/// Adds data that will be logged as part of the request or response. See <see cref="Parameters"/>. | ||
/// </summary> | ||
/// <param name="key">The parameter name.</param> | ||
/// <param name="value">The parameter value.</param> | ||
public void AddParameter(string key, object? value) | ||
{ | ||
Parameters.Add(new(key, value)); | ||
} | ||
|
||
/// <summary> | ||
/// Adds the given fields to what's currently enabled in <see cref="LoggingFields"/>. | ||
/// </summary> | ||
/// <param name="fields"></param> | ||
Tratcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public void Enable(HttpLoggingFields fields) | ||
{ | ||
LoggingFields |= fields; | ||
} | ||
|
||
/// <summary> | ||
/// Checks if any of the given fields are currently enabled in <see cref="LoggingFields"/>. | ||
/// </summary> | ||
public bool IsAnyEnabled(HttpLoggingFields fields) | ||
BrennanConroy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
return (LoggingFields & fields) != HttpLoggingFields.None; | ||
} | ||
|
||
/// <summary> | ||
/// Removes the given fields from what's currently enabled in <see cref="LoggingFields"/>. | ||
/// </summary> | ||
/// <param name="fields"></param> | ||
Tratcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public void Disable(HttpLoggingFields fields) | ||
{ | ||
LoggingFields &= ~fields; | ||
} | ||
|
||
/// <summary> | ||
/// Checks if any of the given fields are currently enabled in <see cref="LoggingFields"/> | ||
Tratcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// and disables them so that a custom log value can be provided instead. | ||
/// </summary> | ||
/// <param name="fields">One or more field flags to check.</param> | ||
Tratcher marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// <returns><see langword="true" /> if any of the fields were previously enabled.</returns> | ||
public bool TryDisable(HttpLoggingFields fields) | ||
{ | ||
if (IsAnyEnabled(fields)) | ||
{ | ||
Disable(fields); | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
internal void Reset() | ||
{ | ||
_httpContext = null; | ||
LoggingFields = HttpLoggingFields.None; | ||
RequestBodyLogLimit = 0; | ||
ResponseBodyLogLimit = 0; | ||
StartTimestamp = 0; | ||
TimeProvider = null!; | ||
Parameters.Clear(); | ||
} | ||
|
||
internal double GetDuration() | ||
{ | ||
return TimeProvider.GetElapsedTime(StartTimestamp).TotalMilliseconds; | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to reference
ExcludePathStartsWith
here? I don't think that folks reviewing this sample should take into account https://github.com/dotnet/extensions/blob/a387e38dc927035db5492b47e9607867da7d5005/src/Libraries/Microsoft.AspNetCore.Telemetry.Middleware/Logging/LoggingOptions.cs#L216There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I presume this sample targets external audience that might not be aware of
dotnet/extensions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These samples are for our own usage. I have this one currently set up to mimic what we want to accomplish in extensions. I may clean out some of the references before merging.