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

HttpLoggingMiddleware could allow custom code to decide whether to log or not #39200

Closed
Tracked by #31863
hugoqribeiro opened this issue Dec 27, 2021 · 17 comments
Closed
Tracked by #31863
Assignees
Labels
api-needs-work API needs work before it is approved, it is NOT ready for implementation area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions
Milestone

Comments

@hugoqribeiro
Copy link

Background and Motivation

The HttpLoggingMiddleware added recently works based on configuration options in HttpLoggingOptions.

The currently implementation is basically all or nothing. You either add the middleware or not. Even if you can decide what is redacted or not and if request/response bodies are included.

It would be nice if HttpLoggingOptions included a delegate to let the application (custom code) decide whether to log or not for a specific request.

Proposed API

namespace Microsoft.AspNetCore.HttpLogging
{
    public sealed class HttpLoggingOptions
    {
+       public Func<HttpContext, bool> Selector { get; set; } = context => true;
    }
}

Usage Examples

  • Log only failed requests.
  • Log request body only for specific endpoints.

Alternative Designs

You can implement your own middleware but that beats the whole purpose of implementing this feature in the framework.
An alternative would be to make HttpLoggingMiddleware public and add some kind of extensibility to it. At least one could implement that custom middleware but reuse the framework implementation (e.g. request buffering).

Risks

The risk is that performance of HTTP logging would depend on the custom code implemented in the delegate.

@hugoqribeiro hugoqribeiro added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Dec 27, 2021
@pranavkm
Copy link
Contributor

pranavkm commented Dec 27, 2021

Couldn't you wrap UseHttpLogging in a app.UseWhen to achieve this?

@hugoqribeiro
Copy link
Author

hugoqribeiro commented Dec 27, 2021

UseWhen would problably work better for the "log only for specific endpoints".
It would not help for the "log only failed requests" right?

@BrennanConroy
Copy link
Member

Similar to #31844

@BrennanConroy BrennanConroy added this to the .NET 7 Planning milestone Jan 3, 2022
@ghost
Copy link

ghost commented Jan 3, 2022

Thanks for contacting us.

We're moving this issue to the .NET 7 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s).
If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues.
To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

@Tratcher
Copy link
Member

Rather than on/off, you'd want something more granular like HttpLoggingFields (and return None for off).

namespace Microsoft.AspNetCore.HttpLogging
{
    public sealed class HttpLoggingOptions
    {
+       public Func<HttpContext, HttpLoggingFields>? FieldSelector { get; set; } 
    }
}

This is similar to the proposed endpoint attribute in #43222.

@Tratcher Tratcher added api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation labels Feb 24, 2023
@ghost
Copy link

ghost commented Feb 24, 2023

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.

@Ole-Visma
Copy link

As mentioned in the description, it should be possible to log or not log a specific request. Not by route or smth static. Two requests to the same endpoint may be handled differently depending on a specific criteria, like the status code.

@halter73
Copy link
Member

halter73 commented Mar 16, 2023

API Review Notes:

  • Would the FieldSelector be called at the beginning of the request? Yes.
    • Wouldn't this be too early to "log only failed requests"? Yes.
    • Are we okay with this? Sounds like yes. We could possibly add an option to delay logs and also the call to the FieldSelector until after the response is produced at a later date.
  • Should we register a service rather than add a settable Func to options? This would make it a little easier to resolve services and maintain. It's possible to resolve services using the options pattern, so this is a matter of style.
  • What does the example usage look like?
builder.Services.AddHttpLogging(options =>
{
    //options.LoggingFields = FieldSelector.RequestPropertiesAndHeaders;

    options.FieldSelector = context =>
    {
          if (<some condition that means the request should not be logged>)
          {
               return HttpLoggingFields.None;
          }

          return null;
    };
});
  • How do we combine it with HttpLoggingOptions.LoggingFields? Does it override it always? Should we make the return type nullable if we want the default behavior? Let's do that.
  • Do we want to support multiple FieldSelectors? We think manually chaining the funcs is sufficient for this.

API Approved with nullable return type!

namespace Microsoft.AspNetCore.HttpLogging
{
    public sealed class HttpLoggingOptions
    {
+       public Func<HttpContext, HttpLoggingFields?>? FieldSelector { get; set; } 
    }
}

@halter73 halter73 added api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews labels Mar 16, 2023
@BrennanConroy
Copy link
Member

I was thinking about whether the Func should include the current field options, it would be easy to do that with WAB:

builder.Services.AddHttpLogging(options =>
{
    var defaultOptions = HttpLoggingFields.RequestPropertiesAndHeaders;
    options.LoggingFields = defaultOptions;

    options.FieldSelector = context =>
    {
          if (<some condition to remove request headers>)
          {
               return defaultOptions & ~HttpLoggingFields.RequestHeaders;
          }

          return null;
    };
});

But, not everyone uses WAB, and we are using IOptionsMonitor<HttpLoggingOptions> which means the default could change at any time. It seems like we should add the current options then:

+       public Func<HttpContext, HttpLoggingFields, HttpLoggingFields?>? FieldSelector { get; set; } 

Also, thinking about

We could possibly add an option to delay logs and also the call to the FieldSelector until after the response is produced at a later date.

It seems like we would need a way of expressing this in the FieldSelector call, so users would know that this was called after the response or not.
e.g.

+       public Func<HttpContext, bool, HttpLoggingFields, HttpLoggingFields?>? FieldSelector { get; set; }

And optionally adding a delegate so that the parameters can be named.

+       public FieldSelectorDelegate? FieldSelector { get; set; }
+       public delegate HttpLoggingFields? FieldSelectorDelegate(HttpContext context, bool afterResponse, HttpLoggingFields currentFields);

Or, adding a context object like we do in other middleware, in case we want to add more settings in the future, such as changing the response/request body log limits.

@Tratcher
Copy link
Member

If you're going to include the current HttpLoggingFields as an input, then the output doesn't need to be nullable, you can return the original input.

+       public Func<HttpContext, HttpLoggingFields, HttpLoggingFields>? FieldSelector { get; set; } 

@Tratcher
Copy link
Member

It seems like we would need a way of expressing this in the FieldSelector call, so users would know that this was called after the response or not.

You'd also need a separate option to enable the delayed call. Or even a two stage flow where you call it once, it says what to preserve, and then you call it again with the results and it decides if the logs should happen. This seems more complicated than we want to pursue right now.

And by the time you have this many parameters you should be passing a context object.

@BrennanConroy
Copy link
Member

You'd also need a separate option to enable the delayed call.

Yes, I was assuming that since it was already called out by Stephen.

And by the time you have this many parameters you should be passing a context object.

Yes, that's what I said at the end of my comment.

This seems more complicated than we want to pursue right now.

I'm not saying we should add the delayed call right now. But we should design for potential future changes.

@BrennanConroy
Copy link
Member

BrennanConroy commented Apr 3, 2023

API Review Notes:

  • We would like feature parity with the attributes in Make Http Logging Middleware Endpoint aware #43222, meaning request/response size limits should be settable
  • Adding a context object makes this more future proof
  • Media type options would be a pain to pass in and allow setting per endpoint
  • Similar with request/response headers, would need to copy every time
  • Just keep it simple with Fields and body size limits for now, it's a context so we can add things easily in the future!
  • Calling into question why we even need this if you can fairly easily write a middleware to do the same thing (now that the attribute apis are approved)
    • We aren't doing anything fancy on the users' behalf that they couldn't do themselves
    • caveat, if this was called after response, it would be more useful (hard to do in middleware) since we would be buffering request/response and not logging until you made a decision

API needs feedback still

namespace Microsoft.AspNetCore.HttpLogging;

+public sealed class HttpLoggingContext
+{
+    public HttpLoggingContext();

+    public HttpContext HttpContext { get; set; }

+    public HttpLoggingFields LoggingFields { get; set; }

+    public int? RequestBodyLogLimit { get; }

+    public int? ResponseBodyLogLimit { get; }
+}

namespace Microsoft.AspNetCore.HttpLogging;

public sealed class HttpLoggingOptions
{
+    public Action<HttpLoggingContext>? HttpLoggingSelector { get; set; }
}

@BrennanConroy BrennanConroy added api-needs-work API needs work before it is approved, it is NOT ready for implementation and removed api-approved API was approved in API review, it can be implemented labels Apr 3, 2023
@adityamandaleeka
Copy link
Member

Triage: it would be interesting to think about how you could do this in ILogger instead of here.

cc @JamesNK who is working on other logging things that may be related.

@Tratcher
Copy link
Member

ILogger seems too late. Collecting these fields from the HttpContext and creating the log message, especially with the request body, is expensive.

@BrennanConroy
Copy link
Member

I think this is replaced by #31844 (comment)
Which allows the user more granular control over what gets logged.

@adityamandaleeka
Copy link
Member

Closing as duplicate ^

@amcasey amcasey added area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions and removed area-runtime labels Aug 24, 2023
@ghost ghost locked as resolved and limited conversation to collaborators Sep 24, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-needs-work API needs work before it is approved, it is NOT ready for implementation area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions
Projects
None yet
Development

No branches or pull requests

8 participants