Skip to content
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

Adds HttpLogging middleware #31816

Merged
merged 45 commits into from
Apr 20, 2021
Merged

Adds HttpLogging middleware #31816

merged 45 commits into from
Apr 20, 2021

Conversation

jkotalik
Copy link
Contributor

Fixes #3700

There are a few follow up issues that I'll file:

  • W3C Logging
  • Request Response Trailers Logging
  • Redaction/Additional logging

I'll write the API proposal in the issue linked with the information here.

There are a few tests failing ATM as I have some questions that review will help with.

@jkotalik jkotalik added this to the 6.0-preview4 milestone Apr 14, 2021
@jkotalik jkotalik requested review from Tratcher and a team as code owners April 14, 2021 21:22
@davidfowl davidfowl requested a review from halter73 April 14, 2021 22:13
src/Middleware/HttpLogging/src/HttpLoggingFields.cs Outdated Show resolved Hide resolved
src/Middleware/HttpLogging/src/HttpLoggingMiddleware.cs Outdated Show resolved Hide resolved
src/Middleware/HttpLogging/src/HttpLoggingMiddleware.cs Outdated Show resolved Hide resolved
src/Middleware/HttpLogging/src/HttpLoggingMiddleware.cs Outdated Show resolved Hide resolved
src/Middleware/HttpLogging/src/HttpLoggingOptions.cs Outdated Show resolved Hide resolved
src/Middleware/HttpLogging/src/HttpLoggingOptions.cs Outdated Show resolved Hide resolved
src/Middleware/HttpLogging/src/HttpRequestLog.cs Outdated Show resolved Hide resolved
src/Middleware/HttpLogging/src/HttpResponseLog.cs Outdated Show resolved Hide resolved
@davidfowl
Copy link
Member

We need to do an api review

@jkotalik
Copy link
Contributor Author

For sure; I'm scheduling one for tomorrow/friday one off.

@jkotalik jkotalik added the api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews label Apr 15, 2021
@ghost
Copy link

ghost commented Apr 15, 2021

Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:

  • The PR contains changes to the reference-assembly that describe the API change. Or, you have included a snippet of reference-assembly-style code that illustrates the API change.
  • The PR describes the impact to users, both positive (useful new APIs) and negative (breaking changes).
  • Someone is assigned to "champion" this change in the meeting, and they understand the impact and design of the change.

private readonly IOptionsMonitor<HttpLoggingOptions> _options;
private const int DefaultRequestFieldsMinusHeaders = 7;
private const int DefaultResponseFieldsMinusHeaders = 2;
private const string Redacted = "X";
Copy link
Member

@davidfowl davidfowl Apr 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Redacted] instead of X

/// <summary>
/// Maximum request body size to log (in bytes). Defaults to 32 KB.
/// </summary>
public int RequestBodyLogLimit { get; set; } = 32 * 1024;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need Size in this name? We have size everywhere else in Kestrel.

long RequestBodySizeLimit
long ResponseBodySizeLimit

Why didn't we go with longs, we didn't learn from IIS 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a long in Kestrel. I can only imagine the logs with 2 GB+ bodies inside 😆

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could even make it nullable for consistency with Kestrel. Why force an upper bound of 2^63 bytes!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a serious conversation? I guess we can make it a long....

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK we can leave it as an int and rename it to SizeLimit

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We covered this in the API review and picked LogLimit over SizeLimit. In kestrel it's an absolute size limit and exceeding it causes the request to fail. Here it's a limit on the amount that will be logged before we truncate.

You really want to support GB of logged data? We'll definitely need to switch to a chunking/streaming model then to avoid buffering the whole thing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets stick with an int (2GB is still GB) but I think Size should be in the name. It's the size of the request and response body in bytes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @Tratcher, unless we have a name like RequestBodyLogSizeLimit which seems needlessly verbose.

/// <param name="contentType">The content type to add.</param>
public void AddBinary(string contentType)
{
throw new NotSupportedException();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the deal here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wanted to have the APIs present for binary logging format, which are currently not supported.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RequestBufferingStream? requestBufferingStream = null;
Stream? originalBody = null;

if ((HttpLoggingFields.Request & options.LoggingFields) != HttpLoggingFields.None)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice we don't check if Logging is enabled before we do any of this stuff. Seems like we should no?


// Afterwards, we need to call convert in a loop until complete is true.
// The first call to convert many return true, but if it doesn't, we call
// Convert with a empty ReadOnlySequence and flush: true until we get completed: true.
Copy link
Member

@davidfowl davidfowl Apr 20, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? There's no more data coming in right?


private static readonly Action<ILogger, Exception?> _unrecognizedMediaType =
LoggerMessage.Define(LogLevel.Debug, new EventId(6, "UnrecognizedMediaType"), "Unrecognized Content-Type for body.");

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Remove space

Copy link
Member

@davidfowl davidfowl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jkotalik
Copy link
Contributor Author

I did a bunch of perf runs locally to generally get a picture of what's going on.

With a custom logging provider which noop'd (to remove the impact of the actual logging), I saw around a 3% regression with logging the request, response, and response body.

Before (no HttpLoggingMiddleware)

Bombarding http://localhost:5000 for 10s using 125 connection(s)
[=================================================================================================================================================================================================================================================================================] 10s
Done!
Statistics        Avg      Stdev        Max
  Reqs/sec     70548.12   12436.92  144929.16
  Latency        1.72ms   763.52us    70.14ms
  HTTP codes:
    1xx - 0, 2xx - 713518, 3xx - 0, 4xx - 0, 5xx - 0
    others - 0
  Throughput:    14.36MB/s

After (with middleware and noop logging)

Bombarding http://localhost:5000 for 10s using 125 connection(s)
[=================================================================================================================================================================================================================================================================================] 10s
Done!
Statistics        Avg      Stdev        Max
  Reqs/sec     68877.66   14550.83  114951.68
  Latency        1.77ms   629.66us   139.06ms
  HTTP codes:
    1xx - 0, 2xx - 696073, 3xx - 0, 4xx - 0, 5xx - 0
    others - 0
  Throughput:    14.01MB/s

I averaged a few runs here and in general, removing the middleware showed around a 3% gain, which is expected.

I'm going to try some worst case scenarios just for kicks now. But there isn't anything obscenely wrong perf wise.

@jkotalik
Copy link
Contributor Author

To no surprise, a larger response body affects perf even more, seeing around a 40% regression. Writing a 50 KB response, perf numbers go from 21k RPS to 13K RPS.

Bombarding http://localhost:5000 for 10s using 125 connection(s)
[=================================================================================================================================================================================================================================================================================] 10s
Done!
Statistics        Avg      Stdev        Max
  Reqs/sec     20990.03    8384.13   52401.26
  Latency        5.97ms     1.81ms    70.33ms
  HTTP codes:
    1xx - 0, 2xx - 209109, 3xx - 0, 4xx - 0, 5xx - 0
    others - 0
  Throughput:     0.98GB/s
C:\Users\jukotali> ^C
C:\Users\jukotali> bombardier localhost:5000
Bombarding http://localhost:5000 for 10s using 125 connection(s)
[=================================================================================================================================================================================================================================================================================] 10s
Done!
Statistics        Avg      Stdev        Max
  Reqs/sec     12807.25    5641.34   28040.42
  Latency        9.52ms     3.94ms   226.19ms
  HTTP codes:
    1xx - 0, 2xx - 131084, 3xx - 0, 4xx - 0, 5xx - 0
    others - 0
  Throughput:   628.02MB/s

@Tratcher
Copy link
Member

To no surprise, a larger response body affects perf even more, seeing around a 40% regression. Writing a 50 KB response, perf numbers go from 21k RPS to 13K RPS.

Buffering the bytes shouldn't cause much throughput overhead(?). Is it the decoding and logging a big string that slows it down? I suspect streaming/chunked body logging will help here.

@jkotalik
Copy link
Contributor Author

Buffering the bytes shouldn't cause much throughput overhead(?). Is it the decoding and logging a big string that slows it down? I suspect streaming/chunked body logging will help here.

Yeah there are many things we can do to improve perf here, but that's for after this PR is merged 😄 . I mostly wanted to make sure there wasn't something horrendously wrong.

@BrennanConroy
Copy link
Member

Buffering the bytes shouldn't cause much throughput overhead(?).

Console logging pushes to a BlockingCollection which will hold a few items before blocking more logs. So once the queue is full, it completely blocks the thread.

@davidfowl
Copy link
Member

Buffering the bytes shouldn't cause much throughput overhead(?). Is it the decoding and logging a big string that slows it down? I suspect streaming/chunked body logging will help here.

Copying the body will have CPU overhead and decoding UTF8 will have even more CPU overhead.

@jkotalik
Copy link
Contributor Author

This isn't logging to console atm, it's nooping that portion. This is to primarily check the perf of the middleware and buffering.

@BrennanConroy
Copy link
Member

Oh, that doesn't seem great then 😄

@jkotalik
Copy link
Contributor Author

@dotnet/aspnet-build please merge.

@dougbu dougbu merged commit 4ee074f into release/6.0-preview4 Apr 20, 2021
@dougbu dougbu deleted the jkotalik/httpLogging branch April 20, 2021 20:57
@amcasey amcasey added area-middleware Includes: URL rewrite, redirect, response cache/compression, session, and other general middlesware area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions and removed area-runtime labels Jun 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-approved API was approved in API review, it can be implemented area-middleware Includes: URL rewrite, redirect, response cache/compression, session, and other general middlesware area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add RequestLoggingMiddleware