dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.66k stars 4.57k forks source link

[API Proposal]: ConsoleFormatterOptions.MergeScopes #73328

Open verdie-g opened 1 year ago

verdie-g commented 1 year ago

Background and motivation

Here is a log of my application using JsonConsoleFormatter:

{
  "Timestamp":"2022-08-03T18:41:29.7447618\u002B00:00",
  "EventId":0,
  "LogLevel":"Information",
  "Category":"Crpg.Application.Items.Commands.BuyItemCommand",
  "Message":"User \u00271\u0027 bought item \u0027banner_big\u0027",
  "State":{
    "Message":"User \u00271\u0027 bought item \u0027banner_big\u0027",
    "0":1,
    "1":"banner_big",
    "{OriginalFormat}":"User \u0027{0}\u0027 bought item \u0027{1}\u0027"
  },
  "Scopes":[
    {
      "Message":"SpanId:167980b069a14d3f, TraceId:00000000000000005bd6a38e4825e09e, ParentId:66b1523eea668af4",
      "SpanId":"167980b069a14d3f",
      "TraceId":"00000000000000005bd6a38e4825e09e",
      "ParentId":"66b1523eea668af4"
    },
    {
      "Message":"ConnectionId:0HMJK1853J2KC",
      "ConnectionId":"0HMJK1853J2KC"
    },
    {
      "Message":"RequestPath:/users/self/items RequestId:0HMJK1853J2KC:00000002",
      "RequestId":"0HMJK1853J2KC:00000002",
      "RequestPath":"/users/self/items"
    },
    {
      "Message":"Crpg.WebApi.Controllers.UsersController.BuyItem (Crpg.WebApi)",
      "ActionId":"27b7d110-505e-4296-b234-1ced1d9b69dd",
      "ActionName":"Crpg.WebApi.Controllers.UsersController.BuyItem (Crpg.WebApi)"
    },
    {
      "Message":"System.Collections.Generic.KeyValuePair\u00602[System.String,System.Object][]",
      "enduser.id":1,
      "enduser.role":"User"
    }
  ]
}

In my log collector I would like to filter logs by enduser.id but its key depends on the request. Here it's Scopes.4.enduser.id but it could also be Scopes.3.enduser.id. Also if for some reason, a middleware decides that the user is actually someone else and create a new scope I don't want to see two enduser.id in my scope but only the last one. For those reasons I would like my scopes merged like this:

{
  "Timestamp":"2022-08-03T18:41:29.7447618\u002B00:00",
  "EventId":0,
  "LogLevel":"Information",
  "Category":"Crpg.Application.Items.Commands.BuyItemCommand",
  "Message":"User \u00271\u0027 bought item \u0027banner_big\u0027",
  "State":{
    "Message":"User \u00271\u0027 bought item \u0027banner_big\u0027",
    "0":1,
    "1":"banner_big",
    "{OriginalFormat}":"User \u0027{0}\u0027 bought item \u0027{1}\u0027"
  },
  "Scope":{
    "SpanId":"167980b069a14d3f",
    "TraceId":"00000000000000005bd6a38e4825e09e",
    "ParentId":"66b1523eea668af4",
    "ConnectionId":"0HMJK1853J2KC",
    "RequestId":"0HMJK1853J2KC:00000002",
    "RequestPath":"/users/self/items",
    "ActionId":"27b7d110-505e-4296-b234-1ced1d9b69dd",
    "ActionName":"Crpg.WebApi.Controllers.UsersController.BuyItem (Crpg.WebApi)",
    "enduser.id":1,
    "enduser.role":"User"
  }
}

Here I can easily filter by Scope.enduser.id.

API Proposal

  public class ConsoleFormatterOptions
  {
      public ConsoleFormatterOptions() { }

      public bool IncludeScopes { get; set; }
+     public bool MergeScopes { get; set; }
      public string? TimestampFormat { get; set; }
      public bool UseUtcTimestamp { get; set; }
  }

API Usage

Alternative Designs

No response

Risks

No response

dotnet-issue-labeler[bot] commented 1 year ago

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

ghost commented 1 year ago

Tagging subscribers to this area: @dotnet/area-extensions-logging See info in area-owners.md if you want to be subscribed.

Issue Details
### Background and motivation Here is a log of my application using JsonConsoleFormatter: ```json { "Timestamp":"2022-08-03T18:41:29.7447618\u002B00:00", "EventId":0, "LogLevel":"Information", "Category":"Crpg.Application.Items.Commands.BuyItemCommand", "Message":"User \u00271\u0027 bought item \u0027banner_big\u0027", "State":{ "Message":"User \u00271\u0027 bought item \u0027banner_big\u0027", "0":1, "1":"banner_big", "{OriginalFormat}":"User \u0027{0}\u0027 bought item \u0027{1}\u0027" }, "Scopes":[ { "Message":"SpanId:167980b069a14d3f, TraceId:00000000000000005bd6a38e4825e09e, ParentId:66b1523eea668af4", "SpanId":"167980b069a14d3f", "TraceId":"00000000000000005bd6a38e4825e09e", "ParentId":"66b1523eea668af4" }, { "Message":"ConnectionId:0HMJK1853J2KC", "ConnectionId":"0HMJK1853J2KC" }, { "Message":"RequestPath:/users/self/items RequestId:0HMJK1853J2KC:00000002", "RequestId":"0HMJK1853J2KC:00000002", "RequestPath":"/users/self/items" }, { "Message":"Crpg.WebApi.Controllers.UsersController.BuyItem (Crpg.WebApi)", "ActionId":"27b7d110-505e-4296-b234-1ced1d9b69dd", "ActionName":"Crpg.WebApi.Controllers.UsersController.BuyItem (Crpg.WebApi)" }, { "Message":"System.Collections.Generic.KeyValuePair\u00602[System.String,System.Object][]", "enduser.id":1, "enduser.role":"User" } ] } ``` In my log collector I would like to filter logs by `enduser.id` but its key depends on the request. Here it's `Scopes.4.enduser.id` but it could also be `Scopes.3.enduser.id`. Also if for some reason, a middleware decides that the user is actually someone else and create a new scope I don't want to see two `enduser.id` in my scope but only the last one. For those reasons I would like my scopes merged like this: ```json { "Timestamp":"2022-08-03T18:41:29.7447618\u002B00:00", "EventId":0, "LogLevel":"Information", "Category":"Crpg.Application.Items.Commands.BuyItemCommand", "Message":"User \u00271\u0027 bought item \u0027banner_big\u0027", "State":{ "Message":"User \u00271\u0027 bought item \u0027banner_big\u0027", "0":1, "1":"banner_big", "{OriginalFormat}":"User \u0027{0}\u0027 bought item \u0027{1}\u0027" }, "Scope":{ "SpanId":"167980b069a14d3f", "TraceId":"00000000000000005bd6a38e4825e09e", "ParentId":"66b1523eea668af4", "ConnectionId":"0HMJK1853J2KC", "RequestId":"0HMJK1853J2KC:00000002", "RequestPath":"/users/self/items", "ActionId":"27b7d110-505e-4296-b234-1ced1d9b69dd", "ActionName":"Crpg.WebApi.Controllers.UsersController.BuyItem (Crpg.WebApi)", "enduser.id":1, "enduser.role":"User" } } ``` Here I can easily filter by `Scope.enduser.id`. ### API Proposal ```diff public class ConsoleFormatterOptions { public ConsoleFormatterOptions() { } public bool IncludeScopes { get; set; } + public bool MergeScopes { get; set; } public string? TimestampFormat { get; set; } public bool UseUtcTimestamp { get; set; } } ``` ### API Usage ```csharp ``` ### Alternative Designs _No response_ ### Risks _No response_
Author: verdie-g
Assignees: -
Labels: `api-suggestion`, `untriaged`, `area-Extensions-Logging`
Milestone: -