Skip to content

crip-home/Crip.AspNetCore.Logging

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

69 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Crip.AspNetCore.Logging

issues forks stars license

Make HTTP request logging ease.

Setup request/response logging in application

Configure log level for all requests:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Crip.AspNetCore.Logging.RequestLoggingMiddleware": "Trace"
    }
  }
}

Configure dependency injection in Startup.ConfigureServices:

services.AddRequestLogging();

Add logging middleware in Startup.Configure:

app.UseRouting();

// After routing
app.UseRequestLoggingMiddleware();
// And before endpoints

app.UseEndpoints(endpoints => ... );

In case of net6.0 it is not important where place app.UseRequestLoggingMiddleware();. Check out example net6.0 project

And now you are ready to see all request/response in logging output:

[ 12:52:31 VRB ] POST http://localhost:5000/api/test/verbose HTTP/1.1
Connection: keep-alive
Content-Type: application/json
Accept: */*
Accept-Encoding: gzip, deflate, br
Host: localhost:5000
Content-Length: 27
                  { "body": "content" }
  { Crip.AspNetCore.Logging.RequestLoggingMiddleware.Test.Verbose } { EventName="HttpRequest", Endpoint="http://localhost:5000/api/test/verbose", HttpMethod="POST", RequestId="xxx", RequestPath="/api/test/verbose", SpanId="|xxx.", TraceId="xxx", ParentId="", ConnectionId="xxx" }

  [ 12:52:31 VRB ] HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
                { "level": "Verbose" }
  { Crip.AspNetCore.Logging.RequestLoggingMiddleware.Test.Verbose } { EventName="HttpResponse", StatusCode=200, Elapsed=6, Endpoint="http://localhost:5000/api/test/verbose", HttpMethod="POST", RequestId="xxx", RequestPath="/api/test/verbose", SpanId="|xxx.", TraceId="xxx", ParentId="", ConnectionId="xxx" }

  [ 12:52:31 INF ] POST http://localhost:5000/api/test/verbose at 00:00:00.006 with 200 OK {Crip.AspNetCore.Logging.RequestLoggingMiddleware.Test.Verbose} {EventName="HttpResponse", StatusCode=200, Elapsed=6, Endpoint="http://localhost:5000/api/test/verbose", HttpMethod="POST", RequestId="xxx", RequestPath="/api/test/verbose", SpanId="|xxx.", TraceId="xxx", ParentId="", ConnectionId="xxx"}

Setup HTTP client request/response logging

Configure log level for a service:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Crip.AspNetCore.Logging.LoggingHandler": "Trace"
    }
  }
}

Configure dependency injection in Startup.ConfigureServices:

services.AddRequestLogging();

Register HTTP client with message handler:

services
    .AddRequestLoggingHandler()
    .AddHttpClient<MyHttpClient>()
    .AddHttpMessageHandler<LoggingHandler<MyHttpClient>>();

// Or use predefined extension method:
services.AddLoggableHttpClient<MyHttpClient>();

Configuration options

Disable response logging

If response content logging is not required - it could be disabled at service registration:

services.AddRequestLogging(options => options.LogResponse = false);

Change verbosity level to reduce logs

With different verbosity level, different output will be written to logs

  • Trace

    • Writes log message with incoming request headers and body
    • Writes log message with returned response headers and body
    • Writes basic response timing/status message
    [12:52:31 VRB] POST http://localhost:5000/api/test/verbose HTTP/1.1
    Connection: keep-alive
    Content-Type: application/json
    Accept: */*
    Accept-Encoding: gzip, deflate, br
    Host: localhost:5000
    Content-Length: 27
    {"body":"content"}
     {Crip.AspNetCore.Logging.RequestLoggingMiddleware.Test.Verbose} {EventName="HttpRequest", Endpoint="http://localhost:5000/api/test/verbose", HttpMethod="POST", RequestId="xxx", RequestPath="/api/test/verbose", SpanId="|xxx.", TraceId="xxx", ParentId="", ConnectionId="xxx"}
    
    [12:52:31 VRB] HTTP/1.1 200 OK
    Content-Type: application/json; charset=utf-8
    {"level":"Verbose"}
     {Crip.AspNetCore.Logging.RequestLoggingMiddleware.Test.Verbose} {EventName="HttpResponse", StatusCode=200, Elapsed=6, Endpoint="http://localhost:5000/api/test/verbose", HttpMethod="POST", RequestId="xxx", RequestPath="/api/test/verbose", SpanId="|xxx.", TraceId="xxx", ParentId="", ConnectionId="xxx"}
    
    [12:52:31 INF] POST http://localhost:5000/api/test/verbose at 00:00:00.006 with 200 OK {Crip.AspNetCore.Logging.RequestLoggingMiddleware.Test.Verbose} {EventName="HttpResponse", StatusCode=200, Elapsed=6, Endpoint="http://localhost:5000/api/test/verbose", HttpMethod="POST", RequestId="xxx", RequestPath="/api/test/verbose", SpanId="|xxx.", TraceId="xxx", ParentId="", ConnectionId="xxx"}
  • Debug

    • Writes log message with incoming request headers
    • Writes log message with returned response headers
    • Writes basic response timing/status message
    [12:55:58 DBG] POST http://localhost:5000/api/test/debug HTTP/1.1
    Connection: keep-alive
    Content-Type: application/json
    Accept: */*
    Accept-Encoding: gzip, deflate, br
    Host: localhost:5000
    Content-Length: 27
    {Crip.AspNetCore.Logging.RequestLoggingMiddleware.Test.Debug} {EventName="HttpRequest", Endpoint="http://localhost:5000/api/test/debug", HttpMethod="POST", RequestId="yyy", RequestPath="/api/test/debug", SpanId="|yyy.", TraceId="yyy", ParentId="", ConnectionId="yyy"}
    
    [12:55:58 DBG] HTTP/1.1 200 OK
    Date: Fri, 26 Aug 2022 09:55:57 GMT
    Transfer-Encoding: chunked
    Content-Type: application/json; charset=utf-8
    {Crip.AspNetCore.Logging.RequestLoggingMiddleware.Test.Debug} {EventName="HttpResponse", StatusCode=200, Elapsed=5, Endpoint="http://localhost:5000/api/test/debug", HttpMethod="POST", RequestId="yyy", RequestPath="/api/test/debug", SpanId="|yyy.", TraceId="yyy", ParentId="", ConnectionId="yyy"}
    
    [12:55:58 INF] POST http://localhost:5000/api/test/debug at 00:00:00.004 with 200 OK {Crip.AspNetCore.Logging.RequestLoggingMiddleware.Test.Debug} {EventName="HttpResponse", StatusCode=200, Elapsed=5, Endpoint="http://localhost:5000/api/test/debug", HttpMethod="POST", RequestId="yyy", RequestPath="/api/test/debug", SpanId="|yyy.", TraceId="yyy", ParentId="", ConnectionId="yyy"}
  • Information

    • Writes basic response timing/status message
    [12:57:24 INF] POST http://localhost:5000/api/test/info at 00:00:00.001 with 200 OK {Crip.AspNetCore.Logging.RequestLoggingMiddleware.Test.Info} {EventName="HttpResponse", StatusCode=200, Elapsed=2, Endpoint="http://localhost:5000/api/test/info", HttpMethod="POST", RequestId="zzz", RequestPath="/api/test/info", SpanId="|zzz.", TraceId="zzz", ParentId="", ConnectionId="zzz"}
  • Any other level will not write logs at all.

Configure verbosity for controller

Each controller has its own source context. This allows configure specific verbosity for a controller. If controller is named OrdersController, you can set verbosity for it in configuration:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Crip.AspNetCore.Logging.RequestLoggingMiddleware": "None",
      "Crip.AspNetCore.Logging.RequestLoggingMiddleware.Orders": "Trace"
    }
  }
}

In this case only OrdersController requests/responses will be written to logs.

Configure verbosity for controller method

Each controller method has its own source context. This allows configure specific verbosity for a controller method. If controller is named OrdersController and you want see requests going to Index method, you can set verbosity for it in configuration:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Crip.AspNetCore.Logging.RequestLoggingMiddleware": "None",
      "Crip.AspNetCore.Logging.RequestLoggingMiddleware.Orders.Index": "Trace"
    }
  }
}

In this case only OrdersController Index method requests/responses will be written to logs.

Configure verbosity for mapped method

When registering methods like in net6.0 minimal API, you can provide name, and that name will be used for logging context:

app
    .MapGet("/", () => Results.Json(new { status = "OK" }))
    .WithName("SomeRouteName");

You can set verbosity for it in configuration:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Crip.AspNetCore.Logging.RequestLoggingMiddleware": "None",
      "Crip.AspNetCore.Logging.RequestLoggingMiddleware.SomeRouteName": "Trace"
    }
  }
}

In this case only this method requests/responses will be written to logs.

If there is no name for request method (for example, unknown controller), Crip.AspNetCore.Logging.RequestLoggingMiddleware context will be used.


Configure verbosity for HTTP client

When you register client message handler with type .AddHttpMessageHandler<LoggingHandler<MyHttpClient>>(), this value is used as source context postfix.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Crip.AspNetCore.Logging.LoggingHandler": "None",
      "Crip.AspNetCore.Logging.LoggingHandler.MyHttpClient": "Trace"
    }
  }
}

In this case only MyHttpClient requests/responses will be written to logs.

Filter endpoints

If there is some endpoints you would like to exclude from logs, you can configure predicate:

services.AddRequestLoggingExclude("/images*", "/swagger*")

Or if you like to include only API requests in logging:

services.AddSingleton<IHttpRequestPredicate>(provider =>
    new EndpointPredicate(false, "/api*"));

Or create your own IHttpRequestPredicate implementation and add it to service collection.

Filter logged content

By default AuthorizationHeaderLoggingMiddleware is added to DI. You can create your own implementations of the IHeaderLogMiddleware or IRequestContentLogMiddleware to modify logged content for your own needs.

IHeaderLogMiddleware

AuthorizationHeaderLoggingMiddleware implements IHeaderLogMiddleware interface and will hide Authorization header values replacing value with *****.

[ 15:26:52 VRB ] GET http://localhost:5000/api/test HTTP/1.1
Connection: keep-alive
Authorization: Bearer *****
Host: localhost:5000

To hide custom authorization header value you can configure options (where header name is case insensitive):

services
    .AddRequestLogging(options =>
    {
        options.AuthorizationHeaders.AuthorizationHeaderNames.Add("x-auth");
    })
[ 15:26:52 VRB ] GET http://localhost:5000/api/test HTTP/1.1
Connection: keep-alive
X-Auth: Basic *****
Host: localhost:5000

You can add CookieHeaderLoggingMiddleware to avoid long cookie value write to logs:

services
    .AddRequestLogging()
    .AddRequestLoggingCookieValueMiddleware();
[ 15:34:27 VRB ] GET http://localhost:5000/api/test HTTP/1.1
Connection: keep-alive
Accept-Encoding: gzip, deflate, br
Cookie: a=Rtgyhjk456***
Host: localhost:5000

IRequestContentLogMiddleware

If you need modify content before you log it - you can implement IRequestContentLogMiddleware. Crip.AspNetCore.Logging.LongJsonContent library provides some implementations for middleware:

  • LongJsonContentMiddleware

For more technical details take a look in example projects: