FlatJsonConsoleFormatter 2.0.0

dotnet add package FlatJsonConsoleFormatter --version 2.0.0
                    
NuGet\Install-Package FlatJsonConsoleFormatter -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FlatJsonConsoleFormatter" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FlatJsonConsoleFormatter" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="FlatJsonConsoleFormatter" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FlatJsonConsoleFormatter --version 2.0.0
                    
#r "nuget: FlatJsonConsoleFormatter, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FlatJsonConsoleFormatter@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FlatJsonConsoleFormatter&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=FlatJsonConsoleFormatter&version=2.0.0
                    
Install as a Cake Tool

FlatJsonConsoleFormatter

This project emits log messages as json, but constructs the log message differently than the default JsonConsoleFormatter. The default JSON formatter creates an object that is deeply nested, includes unnecessary information, and repeats log messages. This log message formatter gives you a simple collection of key/value pairs formatted as a json object that includes state and scopes, while avoiding unnecessary information.

Breaking Changes in v2.0

In version 2.0, default options were introduced to make the most common use cases produce more succinct log messages.

  • Category names are truncated by default to include only the text after the lat period. Typically, this means that the log category will be the class name without the namespace.
  • Duplicate scope keys are merged instead of numbered.

To use old behavior, set explicit options in startup:

var services = new ServiceCollection();
services.AddLogging(builder =>
{
    builder.AddConfiguration(configuration.GetSection("Logging"));
    builder.AddFlatJsonConsole(options => {
        options.TruncateCategory = false;
        options.MergeDuplicateKeys = false;
    });
});

Usage

Add the nuget reference

dotnet add package FlatJsonConsoleFormatter

Setup the library in your project

var services = new ServiceCollection();
services.AddLogging(builder =>
{
    builder.AddConfiguration(configuration.GetSection("Logging"));
    builder.AddFlatJsonConsole();
});

Why another formatter?

Here is an example of a message logged using JsonConsoleFormatter that includes scopes, where the scope object is Dictionary<string, object>.

using (_log.BeginScope(new Dictionary<string, object> { { "MessageId", messageId } }))
using (_log.BeginScope(new Dictionary<string, object> { { "BaseUrl", url }, { "CustomerId", customerId } }))
{
    _log.LogDebug("GET {Endpoint}", endpoint);
}

The above code results in the following log message:

{
    "EventId": 0,
    "LogLevel": "Debug",
    "Category": "ACME.Project.ApiInvoker",
    "Message": "GET https://example.com/api/endpoint/32120",
    "State": {
        "Message": "GET https://example.com/api/endpoint/32120",
        "Endpoint": "https://example.com/api/endpoint/32120",
        "{OriginalFormat}": "GET {Endpoint}"
    },
    "Scopes": [
        {
            "Message": "System.Collections.Generic.Dictionary\u00602[System.String,System.Object]",
            "MessageId": "a38cb57d-4719-4d39-a36c-19f75b289bb4"
        },
        {
            "Message": "System.Collections.Generic.Dictionary\u00602[System.String,System.Object]",
            "BaseUrl": "https://example.com/api",
            "CustomerId": 32120
        }
    ]
}

What is wrong with that log message?

  • The message GET https://example.com/api is repeated multiple times
  • I don't care what the original format string was. I care about having the format values and the result.
  • The state and scope information is contained as nested objects, which makes it harder than necessary to parse using tools such as AWS CloudWatch Log Insights
  • I don't care about the type of the individual scope objects. The scope object is just a collection of values I want logged.
  • Not shown above, but if you want your json formatted on a single line and log an exception, then newlines will be stripped from the exception message making the stack trace unreadable. JSON has an escape sequence for newline characters. This is unnecessary and weird, and seems to be removed in .NET 8.

How does this project address those shortcomings?

FlatJsonConsoleFormatter will log the following message instead:

{
    "EventId": 0,
    "LogLevel": "Debug",
    "Category": "ACME.Project.ApiInvoker",
    "Message": "GET https://example.com/api/endpoint/32120",
    "Endpoint": "https://example.com/api/endpoint/32120",
    "MessageId": "a38cb57d-4719-4d39-a36c-19f75b289bb4"
    "BaseUrl": "https://example.com/api",
    "CustomerId": 32120
}

It does this by merging the state and scope keys into a dictionary and writing them as top-level properties. When there is a conflict between the names of keys from different sources, the default strategy is "last one wins", but you can set an option in startup to keep all scope items and differentiate them by appending a number to the key.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 4,609 12/5/2024
1.2.1 5,837 11/20/2023
1.2.0 702 9/28/2023
1.1.0 371 9/6/2023
1.0.3 514 9/6/2023
1.0.2 393 9/6/2023
1.0.1 397 9/6/2023
1.0.0 381 9/6/2023

BREAKING CHANGES IN 2.0: Default options were changed to truncate category names and merge duplicate log scope keys. To use old behavior, set explicit options in startup.

     Remove net6.0; Add net9.0 target; Merge changes made in v9.0.0 of Microsoft.Extensions.Logging.Console