{"id":28372350,"url":"https://github.com/abc-arbitrage/zerolog","last_synced_at":"2026-01-16T03:07:01.738Z","repository":{"id":38428235,"uuid":"77904148","full_name":"Abc-Arbitrage/ZeroLog","owner":"Abc-Arbitrage","description":"A high-performance, zero-allocation .NET logging library.","archived":false,"fork":false,"pushed_at":"2025-05-15T08:52:31.000Z","size":1837,"stargazers_count":417,"open_issues_count":1,"forks_count":31,"subscribers_count":29,"default_branch":"master","last_synced_at":"2025-05-29T14:23:22.964Z","etag":null,"topics":["c-sharp","logger","no-allocation"],"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/Abc-Arbitrage.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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,"zenodo":null}},"created_at":"2017-01-03T09:49:30.000Z","updated_at":"2025-05-15T08:52:35.000Z","dependencies_parsed_at":"2024-11-28T11:19:59.713Z","dependency_job_id":"03daa4e4-dd48-41fa-81de-ddb32911b3d1","html_url":"https://github.com/Abc-Arbitrage/ZeroLog","commit_stats":{"total_commits":626,"total_committers":19,"mean_commits":32.94736842105263,"dds":0.4536741214057508,"last_synced_commit":"dd32e73737dc6c05ebb77b8852a3c86c1fca053a"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"purl":"pkg:github/Abc-Arbitrage/ZeroLog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Abc-Arbitrage%2FZeroLog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Abc-Arbitrage%2FZeroLog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Abc-Arbitrage%2FZeroLog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Abc-Arbitrage%2FZeroLog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Abc-Arbitrage","download_url":"https://codeload.github.com/Abc-Arbitrage/ZeroLog/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Abc-Arbitrage%2FZeroLog/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260658905,"owners_count":23043439,"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":["c-sharp","logger","no-allocation"],"created_at":"2025-05-29T14:17:31.257Z","updated_at":"2026-01-16T03:07:01.730Z","avatar_url":"https://github.com/Abc-Arbitrage.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ZeroLog                    \u003ca href=\"#\"\u003e\u003cimg src=\"icon.png\" align=\"right\" alt=\"Logo\" /\u003e\u003c/a\u003e\n\n[![Build](https://github.com/Abc-Arbitrage/ZeroLog/actions/workflows/build.yml/badge.svg?branch=master\u0026event=push)](https://github.com/Abc-Arbitrage/ZeroLog/actions/workflows/build.yml)\n[![NuGet](https://img.shields.io/nuget/v/ZeroLog.svg?label=NuGet\u0026logo=NuGet)](http://www.nuget.org/packages/ZeroLog/)\n\n**ZeroLog is a high-performance, zero-allocation .NET logging library**.\n\nIt provides logging capabilities to be used in latency-sensitive applications, where garbage collections are undesirable. ZeroLog can be used in a complete zero-allocation manner, meaning that after the initialization phase, it will not allocate any managed object on the heap, thus preventing any GC from being triggered.\n\nSince ZeroLog does not aim to replace any existing logging libraries in any kind of application, it won't try to compete on feature set level with more pre-eminent projects like log4net or NLog for example. The focus will remain on performance and allocation free aspects.\n\nThe project is production ready and you can [get it via NuGet](https://www.nuget.org/packages/ZeroLog) if you want to give it a try.\n\n**ZeroLog v2 requires .NET 6 and C# 10 or later.** If your application targets an earlier .NET version, you can use [ZeroLog v1](https://github.com/Abc-Arbitrage/ZeroLog/tree/v1). Note that a .NET Standard 2.0 target is provided with a limited API surface for use by libraries, but it still requires the main application to target .NET 6 or later.\n\n\n## Internal Design\n \nZeroLog was designed to meet two main objectives:\n\n - Being a **zero allocation library**.\n - Doing **as little work as possible in calling threads**.\n\nThe second goal implies a major design choice: the actual logging is completely asynchronous. It means that writing messages to the appenders occurs in a background thread, and *all formatting operations are delayed to be performed just before the appending*. **No formatting occurs in the calling thread**, the log data is merely marshalled to the background logging thread in the most efficient way possible.\n\nInternally, each logging call data (context, log messages, arguments, etc.) will be serialized to a pooled log message, before being enqueued in a concurrent data structure the background logging thread consumes. The logging thread will then format the log messages and append them to the configured appenders.\n\n\n## Getting Started\n\nBefore using ZeroLog, you need to initialize the `LogManager` by calling `LogManager.Initialize` and providing a configuration.\n\n\u003c!-- snippet: Initialize --\u003e\n\u003ca id='snippet-Initialize'\u003e\u003c/a\u003e\n```cs\nLogManager.Initialize(new ZeroLogConfiguration\n{\n    RootLogger =\n    {\n        Appenders =\n        {\n            new ConsoleAppender()\n        }\n    }\n});\n```\n\u003csup\u003e\u003ca href='/src/ZeroLog.Tests/Snippets.cs#L21-L34' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-Initialize' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\nThe `LogManager` needs to be shut down by calling `LogManager.Shutdown()` when your application needs to exit.\n\nYou can retrieve a logger that will be the logging API entry point. Store this logger in a field.\n\n\u003c!-- snippet: GetLogger --\u003e\n\u003ca id='snippet-GetLogger'\u003e\u003c/a\u003e\n```cs\nprivate static readonly Log _log = LogManager.GetLogger(typeof(YourClass));\n```\n\u003csup\u003e\u003ca href='/src/ZeroLog.Tests/Snippets.cs#L12-L16' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-GetLogger' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n### Logging APIs\n\nTwo logging APIs are provided:\n\n#### A string interpolation API:\n\n\u003c!-- snippet: StringInterpolationApi --\u003e\n\u003ca id='snippet-StringInterpolationApi'\u003e\u003c/a\u003e\n```cs\nvar date = DateTime.Today.AddDays(1);\n_log.Info($\"Tomorrow ({date:yyyy-MM-dd}) will be in {GetNumberOfSecondsUntilTomorrow():N0} seconds.\");\n```\n\u003csup\u003e\u003ca href='/src/ZeroLog.Tests/Snippets.cs#L40-L45' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-StringInterpolationApi' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\nThis API uses C# 10 string interpolation handlers to implement custom interpolation support without allocations.\n\nNote that if the log level is disabled (`Info` in this example), method calls such as `GetNumberOfSecondsUntilTomorrow()` will *not* be executed. \n\n#### A `StringBuilder`-like API:\n\n\u003c!-- snippet: StringBuilderApi --\u003e\n\u003ca id='snippet-StringBuilderApi'\u003e\u003c/a\u003e\n```cs\n_log.Info()\n    .Append(\"Tomorrow (\")\n    .Append(DateTime.Today.AddDays(1), \"yyyy-MM-dd\")\n    .Append(\") will be in \")\n    .Append(GetNumberOfSecondsUntilTomorrow(), \"N0\")\n    .Append(\" seconds.\")\n    .Log();\n```\n\u003csup\u003e\u003ca href='/src/ZeroLog.Tests/Snippets.cs#L51-L61' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-StringBuilderApi' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\nThis API supports more features, but is less convenient to use. You need to call `Log` at the end of the chain. Note that an `Append` overload with a string interpolation handler is provided though.\n\nThe library provides Roslyn analyzers that check for incorrect usages of these APIs.\n\n\n### Structured Data\n\nZeroLog supports appending structured data (formatted as JSON) to log messages.\n\nStructured data can be appended by calling `AppendKeyValue`, like so:\n\n\u003c!-- snippet: StructuredData --\u003e\n\u003ca id='snippet-StructuredData'\u003e\u003c/a\u003e\n```cs\n_log.Info()\n    .Append(\"Tomorrow is another day.\")\n    .AppendKeyValue(\"NumSecondsUntilTomorrow\", GetNumberOfSecondsUntilTomorrow())\n    .Log();\n```\n\u003csup\u003e\u003ca href='/src/ZeroLog.Tests/Snippets.cs#L67-L74' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-StructuredData' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n## Configuration\n\n### Appenders\n\nYou need to instantiate a set of appenders (*output channels*) that can be used by loggers, and pass them to the logger configurations.\n\nSeveral appenders are provided by default, such as `ConsoleAppener` or `DateAndSizeRollingFileAppender`, but you can also write your own.\n\n#### Formatters\n\nThe output format of the built-in appenders may be customized through the `Formatter` property, which controls how the message metadata is formatted. A `DefaultFormatter` is provided, which uses a customizable pattern to control how the message should be displayed, and suffixes it with the structured data as JSON.\n\nThe pattern is a string with the following placeholders:\n\n| Placeholder         | Effect                                                           | Format                                                     | \n|---------------------|------------------------------------------------------------------|------------------------------------------------------------|\n| `%date`             | The message date in UTC (recommended, also contains time of day) | A `DateTime` format string, default: `yyyy-MM-dd`          |\n| `%localDate`        | The message date converted to the local time zone                | A `DateTime` format string, default: `yyyy-MM-dd`          |\n| `%time`             | The message time of day (in UTC)                                 | A `TimeSpan` format string, default: `hh\\:mm\\:ss\\.fffffff` |\n| `%localTime`        | The message time of day (converted to the local time zone)       | A `TimeSpan` format string, default: `hh\\:mm\\:ss\\.fffffff` |\n| `%thread`           | The thread name (or ID) which logged the message                 |                                                            |\n| `%threadId`         | The thread ID which logged the message                           |                                                            |\n| `%threadName`       | The thread name which logged the message, or an empty string     |                                                            |\n| `%level`            | The log level (default: a short uppercase label)                 | `pad` makes eack level the same length                     |\n| `%levelColor`       | The ANSI color code associated to the log lavel                  |                                                            |\n| `%logger`           | The logger name                                                  |                                                            | \n| `%loggerCompact`    | The logger name, with the namespace shortened to its initials    |                                                            | \n| `%message`          | The logged message                                               |                                                            | \n| `%exceptionMessage` | The exception message, if any                                    |                                                            | \n| `%exceptionType`    | The exception type, if any                                       |                                                            | \n| `%newline`          | Equivalent to `Environment.NewLine`                              |                                                            | \n| `%column`           | Inserts padding spaces until a given column index                | The column index to reach                                  | \n| `%resetColor`       | The reset ANSI color code                                        | `\\e[0m`                                                    | \n| `%%`                | Inserts a single `%` character (escaping)                        |                                                            | \n\nPatterns can be written in the form `%{field}` or `%{field:format}` to define a format string. String placeholders accept an integer format string which defines their minimum length. For instance, `%{logger:20}` will always be at least 20 characters wide.\n\nA few default patterns are provided. You can run the `ZeroLog.Examples` project to preview them, or write your own. You can also write your own `Formatter`, as shown in the same project.\n\n### Loggers\n\nZeroLog supports hierarchical loggers. When `GetLogger(\"Foo.Bar.Baz\")` is called, it will try to find the best matching configuration using a hierarchical namespace-like mode. If `Foo.Bar` is configured, but `Foo.Bar.Baz` is not, it will use the configuration for `Foo.Bar`.\n\nYou can specify the following options for each level, by adding items to the `Loggers` collection:\n\n - `Level` is the minimal level the logger will work on.\n - `Appenders` is a set of appenders the logger will use, in addition to the parent appenders which are included by default.\n - `IncludeParentAppenders` can be set to `false` to disable appenders configured at parent levels.\n - `LogMessagePoolExhaustionStrategy` is used to specify what to do when the log message queue is full.\n\nEach appender can be additionally configured with a `Level`, either at the logger configuration level, or on the appender itself.\n\nYou can add an appender instance to multiple logger configurations.\n\n### Root Logger\n\nThe root logger is the default logger. If a `GetLogger` is called on an unconfigured namespace, it will fallback to the root logger.\n\n### Log Message Pool Exhaustion Strategy\n\nThere are currently three strategies to handle a full queue scenario:\n\n - `DropLogMessageAndNotifyAppenders` (default) - Drop the log message and log an error instead.\n - `DropLogMessage` - Forget about the message.\n - `WaitUntilAvailable` - Block until it's possible to log.\n - `Allocate` - Allocates a new message.\n\n### Queue and Message Size\n\nThese values can be configured directly in `ZeroLogConfiguration`:\n\n - `LogMessagePoolSize` (default: `1024`) - Count of pooled log messages. A log message is acquired from the pool on demand, and released by the logging thread.\n - `LogMessageBufferSize` (default: `128`) - The size of the buffer used to serialize log message arguments. Once exceeded, the message is truncated. All `Append` calls use a few bytes, except for those with a `ReadOnlySpan` parameter, which copy the whole data into the buffer.\n - `LogMessageStringCapacity` (default: `32`) - The maximum number of `Append` calls which involve `string` objects that can be made for a log message. Note that `string` objects are also used for format strings.\n\n### Other Settings\n\nOther settings that can be set on the `ZeroLogConfiguration` object are:\n\n - `AutoRegisterEnums` (default: `false`) - Automatically registers an enum type when it's logged for the first time. This causes allocations. Use `LogManager.RegisterEnum` when automatic registration is disabled.\n - `NullDisplayString` (default: `\"null\"`) - The string which should be logged instead of a `null` value.\n - `TruncatedMessageSuffix` (default: `\" [TRUNCATED]\"`) - The string which is appended to a message when it is truncated.\n - `AppenderQuarantineDelay` (default: 15 seconds) - The time an appender will be put into quarantine (not used to log messages) after it throws an exception.\n - `AppendingStrategy` (default: `Asynchronous`) - The way log messages are handled, use `Synchronous` in unit tests.\n\n## Unit testing\n\nZeroLog can be used in unit tests. The easiest way to initialize it is to use the `ZeroLogConfiguration.CreateTestConfiguration()` method, which returns suitable defaults for unit tests. It will log messages to `Console.Out` from the current thread, which can then be intercepted by a logging framework.\n\nHere is a minimal ZeroLog initializer for NUnit:\n\n\u003c!-- snippet: NUnitInitializer --\u003e\n\u003ca id='snippet-NUnitInitializer'\u003e\u003c/a\u003e\n```cs\n[SetUpFixture]\npublic class Initializer\n{\n    [OneTimeSetUp]\n    public void SetUp()\n        =\u003e LogManager.Initialize(ZeroLogConfiguration.CreateTestConfiguration());\n\n    [OneTimeTearDown]\n    public void TearDown()\n        =\u003e LogManager.Shutdown();\n}\n```\n\u003csup\u003e\u003ca href='/src/ZeroLog.Tests/Snippets.Init.cs#L10-L24' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-NUnitInitializer' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\nThe [Verify.ZeroLog](https://github.com/VerifyTests/Verify.ZeroLog) project provides support for snapshot testing with [Verify](https://github.com/VerifyTests/Verify). It can be used to validate that the tested project takes the expected code path, with the expected intermediate values, thanks to the messages it logs in the process.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabc-arbitrage%2Fzerolog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fabc-arbitrage%2Fzerolog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabc-arbitrage%2Fzerolog/lists"}