{"id":13731203,"url":"https://github.com/Cysharp/ZLogger","last_synced_at":"2025-05-08T04:32:15.464Z","repository":{"id":39492689,"uuid":"249732577","full_name":"Cysharp/ZLogger","owner":"Cysharp","description":"Zero Allocation Text/Structured Logger for .NET with StringInterpolation and Source Generator, built on top of a Microsoft.Extensions.Logging.","archived":false,"fork":false,"pushed_at":"2024-11-05T09:52:58.000Z","size":4030,"stargazers_count":1281,"open_issues_count":0,"forks_count":89,"subscribers_count":22,"default_branch":"master","last_synced_at":"2024-11-11T18:23:42.463Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Cysharp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-24T14:38:02.000Z","updated_at":"2024-11-11T08:17:39.000Z","dependencies_parsed_at":"2024-01-25T02:23:55.562Z","dependency_job_id":"36511abd-dca2-43a1-9a20-14ec6c76a0ab","html_url":"https://github.com/Cysharp/ZLogger","commit_stats":{"total_commits":178,"total_committers":13,"mean_commits":"13.692307692307692","dds":0.2303370786516854,"last_synced_commit":"60d4fa1934b67922671207c93dce15a71157e41a"},"previous_names":[],"tags_count":40,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cysharp%2FZLogger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cysharp%2FZLogger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cysharp%2FZLogger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Cysharp%2FZLogger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Cysharp","download_url":"https://codeload.github.com/Cysharp/ZLogger/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224702164,"owners_count":17355509,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T02:01:25.275Z","updated_at":"2024-11-14T22:30:31.008Z","avatar_url":"https://github.com/Cysharp.png","language":"C#","funding_links":[],"categories":["C#","Parsing","C# #"],"sub_categories":[],"readme":"ZLogger\n===\n[![GitHub Actions](https://github.com/Cysharp/ZLogger/workflows/Build-Debug/badge.svg)](https://github.com/Cysharp/ZLogger/actions) [![Releases](https://img.shields.io/github/release/Cysharp/ZLogger.svg)](https://github.com/Cysharp/ZLogger/releases)\n\n**Z**ero Allocation Text/Structured **Logger** for .NET and Unity, with StringInterpolation and Source Generator, built on top of a `Microsoft.Extensions.Logging`.\n\nThe usual destinations for log output are `Console(Stream)`, `File(Stream)`, `Network(Stream)`, all in UTF8 format. However, since typical logging architectures are based on Strings (UTF16), this requires additional encoding costs. In ZLogger, we utilize the [String Interpolation Improvement of C# 10](https://devblogs.microsoft.com/dotnet/string-interpolation-in-c-10-and-net-6/) and by leveraging .NET 8's [IUtf8SpanFormattable](https://learn.microsoft.com/en-us/dotnet/api/system.iutf8spanformattable?view=net-8.0), we have managed to avoid the boxing of values and maintain high performance by consistently outputting directly in UTF8 from input to output.\n\nZLogger is built directly on top of `Microsoft.Extensions.Logging`. `Microsoft.Extensions.Logging` is an official log abstraction used in many frameworks, such as ASP.NET Core and Generic Host. However, since regular loggers have their own systems, a bridge is required to connect these systems, and this is where a lot of overhead can be observed. ZLogger eliminates the need for this bridge, thereby completely avoiding overhead.\n\n![Alt text](docs/image.png)\n\nThis benchmark is for writing to a file, but the default settings of typical loggers are very slow. This is because they flush after every write. In the benchmark, to ensure fairness, careful attention was paid to set the options in each logger for maximum speed. ZLogger is designed to be the fastest by default, so there is no need to worry about any settings.\n\nThe slowness of this default setting is due to I/O, so it can be mitigated by using a faster drive. When taking benchmarks, please note that the results can vary greatly not only on your local (which is probably fast!) but also on drives attached to the cloud and in environments like Docker. One of the good points about the async-buffered setting is that it can reduce the impact of such I/O issues.\n\nZLogger focuses on the new syntax of C#, and fully adopts Interpolated Strings.\n\n![Alt text](docs/image-1.png)\n\nThis allows for providing parameters to logs in the most convenient form. Also, by closely integrating with System.Text.Json's Utf8JsonWriter, it not only enables high-performance output of text logs but also makes it possible to efficiently output structured logs.\n\nZLogger also emphasizes console output, which is crucial in cloud-native applications. By default, it outputs with performance that can withstand destinations in cloud log management. Of course, it supports both text logs and structured logs.\n\nZLogger delivers its best performance with .NET 8 and above, but it is designed to maintain consistent performance with .NET Standard 2.0 and .NET 6 through a fallback to its own IUtf8SpanFormattable.\n\nAs for standard logger features, it supports loading LogLevel from json, filtering by category, and scopes, as found in Microsoft.Extensions.Logging. In terms of output destinations, it is equipped with sufficient capabilities for `Console`, `File`, `RollingFile`, `InMemory`, `Stream`, and an `AsyncBatchingProcessor` for sending logs over HTTP and similar protocols.\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n## Table of Contents\n\n- [Getting Started](#getting-started)\n- [Logging Providers](#logging-providers)\n  - [Console](#console)\n  - [File](#file)\n  - [RollingFile](#rollingfile)\n  - [Stream](#stream)\n  - [In-Memory](#in-memory)\n  - [LogProcessor](#logprocessor)\n- [Formatter Configurations](#formatter-configurations)\n  - [PlainText](#plaintext)\n  - [JSON](#json)\n    - [KeyNameMutator](#keynamemutator)\n  - [MessagePack](#messagepack)\n  - [Custom Formatter](#custom-formatter)\n- [LogInfo](#loginfo)\n- [ZLoggerOptions](#zloggeroptions)\n- [ZLoggerMessage Source Generator](#zloggermessage-source-generator)\n- [Microsoft.CodeAnalysis.BannedApiAnalyzers](#microsoftcodeanalysisbannedapianalyzers)\n- [Global LoggerFactory](#global-loggerfactory)\n- [Unity](#unity)\n  - [Installation](#installation)\n  - [Basic usage](#basic-usage)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\nGetting Started\n---\nThis library is distributed via NuGet, supporting `.NET Standard 2.0`, `.NET Standard 2.1`, `.NET 6(.NET 7)` and `.NET 8` or above. \nFor Unity, the requirements and installation process are completely different. See the [Unity](#unity) section for details.\n\n\u003e dotnet add package [ZLogger](https://www.nuget.org/packages/ZLogger)\n\nHere is the most simple sample on ASP.NET Core.\n\n```csharp\nusing ZLogger;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Logging.ClearProviders();\nbuilder.Logging.AddZLoggerConsole();\n```\n\nYou can get logger from dependency injection.\n\n```csharp\n@page\n@using ZLogger;\n@inject ILogger\u003cIndex\u003e logger\n@{\n    logger.ZLogInformation($\"Requested path: {this.HttpContext.Request.Path}\");\n}\n```\n\nThis simple logger setup is possible because it is integrated with `Microsoft.Extensions.Logging` by default. For reference, here's how you would set it up using [LoggerFactory](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging):\n\n```csharp\nusing Microsoft.Extensions.Logging;\nusing ZLogger;\n\nusing var factory = LoggerFactory.Create(logging =\u003e\n{\n    logging.SetMinimumLevel(LogLevel.Trace);\n\n    // Add ZLogger provider to ILoggingBuilder\n    logging.AddZLoggerConsole();\n    \n    // Output Structured Logging, setup options\n    // logging.AddZLoggerConsole(options =\u003e options.UseJsonFormatter());\n});\n\nvar logger = factory.CreateLogger(\"Program\");\n\nvar name = \"John\";\nvar age = 33;\n\n// Use **Z**Log method and string interpolation to log message\nlogger.ZLogInformation($\"Hello my name is {name}, {age} years old.\");\n```\n\nNormally, you don't create LoggerFactory yourself. Instead, you set up a Generic Host and receive ILogger through dependency injection (DI). You can setup logger by [.NET Generic Host](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-8.0)(for ASP.NET Core) and if you want to use this in ConsoleApplication, we provides [ConsoleAppFramework](https://github.com/Cysharp/ConsoleAppFramework) to use hosting abstraction.\n\nHere is the showcase of providers.\n\n```csharp\nusing ZLogger;\n\nvar builder = Host.CreateApplicationBuilder();\n\nbuilder.Logging\n    // optional(MS.E.Logging):clear default providers(recommend to remove all)\n    .ClearProviders()\n\n    // optional(MS.E.Logging):setup minimum log level\n    .SetMinimumLevel(LogLevel.Trace)\n    \n    // Add to output to console\n    .AddZLoggerConsole();\n\n    // Add to output to the file\n    .AddZLoggerFile(\"/path/to/file.log\")\n    \n    // Add to output the file that rotates at constant intervals.\n    .AddZLoggerRollingFile(options =\u003e\n    {\n        // File name determined by parameters to be rotated\n        options.FilePathSelector = (timestamp, sequenceNumber) =\u003e $\"logs/{timestamp.ToLocalTime():yyyy-MM-dd}_{sequenceNumber:000}.log\";\n        \n        // The period of time for which you want to rotate files at time intervals.\n        options.RollingInterval = RollingInterval.Day;\n        \n        // Limit of size if you want to rotate by file size. (KB)\n        options.RollingSizeKB = 1024;        \n    })    \n    \n    // Add to output of simple rendered strings into memory. You can subscribe to this and use it.\n    .AddZLoggerInMemory(processor =\u003e\n    {\n        processor.MessageReceived += renderedLogString =\u003e \n        {\n            System.Console.WriteLine(renderedLogString);    \n        };\n    })\n    \n    // Add output to any steram (`System.IO.Stream`)\n    .AddZLoggerStream(stream);\n\n    // Add custom output\n    .AddZLoggerLogProcessor(new YourCustomLogExporter());\n    \n    // Format as json\n    .AddZLoggerConsole(options =\u003e\n    {\n        options.UseJsonFormatter();\n    })\n    \n    // Format as json and configure output\n    .AddZLoggerConsole(options =\u003e\n    {\n        options.UseJsonFormatter(formatter =\u003e\n        {\n            formatter.IncludeProperties = IncludeProperties.ParameterKeyValues;\n        });\n    })\n\n    // Further common settings\n    .AddZLoggerConsole(options =\u003e\n    {\n        // Enable LoggerExtensions.BeginScope\n        options.IncludeScopes = true;\n        \n        // Set TimeProvider\n        options.TimeProvider = yourTimeProvider\n    });\n```\n\nLook at the use of loggers and the syntax of ZLog.\n\n```cs\nusing Microsoft.Extensions.Logging;\nusing ZLogger;\n\n// get ILogger\u003cT\u003e from DI.\npublic class MyClass(ILogger\u003cMyClass\u003e logger)\n{\n    // name = \"Bill\", city = \"Kumamoto\", age = 21\n    public void Foo(string name, string city, int age)\n    {\n        // plain-text:\n        // Hello, Bill lives in Kumamoto 21 years old.\n        // json:\n        // {\"Timestamp\":\"2023-11-30T17:28:35.869211+09:00\",\"LogLevel\":\"Information\",\"Category\":\"MyClass\",\"Message\":\"Hello, Bill lives in Kumamoto 21 years old.\",\"name\":\"Bill\",\"city\":\"Kumamoto\",\"age\":21}\n        // json(IncludeProperties.ParameterKeyValues):\n        // {\"name\":\"Bill\",\"city\":\"Kumamoto\",\"age\":21}\n        logger.ZLogInformation($\"Hello, {name} lives in {city} {age} years old.\");\n    \n        // Explicit property name, you can use custom format string start with '@'\n        logger.ZLogInformation($\"Hello, {name:@user-name} id:{100:@id} {age} years old.\");\n    \n        // Dump variables as JSON, you can use custom format string `json`\n        var user = new User(1, \"Alice\");\n\n        // user: {\"Id\":1,\"Name\":\"Bob\"}\n        logger.ZLogInformation($\"user: {user:json}\");\n    }\n}\n```\n\nAll standard `.Log` methods are processed as strings by ZLogger's Provider. However, by using our unique `.ZLog*` methods, you can process them at high performance while remaining in UTF8. Additionally, these methods support both text logs and structured logs using String Interpolation syntax.\n\nAll logging methods are completely similar as [Microsoft.Extensions.Logging.LoggerExtensions](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.loggerextensions), but it has **Z** prefix overload.\n\nThe ZLog* method uses [InterpolatedStringHandler](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/interpolated-string-handler) in .NET and prepare the template at compile time.\n\nSome special custom formats are also supported. The `:@` can be used when you want to explicitly give the structured log a name other than the name of the variable to capture. `:json` can be used to log the result of JsonSerializing an object.\n\nThe `@` parameter name specification and format string can be used together.\n\n```csharp\n// Today is 2023-12-19.\n// {\"date\":\"2023-12-19T11:25:34.3642389+09:00\"}\nlogger.ZLogDebug($\"Today is {DateTime.Now:@date:yyyy-MM-dd}.\");\n```\n\nLogging Providers\n---\nBy adding Providers, you can configure where the logs are output. ZLogger has the following providers.\n\n| Type                                   | Alias               | Builder Extension      |\n|----------------------------------------|---------------------|------------------------|\n| ZLoggerConsoleLoggerProvider           | ZLoggerConsole      | AddZLoggerConsole      |\n| ZLoggerFileLoggerProvider              | ZLoggerFile         | AddZLoggerFile         |\n| ZLoggerRollingFileLoggerProvider       | ZLoggerRollingFile  | AddZLoggerRollingFile  |\n| ZLoggerStreamLoggerProvider            | ZLoggerStream       | AddZLoggerStream       |\n| ZLoggerInMemoryProcessorLoggerProvider | ZLoggerInMemory     | AddZLoggerInMemory     |\n| ZLoggerLogProcessorLoggerProvider      | ZLoggerLogProcessor | AddZLoggerLogProcessor |\n\nAll Providers can take an Action that sets `ZLoggerOptions` as the last argument. As follows.\n\n```cs\nbuilder.Logging\n    .ClearProviders()\n\n    // Configure options\n    .AddZLoggerConsole(options =\u003e \n    {\n        options.LogToStandardErrorThreshold = LogLevel.Error;\n    });\n    \n    // Configure options with service provider\n    .AddZLoggerConsole((options, services) =\u003e \n    {\n        options.TimeProvider = services.GetService\u003cYourCustomTimeProvider\u003e();\n    });\n```\n\nIf you are using `Microsoft.Extensions.Configuration`, you can set the log level through configuration. In this case, alias of Provider can be used.  for example:\n\n```json\n{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"Default\": \"Information\"\n    },\n    \"ZLoggerConsoleLoggerProvider\": {\n      \"LogLevel\": {\n        \"Default\": \"Debug\"\n      }\n    }\n  }\n}\n```\n\nEach Provider's behavior can be modified using the common `ZLoggerOptions`. For details, please refer to the [ZLoggerOptions](#zloggeroptions) section. Additionally, you can customize structured logging (JSON Logging) using the `UseFormatter` method within these options. For more information on this, check the [Formatter Configurations](#formatter-configurations) section.\n\n### Console\n\nConsole writes to the standard output. Console output is not only for development purposes, but also serves as a standard log input port in containerized and cloud environments, making performance critically important. ZLogger has been optimized to maximize console output performance.\n\n```csharp\nlogging.AddZLoggerConsole();\n```\n\nIf you are using `ZLoggerConsoleLoggerProvider`, the following additional options are available:\n\n| Name                                    | Description                                                                                                                               |\n|:----------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------|\n| `bool OutputEncodingToUtf8`             | Set `Console.OutputEncoding = new UTF8Encoding(false)` when the provider is created.  (default: true)                                     |\n| `bool ConfigureEnableAnsiEscapeCode`    | If set true, then configure console option on execution and enable virtual terminal processing(enable ANSI escape code). (default: false) |\n| `LogLevel LogToStandardErrorThreshold`  | If set, logs at a higher level than the value will be output to standard error. (default: LogLevel.None)                                  |\n\n### File\n\nFile outputs text logs to a file. This is a Provider that writes to a single file in append mode at high speed.\n\n```csharp\nlogging.AddZLoggerFile(\"log.txt\");\n```\n\nIf you are using `ZLoggerFileLoggerProvider`, the following additional options are available:\n\n| Name                                                                              | Description                                                                                                        |\n|:----------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------|\n| `bool fileShared`                                                                  | If set true, enables exclusive control of writing to the same file from multiple processes.(default: false) |\n\n### RollingFile\n\nRollingFile is a Provider that dynamically changes the output file based on certain conditions.\n\n```csharp\n// output to  yyyy-MM-dd_*.log, roll by 1MB or changed date\nlogging.AddZLoggerRollingFile((dt, index) =\u003e $\"{dt:yyyy-MM-dd}_{index}.log\", 1024 * 1024);\n```\n\nIf you are using `ZLoggerRollingFileLoggerProvider`, the following additional options are available:\n\n| Name                                                                              | Description                                                                                                        |\n|:----------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------|\n| `Func\u003cDateTimeOffset, int, string\u003e fileNameSelector`                              | The Func to consturct the file path. `DateTimeOffset` is date of file open time(UTC), `int` is number sequence.        |\n| `RollingInterval rollInterval`                                                    | Interval to automatically rotate files.                                                                            |\n| `int rollSizeKB`                                                                  | Limit size of single file.  If the file size is exceeded, a new file is created with the sequence number moved up. |\n| `bool fileShared`                                                                  | If set true, enables exclusive control of writing to the same file from multiple processes.(default: false) |\n\n\n### Stream\n\nStream can output logs to any arbitrary Stream. For example, if you output to a MemoryStream, you can retrieve the rendered results in memory. If you pass a NetworkStream such as TCP, it will write logs over the network.\n\n```csharp\nvar ms = new MemoryStream();\nlogging.AddZLoggerStream(ms);\n```\n\n### In-Memory\n\nInMemory allows you to retrieve rendered strings as they are generated. It can be conveniently used for purposes such as accumulating logs in a `List\u003cstring\u003e` or `Queue\u003cstring\u003e` for display on screen.\n\n```csharp\nlogging.AddZLoggerInMemory(processor =\u003e\n{\n    processor.MessageReceived += msg =\u003e\n    {\n        Console.WriteLine($\"Received:{msg}\");\n    };\n});\n```\n\nIf you are using `ZLoggerInMemoryLoggerProvider`, the following additional options are available:\n\n| Name                                                                                                            | Description |\n|:----------------------------------------------------------------------------------------------------------------|:------------|\n| `string processorKey`                                                                                           |  If specified, `InMemoryObservableLogProcessor` is registered in the DI container as a keyed service and can be retrieved by name.           |\n| `Action\u003cInMemoryObservableLogProcessor\u003e configureProcessor`                                                     |  Custom actions can be added that use processors instead of DI containers.           |\n\n### LogProcessor\n\nLogProcessor is the most primitive Provider that allows you to customize output on a per-log basis (`IZLoggerEntry`) by implementing a custom `IAsyncLogProcessor`.\n\n```csharp\npublic interface IAsyncLogProcessor : IAsyncDisposable\n{\n    void Post(IZLoggerEntry log);\n}\n```\n\nFor example, a LogProcessor that propagates logs as string events can be written as follows:\n\n```csharp\npublic class SimpleInMemoryLogProcessor : IAsyncLogProcessor\n{\n    public event Action\u003cstring\u003e? OnMessageReceived;\n\n    public void Post(IZLoggerEntry log)\n    {\n        var msg = log.ToString();\n        log.Return();\n        \n        OnMessageReceived?.Invoke(msg);\n    }\n\n    public ValueTask DisposeAsync()\n    {\n        return default;\n    }\n}\n```\n\n```csharp\nvar processor = new SimpleInMemoryLogProcessor();\nprocessor.OnMessageReceived += msg =\u003e Console.WriteLine(msg);\n\nlogging.AddZLoggerLogProcessor(processor);\n```\n\nNote that `IZLoggerEntry` is pooled, so you must always call `Return()`.\n\nHere's a more complex example. `BatchingAsyncLogProcessor` can batch logs together, which is useful for scenarios like sending multiple log lines via HTTP in a single request.\n\n```csharp\npublic class BatchingHttpLogProcessor : BatchingAsyncLogProcessor\n{\n    HttpClient httpClient;\n    ArrayBufferWriter\u003cbyte\u003e bufferWriter;\n    IZLoggerFormatter formatter;\n\n    public BatchingHttpLogProcessor(int batchSize, ZLoggerOptions options)\n        : base(batchSize, options)\n    {\n        httpClient = new HttpClient();\n        bufferWriter = new ArrayBufferWriter\u003cbyte\u003e();\n        formatter = options.CreateFormatter();\n    }\n\n    protected override async ValueTask ProcessAsync(IReadOnlyList\u003cINonReturnableZLoggerEntry\u003e list)\n    {\n        foreach (var item in list)\n        {\n            item.FormatUtf8(bufferWriter, formatter);\n        }\n        \n        var byteArrayContent = new ByteArrayContent(bufferWriter.WrittenSpan.ToArray());\n        await httpClient.PostAsync(\"http://foo\", byteArrayContent).ConfigureAwait(false);\n\n        bufferWriter.Clear();\n    }\n\n    protected override ValueTask DisposeAsyncCore()\n    {\n        httpClient.Dispose();\n        return default;\n    }\n}\n```\n\nIn this case, the LogEntry is NonReturnable, so there's no need to call Return().\n\nFormatter Configurations\n----\n\nBoth PlainText and JSON can be customized in addition to the standard log formats.\n\n### PlainText\n\n```cs\nlogging.AddZLoggerConsole(options =\u003e\n{\n    // Text format\n    // e.g) \"2023-12-01 16:41:55.775|Information|This is log message. (MyNamespace.MyApp)\n    options.UsePlainTextFormatter(formatter =\u003e\n    {\n        formatter.SetPrefixFormatter($\"{0}|{1}|\", (in MessageTemplate template, in LogInfo info) =\u003e template.Format(info.Timestamp, info.LogLevel));\n        formatter.SetSuffixFormatter($\" ({0})\", (in MessageTemplate template, in LogInfo info) =\u003e template.Format(info.Category));\n        formatter.SetExceptionFormatter((writer, ex) =\u003e Utf8StringInterpolation.Utf8String.Format(writer, $\"{ex.Message}\"));\n    });\n});\n```\n\nYou can set Prefix and Suffix individually for text output. For performance reasons, the first argument is a special String Interpolation Template, which is formatted by the lambda expression in the second argument. For properties that can actually be retrieved with `LogInfo`, refer to [LogInfo](#loginfo). It is also possible to retrieve the log file path and line number from LogInfo.\n\nOnly LogLevel supports a special format specification. By passing `:short`, you can get a 3-character log level notation such as `TRC`, `DBG`, `INF`, `WRN`, `ERR`, `CRI`, `NON` (the length of the beginning matches, making it easier to read when opened in an editor). For Timestamp, there are `local | local-longdate | longdate`(local, local-longdate, longdate are same, there are alias), `utc | utc-longdate`, `datetime | local-datetime`, `utc-datetime`, `dateonly | local-dateonly`, `utc-dateonly`, `timeonly | local-timeonly`, `utc-timeonly`. Default is `local`.\n\n```csharp\nlogging.AddZLoggerConsole(options =\u003e\n{\n    options.UsePlainTextFormatter(formatter =\u003e\n    {\n        // 2023-12-19 02:46:14.289 [DBG]......\n        formatter.SetPrefixFormatter($\"{0:utc-longdate} [{1:short}]\", (template, info) =\u003e template.Format(info.Timestamp, info.LogLevel));\n    });\n});\n```\n\nSetExceptionFormatter allows you to customize the display when outputting exceptions. This can be easily converted to a string using `Utf8String.Format`.\n\n### JSON\n\nYou can flexibly change the JSON output format by modifying the JsonFormatter options. For example, if you set `IncludeProperties` to only `ParameterKeyValues`, you will get only the payload JSON. By default, the payload part is output directly without nesting, but if you set `PropertyKeyValuesObjectName`, you can output the payload JSON to a nested location. It is also possible to add values for arbitrary JSON Objects using `AdditionalFormatter`.\n\nThe following is an example of customization to conform to the [Google Cloud Logging format](https://cloud.google.com/logging/docs/structured-logging?hl=en). We have also changed standard key names such as Timestamp.\n\n```csharp\nusing System.Text.Json;\nusing ZLogger;\nusing ZLogger.Formatters;\n\nnamespace ConsoleApp;\n\nusing static IncludeProperties;\nusing static JsonEncodedText; // JsonEncodedText.Encode\n\npublic static class CloudLoggingExtensions\n{\n    // Cloud Logging Json Field\n    // https://cloud.google.com/logging/docs/structured-logging?hl=en\n    public static ZLoggerOptions UseCloudLoggingJsonFormat(this ZLoggerOptions options)\n    {\n        return options.UseJsonFormatter(formatter =\u003e\n        {\n            // Category and ScopeValues is manually write in AdditionalFormatter at labels so remove from include properties.\n            formatter.IncludeProperties = Timestamp | LogLevel | Message | ParameterKeyValues;\n\n            formatter.JsonPropertyNames = JsonPropertyNames.Default with\n            {\n                LogLevel = Encode(\"severity\"),\n                LogLevelNone = Encode(\"DEFAULT\"),\n                LogLevelTrace = Encode(\"DEBUG\"),\n                LogLevelDebug = Encode(\"DEBUG\"),\n                LogLevelInformation = Encode(\"INFO\"),\n                LogLevelWarning = Encode(\"WARNING\"),\n                LogLevelError = Encode(\"ERROR\"),\n                LogLevelCritical = Encode(\"CRITICAL\"),\n\n                Message = Encode(\"message\"),\n                Timestamp = Encode(\"timestamp\"),\n            };\n\n            formatter.PropertyKeyValuesObjectName = Encode(\"jsonPayload\");\n\n            // cache JsonEncodedText outside of AdditionalFormatter\n            var labels = Encode(\"logging.googleapis.com/labels\");\n            var category = Encode(\"category\");\n            var eventId = Encode(\"eventId\");\n            var userId = Encode(\"userId\");\n\n            formatter.AdditionalFormatter = (Utf8JsonWriter writer, in LogInfo) =\u003e\n            {\n                writer.WriteStartObject(labels);\n                writer.WriteString(category, logInfo.Category.JsonEncoded);\n                writer.WriteString(eventId, logInfo.EventId.Name);\n\n                if (logInfo.ScopeState != null \u0026\u0026 !logInfo.ScopeState.IsEmpty)\n                {\n                    foreach (var item in logInfo.ScopeState.Properties)\n                    {\n                        if (item.Key == \"userId\")\n                        {\n                            writer.WriteString(userId, item.Value!.ToString());\n                            break;\n                        }\n                    }\n                }\n                writer.WriteEndObject();\n            };\n        });\n    }\n}\n```\n\nThe list of properties is as follows.\n\n| Name                                                                | Description                                                                       |\n|:--------------------------------------------------------------------|:----------------------------------------------------------------------------------|\n| `JsonPropertyNames JsonPropertyNames`                               | Specify the name of each key in the output JSON                                   |\n| `IncludeProperties IncludeProperties`                               | Flags that can specify properties to be output. (default: `Timestamp, LogLevel, CategoryName, Message, Exception, ScopeKeyValues, ParameterKeyValues`) |\n| `JsonSerializerOptions JsonSerializerOptions`                       | The options of `System.Text.Json`                                                 |\n| `JsonLogInfoFormatter? AdditionalFormatter`                         | Action when rendering additional properties based on `LogInfo`.                   |\n| `JsonEncodedText? PropertyKeyValuesObjectName`                      | If set, the key/value properties is nested under the specified key name.          |\n| `IKeyNameMutator? KeyNameMutator`                                   | You can set the naming convention if you want to automatically convert key names. |\n| `bool UseUtcTimestamp`                                              | If true, timestamp is output in utc. (default: false)                             |\n\n#### KeyNameMutator\n\nBy default, JSON key names are output as is, so in the following character output, \"user.Name\" becomes the JSON key name.\n\n```csharp\nvar user = new User(1, \"Alice\");\nlogger.ZLogInformation($\"Name: {user.Name}\");\n```\n\nIf you set this to `formatter.KeyNameMutator = KeyNameMutator.LastMemberName`, it becomes `Name`. If you set this to `LastMemberNameLowerFirstCharacter`, the first character is replaced with lower-case, resulting in `name`.\n\nThe following is a list of KeyNameMutators provided as standard:\n\n| Name                                  | Description                                                                                               |\n|:--------------------------------------|:----------------------------------------------------------------------------------------------------------|\n| `LastMemberName`                      | Returns the last member name of the source.                                                               |\n| `LowerFirstCharacter`                 | The first character converted to lowercase.                                                               |\n| `UpperFirstCharacter`                 | The first character converted to uppercase.                                                               |\n| `LastMemberNameLowerFirstCharacter`   | Returns the last member name of the source with the first character converted to lowercase.               |\n| `LastMemberNameUpperFirstCharacter`   | Returns the last member name of the source with the first character converted to uppercase.               |       \n\n\n### MessagePack\n\nWe also support structured logging output in binary format using MessagePack instead of JSON. `UseMessagePackFormatter()` requires a reference to the additional package `ZLogger.MessagePack`.\n\n\u003e PM\u003e Install-Package [ZLogger.MessagePack](https://www.nuget.org/packages/ZLogger.MessagePack)\n\n```csharp\nlogging.AddZLoggerFile(\"log.bin\", options =\u003e\n{\n    options.UseMessagePackFormatter();\n});\n```\n\nMessagePack extension uses [MessagePack-CSharp](https://github.com/MessagePack-CSharp/MessagePack-CSharp) as writer.\n\nThe list of properties is as follows.\n\n| Name                                                               | Description                                                        |\n|:-------------------------------------------------------------------|:-------------------------------------------------------------------|\n| `MessagePackSerializerOptions MessagePackSerializerOptions`        | The options of `MessagePack-CSharp`.                               |\n| `IncludeProperties IncludeProperties`                              | Flags that can specify properties to be output. (default: `Timestamp| LogLevel | CategoryName | Message | Exception | ScopeKeyValues | ParameterKeyValues`) |\n| `IKeyNameMutator? KeyNameMutator`                                   | You can set the naming convention if you want to automatically convert key names. |\n\n### Custom Formatter \n\nIf you want to create a formatter other than the default PlainText, Json, and MessagePack, you can implement `IZloggerFormatter` to create any custom output.\n\n```csharp\npublic interface IZLoggerFormatter\n{\n    bool WithLineBreak { get; }\n    void FormatLogEntry\u003cTEntry\u003e(IBufferWriter\u003cbyte\u003e writer, TEntry entry)\n        where TEntry : IZLoggerEntry;\n}\n```\n\n```csharp\noptions.UseFormatter(() =\u003e new MyFormatter());\n```\n\nLogInfo\n---\nAdditional information about when each log was written can be obtained from this LogInfo struct.\n\n| Name                        | Description                                                                                              |\n|:----------------------------|:---------------------------------------------------------------------------------------------------------|\n| `LogCategory Category`      | The category name set for each logger. And holds JsonEncodedText and utf8 byte sequence representations. |\n| `Timestamp Timestamp`       | Timestamp                                                                                                |\n| `LogLevel LogLevel`         | LogLevel  of `Microsoft.Extensions.Logging`                                                              |\n| `EventId EventId`           | EventId of `Microsoft.Extensions.Logging`                                                                |\n| `Exception? Exception`      | Exception given as argument when logging.                                                                |\n| `LogScopeState? ScopeState` | Additional properties set by `ILogger.BeginScope(...)` (if ZLoggerOptions.IncludeScopes = true)          |\n| `ThreadInfo ThreadInfo`     | Additional properties set by ZLoggerOptions.CaptureThreadInfo = true)                                    |\n| `object? Context`    | Additional context | \n| `string? MemberName` | Caller MemberName         |\n| `string? FilePath` | Caller FilePath         |\n| `int LineNumber` | Caller LineNumber         |\n\nZLoggerOptions\n---\nThe following are common option items for all providers.\n\n| Name                                                                         | Description                                                                                                                                                                                                                    |\n|:-----------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `bool IncludeScopes { get; set; }`                                           | Enable `ILogger.BeginScope`, default is `false`.                                                                                                                                                                               |\n| `bool IsFormatLogImmediatelyInStandardLog { get; set; }`                     | Fallback of standard logger.Log, message stringify immediately or not. Default is `true`.                                                                                                                                      |\n| `bool CaptureThreadInfo { get; set; }`                                       | Capture information about the thread that generated a log entry. Default is `false`.                                                                                                                                           |\n| `TimeProvider? TimeProvider { get; set; }`                                   | Gets or sets the time provider for the logger. The Timestamp of LogInfo is generated by TimeProvider's GetUtcNow() and LocalTimeZone when TimeProvider is set. The default value is null, which means use the system standard. |\n| `Action\u003cException\u003e? InternalErrorLogger { get; set; }`                       | `InternalErrorLogger` is a delegate that is called when an exception occurs in the log writing process (such as a serialization error). The default value is `null`, which means errors are ignored.                           |\n| `CreateFormatter()`                                                          | Create an formatter to use in ZLoggerProvider.                                                                                                                                                                                 |\n| `UseFormatter(Func\u003cIZLoggerFormatter\u003e formatterFactory)`                     | Set the formatter that defines the output format of the log.                                                                                                                                                                   |\n| `UsePlainTextFormatter(Action\u003cPlainTextZLoggerFormatter\u003e? configure = null)` | Use the built-in plain text formatter.                                                                                                                                                                                         |\n| `UseJsonFormatter(Action\u003cSystemTextJsonZLoggerFormatter\u003e? configure = null)` | Use the built-in json formatter. (implementation of `System.Text.Json`)                                                                                                                                                        |\n\nBy default, `UsePlainTextFormatter` is set. Also, only one formatter can be set for one provider. If you want to use multiple formatters, you need to add multiple providers.\n\n```csharp\n// Show plain text log for console, json log for file\nlogging.AddZLoggerConsole(options =\u003e options.UsePlainTextFormatter());\nlogging.AddZLoggerFile(\"json.log\", options =\u003e options.UseJsonFormatter());\n```\n\nZLoggerMessage Source Generator\n---\nA log method generator similar to .NET 6's [Compile-time logging source generation](https://learn.microsoft.com/en-us/dotnet/core/extensions/logger-message-generator) is bundled as standard.\n\n```csharp\npublic static partial class MyLogger\n{\n    [ZLoggerMessage(LogLevel.Information, \"Bar: {x} {y}\")]\n    public static partial void Bar(this ILogger\u003cFoo\u003e logger, int x, int y);\n}\n```\n\nThis can achieve the highest performance. It's also possible to use special format specifiers like `:json`.\n\nMicrosoft.CodeAnalysis.BannedApiAnalyzers\n---\n[Microsoft.CodeAnalysis.BannedApiAnalyzers](https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.BannedApiAnalyzers/BannedApiAnalyzers.Help.md) is an interesting analyzer, you can prohibit the normal Log method and induce the user to call ZLogger's ZLog method.\n\n![image](https://user-images.githubusercontent.com/46207/78545188-56ea8a80-7836-11ea-81f2-6cbf7119f027.png)\n\nAll you have to do is prepare the following configuration.\n\n```\nT:Microsoft.Extensions.Logging.LoggerExtensions;Don't use this, use ZLog*** instead.\nT:System.Console;Don't use this, use logger instead.\n```\n\nGlobal LoggerFactory\n---\nLike the traditional log manager, how to get and store logger per type without DI(such as `static readonly ILogger logger = LogManager.GetLogger()`). You can get `ILoggerFactory` from `IHost` before Run and set to the global static loggerfactory store.\n\n```csharp\nusing var host = Host.CreateDefaultBuilder()\n    .ConfigureLogging(logging =\u003e\n    {\n        logging.ClearProviders();\n        logging.AddZLoggerConsole();\n    })\n    .Build(); // use Build instead of Run directly\n\n// get configured loggerfactory.\nvar loggerFactory = host.Services.GetRequiredService\u003cILoggerFactory\u003e();\n\nLogManager.SetLoggerFactory(loggerFactory, \"Global\");\n\n// Run after set global logger.\nawait host.RunAsync();\n\n// -----\n\n// Own static logger manager\npublic static class LogManager\n{\n    static ILogger globalLogger = default!;\n    static ILoggerFactory loggerFactory = default!;\n\n    public static void SetLoggerFactory(ILoggerFactory loggerFactory, string categoryName)\n    {\n        LogManager.loggerFactory = loggerFactory;\n        LogManager.globalLogger = loggerFactory.CreateLogger(categoryName);\n    }\n\n    public static ILogger Logger =\u003e globalLogger;\n\n    // standard LoggerFactory caches logger per category so no need to cache in this manager\n    public static ILogger\u003cT\u003e GetLogger\u003cT\u003e() where T : class =\u003e loggerFactory.CreateLogger\u003cT\u003e();\n    public static ILogger GetLogger(string categoryName) =\u003e loggerFactory.CreateLogger(categoryName);\n}\n```\n\nYou can use this logger manager like following.\n\n```csharp\npublic class Foo\n{\n    static readonly ILogger\u003cFoo\u003e logger = LogManager.GetLogger\u003cFoo\u003e();\n\n    public void Foo(int x)\n    {\n        logger.ZLogDebug($\"do do do: {x}\");\n    }\n}\n```\n\nUnity\n---\n\n### Installation\n\nZLogger uses some of the compile time features of C# 10, and ZLogger.Generator uses some of the features of C# 11.\n\nTo use them in Unity, needs to check the Unity version and set up the compiler.\n\n- Unity 2022.2 or newer\n  - Standard ZLogger features are available.\n  - Unity internally embeds the .NET SDK 6. So C# 10 is available via compiler arguments.\n- Unity 2022.3.12f1 or newer\n  - ZLogger source generator available.\n  - Unity internaly update .NET SDK 6. So C# 11 features are in preview.\n\nPrerequirements:\n- Install [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity)\n  - Required to install the dlls of ZLogger and its dependencies.\n- Install [CsprojModifier](https://github.com/Cysharp/CsprojModifier) \n  - Required to develop in the IDE with a new language version.\n- Install `ZLogger.Unity` package via git url.\n  - Add `https://github.com/Cysharp/ZLogger.git?path=src/ZLogger.Unity/Assets/ZLogger.Unity` to Package Manager\n  \nInstallation steps:\n\n1. Setup the C# compiler for unity. \n    - Add a text file named `csc.rsp` with the following contents under your Assets/.\n        - ```\n          -langVersion:10 -nullable\n          ```\n    - Note:\n        - If you are using assembly definition, put it in the same folder as the asmdef that references ZLogger.\n        - If you are using Unity 2022.3.12f1 or newer, you can use `langVersion:preview` allows parts of C# 11 features.\n\n2. Setup the C# compiler for your IDE. \n    - Add a text file named LangVersion.props with the following contents\n        - ```xml\n          \u003cProject xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\"\u003e\n            \u003cPropertyGroup\u003e\n              \u003cLangVersion\u003e10.0\u003c/LangVersion\u003e\n              \u003cNullable\u003eenable\u003c/Nullable\u003e\n            \u003c/PropertyGroup\u003e\n          \u003c/Project\u003e\n          ``` \n    - Open Project Settings and [C# Project Modifier] section under the [Editor].\n    - Add the .props file you just created, to the list of [Additional project imports].\n    - Note:\n        - If you are using assembly definition, add your additional csproj in the list of [The project to be addef for import].\n        - If you want to use `ZLoggerMessage` Source Generator, require Unity 2022.3.12f1 and change to `\u003cLangVersion\u003e11\u003c/LangVersion\u003e`\n3. Install ZLogger nuget package. \n    - Open [Nuget] -\u003e [Manage Nuget Packages] in the menu bar.\n    - Search `ZLogger`, and press [Install].\n\n\n\n### Basic usage\n\nThe basic functions of ZLogger are also available in Unity as follows. Use LoggerFactory directly to create loggers.\n\n```cs\nvar loggerFactory = LoggerFactory.Create(logging =\u003e\n{\n    logging.SetMinimumLevel(LogLevel.Trace);\n    logging.AddZLoggerUnityDebug(); // log to UnityDebug\n});\n\nvar logger = loggerFactory.CreateLogger\u003cYourClass\u003e();\n\nvar name = \"foo\";\nlogger.ZLogInformation($\"Hello, {name}!\");\n```\n\nAlso supports StructuredLogging(JSON), and FileProvider.\n\n```cs\nvar loggerFactory = LoggerFactory.Create(logging =\u003e\n{\n    logging.AddZLoggerFile(\"/path/to/logfile\", options =\u003e\n    {\n        options.UseJsonFormatter();\n    });\n});\n```\n\nUnity 2022.3.12f1 and enables `-langVersion:preview` supports Source Generator.\n\n```csharp\npublic static partial class LogExtensions\n{\n    [ZLoggerMessage(LogLevel.Debug, \"Hello, {name}\")]\n    public static partial void Hello(this ILogger\u003cNewBehaviourScript\u003e logger, string name);\n}\n```\n\nLicense\n---\nThis library is licensed under the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCysharp%2FZLogger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FCysharp%2FZLogger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCysharp%2FZLogger/lists"}