{"id":13445556,"url":"https://github.com/adams85/filelogger","last_synced_at":"2025-12-29T00:56:23.412Z","repository":{"id":49033873,"uuid":"104459348","full_name":"adams85/filelogger","owner":"adams85","description":"A lightweight yet feature-rich file logger implementation for the Microsoft.Extensions.Logging framework.","archived":false,"fork":false,"pushed_at":"2025-02-01T10:27:32.000Z","size":258,"stargazers_count":157,"open_issues_count":0,"forks_count":22,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-05-15T08:13:13.749Z","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/adams85.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":"2017-09-22T09:52:11.000Z","updated_at":"2025-05-11T14:09:39.000Z","dependencies_parsed_at":"2024-01-02T22:45:08.001Z","dependency_job_id":"c7faf247-5c7b-4161-9e5c-7ac6309d2045","html_url":"https://github.com/adams85/filelogger","commit_stats":{"total_commits":110,"total_committers":5,"mean_commits":22.0,"dds":0.08181818181818179,"last_synced_commit":"4b498c4fb6315ad44be4d69e691158bb59e06d1d"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adams85%2Ffilelogger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adams85%2Ffilelogger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adams85%2Ffilelogger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adams85%2Ffilelogger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adams85","download_url":"https://codeload.github.com/adams85/filelogger/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254337612,"owners_count":22054253,"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-07-31T05:00:35.916Z","updated_at":"2025-12-29T00:56:23.398Z","avatar_url":"https://github.com/adams85.png","language":"C#","funding_links":["https://www.buymeacoffee.com/adams85"],"categories":["Frameworks, Libraries and Tools","框架, 库和工具","C\\#"],"sub_categories":["Logging","日志"],"readme":"| :mega: Important notices |\n|--------------------------|\n| If upgrading from v3 to v4, there are some minor breaking changes you may need to be aware of. See the [release notes](https://github.com/adams85/filelogger/releases/tag/v4.0.0) for the details. Documentation of the previous major version (v3) is available [here](https://github.com/adams85/filelogger/tree/3.6). |\n\n# Karambolo.Extensions.Logging.File\n\nThis class library contains a lightweight implementation of the [Microsoft.Extensions.Logging.ILoggerProvider](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.iloggerprovider) interface for file logging. Runs on all .NET platforms which implement .NET Standard 2.0+, including .NET Core 2+ (ASP.NET Core 2.1+) and .NET 5+.\n\n[![Build status](https://github.com/adams85/filelogger/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/adams85/filelogger/actions/workflows/ci.yml)\n[![NuGet Release](https://img.shields.io/nuget/v/Karambolo.Extensions.Logging.File.svg)](https://www.nuget.org/packages/Karambolo.Extensions.Logging.File/)\n[![Donate](https://img.shields.io/badge/-buy_me_a%C2%A0coffee-gray?logo=buy-me-a-coffee)](https://www.buymeacoffee.com/adams85)\n\nThe code is based on [ConsoleLogger](https://github.com/dotnet/runtime/tree/master/src/libraries/Microsoft.Extensions.Logging.Console), whose **full feature set is implemented** (including log scopes and configuration reloading). The library has **no 3rd party dependencies**.\n\nTo prevent I/O blocking, **log messages are processed asynchronously in the background**, with a guarantee that [pending messages are written to file on graceful shutdown](https://stackoverflow.com/questions/40073743/how-to-log-to-a-file-without-using-third-party-logger-in-net-core#comment129425711_60515392).\n\nFile system access is implemented on top of the [Microsoft.Extensions.FileProviders.IFileProvider](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.fileproviders.ifileprovider) abstraction, so it's even possible to use a custom backing storage.\n\n**JSON structured logging** (following the format established by [the JSON formatter of the built-in console logger](https://learn.microsoft.com/en-us/dotnet/core/extensions/console-log-formatter#register-formatter)) is also available.\n\nThe **self-contained trimmed** and **Native AOT** deployments models are also supported (in applications running on .NET 8 or newer).\n\n### Additional features:\n - Flexible configuration:\n   - Hierarchical (two-level) log file settings.\n   - Fine-grained control over log message filtering.\n - File path templates for including log entry date (or other user-defined tokens) in log file paths/names.\n - Size-limited log files with customizable counter format. (Log rotation is [achievable through customization](https://github.com/adams85/filelogger/tree/master/samples/LogRotation).)\n - Fully customizable log text formatting.\n - Designed with extensibility/customizability in mind.\n - Support for registering multiple providers with different settings.\n\n## Installation\n\nAdd the _Karambolo.Extensions.Logging.File_ NuGet package to your project:\n\n    dotnet add package Karambolo.Extensions.Logging.File\n\nor, if you want structured logging, add _Karambolo.Extensions.Logging.File.Json_ NuGet package instead:\n\n    dotnet add package Karambolo.Extensions.Logging.File.Json\n\nIf you have a .NET Core/.NET 5+ project other than an ASP.NET Core web application (e.g. a console application), you should also consider adding explicit references to the following NuGet packages _with the version matching your .NET runtime_. For example, if your project targets .NET 9:\n\n    dotnet add package Microsoft.Extensions.FileProviders.Physical -v 9.0.11\n    dotnet add package Microsoft.Extensions.Logging.Configuration -v 9.0.11\n    dotnet add package Microsoft.Extensions.Options.ConfigurationExtensions -v 9.0.11\n\n\u003cdetails\u003e\n  \u003csummary\u003eExplanation why this is recommended\u003c/summary\u003e\n\n  The _Karambolo.Extensions.Logging.File_ package depends on some framework libraries and references the lowest possible versions of these dependencies (e.g. the build targeting .NET 9 references _Microsoft.Extensions.Logging.Configuration_ v9.0.0). **These versions may not (mostly will not) align with the version of your application's target platform** since that may be a newer patch, minor or even major version. Thus, referencing _Karambolo.Extensions.Logging.File_ in itself may result in referencing outdated framework libraries on that particular platform (sticking to the previous example, _Microsoft.Extensions.Logging.Configuration_ v9.0.0 instead of v9.0.11).\n\n  Luckily, **in the case of ASP.NET Core this is resolved automatically** as ASP.NET Core projects already reference the correct (newer) versions of the framework libraries in question (by means of the _Microsoft.AspNetCore.App_ metapackage).\n\n  However, **in other cases (like a plain .NET Core/.NET 5+ console application) you may end up with outdated dependencies**, which is usually undesired (even can lead to issues like [this](https://github.com/adams85/filelogger/issues/19)), so you want to resolve this situation by adding the explicit package references listed above.\n\n  For more details, see [NuGet package dependency resolution](https://learn.microsoft.com/en-us/nuget/concepts/dependency-resolution).\n\u003c/details\u003e\n\n## Configuration\n\nIt's entirely possible to configure the file logger provider by code. However, you usually want to do that using an `appsettings.json` file. A minimal configuration looks like this:\n\n``` json5\n{\n  \"Logging\": {\n    \"File\": {\n      \"LogLevel\": {\n        \"Default\": \"Information\"\n      },\n      \"Files\": [\n        {\n          \"Path\": \"app.log\"\n        }\n      ]\n    }\n  }\n}\n```\n\nPlease note that you need to specify at least one log file to get anything logged. For a full reference of available settings, see the [Settings section](#settings).\n\nIf you've chosen structured logging, replace calls to `AddFile(...)` with `AddJsonFile(...)` in the following code examples.\n\n\u003cdetails\u003e\n  \u003csummary\u003eA sidenote regarding structured logging\u003c/summary\u003e\n\n  `AddJsonFile` is just a convenience method, which sets the `TextBuilder` setting to the default JSON formatter (`JsonFileLogEntryTextBuilder`) for the logger provider. You can achieve the same effect by using `AddFile` and setting `TextBuilder` (or `TextBuilderType`) to the aforementioned formatter manually. For details, see the [Settings section](#settings).\n\n  It also follows from the above that you can still override this setting in your configuration (`appsettings.json` or configurer callback) and use other formatters regardless the defaults set by `AddJsonFile`.\n\u003c/details\u003e\n\n### ASP.NET Core\n\n#### Minimal hosting model\n\n``` csharp\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Logging.AddFile(o =\u003e o.RootPath = builder.Environment.ContentRootPath);\n\nvar app = builder.Build();\n\n// ...\n\napp.Run();\n```\n\n#### Legacy hosting model\n\n``` csharp\npublic class Program\n{\n    public static void Main(string[] args)\n    {\n        CreateHostBuilder(args).Build().Run();\n    }\n\n    public static IHostBuilder CreateHostBuilder(string[] args) =\u003e\n        Host.CreateDefaultBuilder(args)\n            .ConfigureWebHostDefaults(webBuilder =\u003e\n            {\n                webBuilder\n                    .ConfigureLogging((ctx, builder) =\u003e\n                    {\n                        // The following line might be necessary when using an older version of ASP.NET Core.\n                        // builder.AddConfiguration(ctx.Configuration.GetSection(\"Logging\"));\n\n                        builder.AddFile(o =\u003e o.RootPath = ctx.HostingEnvironment.ContentRootPath);\n                    })\n                    .UseStartup\u003cStartup\u003e();\n            });\n}\n```\n\n### .NET Generic Host\n\n#### Minimal hosting model\n\n``` csharp\nvar builder = Host.CreateApplicationBuilder(args);\n\nbuilder.Logging.AddFile(o =\u003e o.RootPath = builder.Environment.ContentRootPath);\n\nvar host = builder.Build();\n\n// ...\n\nhost.Run();\n```\n\n#### Legacy hosting model\n\n``` csharp\npublic class Program\n{\n    public static void Main(string[] args)\n    {\n        CreateHostBuilder(args).Build().Run();\n    }\n\n    public static IHostBuilder CreateHostBuilder(string[] args) =\u003e\n        Host.CreateDefaultBuilder(args)\n            .ConfigureLogging((ctx, builder) =\u003e\n            {\n                // The following line might be necessary when using an older version of Microsoft.Extensions.Hosting.\n                // builder.AddConfiguration(ctx.Configuration.GetSection(\"Logging\"));\n\n                builder.AddFile(o =\u003e o.RootPath = ctx.HostingEnvironment.ContentRootPath);\n            });\n}\n```\n\n### Custom setup using DI\n\n``` csharp\n// Build configuration\nvar configuration = new ConfigurationBuilder()\n    .AddJsonFile(\"appsettings.json\")\n    .Build();\n\n// Configure DI\nvar services = new ServiceCollection();\n\nservices.AddLogging(builder =\u003e\n{\n    builder.AddConfiguration(configuration.GetSection(\"Logging\"));\n    builder.AddFile(o =\u003e o.RootPath = AppContext.BaseDirectory);\n});\n\n// Build the DI container and start logging\nawait using (var sp = services.BuildServiceProvider())\n{\n    var logger = sp.GetRequiredService\u003cILogger\u003cProgram\u003e\u003e();\n\n    // ...\n}\n```\n\n### Advanced use cases\n\n#### Using multiple providers with different settings\n\nFirst of all, you need a little bit of boilerplate code:\n\n``` csharp\n[ProviderAlias(\"File2\")]\nclass AltFileLoggerProvider : FileLoggerProvider\n{\n    public AltFileLoggerProvider(FileLoggerContext context, IOptionsMonitor\u003cFileLoggerOptions\u003e options, string optionsName) : base(context, options, optionsName) { }\n}\n```\n\nAnd a setup like this:\n\n``` csharp\nservices.AddLogging(builder =\u003e\n{\n    builder.AddConfiguration(config.GetSection(\"Logging\"));\n    builder.AddFile(o =\u003e o.RootPath = AppContext.BaseDirectory);\n    builder.AddFile\u003cAltFileLoggerProvider\u003e(configure: o =\u003e o.RootPath = AppContext.BaseDirectory);\n});\n```\n\nNow, you have two independent file logger providers. One of them picks up its configuration from the standard configuration section `File` while the other one from section `File2`, as specified by the `ProviderAlias` attribute.\n\nYou may also check out [this demo application](https://github.com/adams85/filelogger/tree/master/samples/SplitByLogLevel), which shows a complete example of this advanced setup.\n\n#### Customizing/extending the logging logic\n\nThe implementation of the file logger provides many extension points (mostly, in the form of overridable virtual methods), so you can customize its behavior and/or implement features that are not available out of the box.\n\nFor example, see [this sample application](https://github.com/adams85/filelogger/tree/master/samples/LogRotation), which extends the file logger with the ability to rotate log files.\n\n## Settings\n\n### Provider settings\n\nThere are some settings which can be configured at provider level only (`FileLoggerOptions`):\n\n|  | **Description** | **Default value** | **Notes** |\n|---|---|---|---|\n| **FileAppender** | Specifies the object responsible for appending log messages. | `PhysicalFileAppender` instance with root path set to `Environment.CurrentDirectory` | The `RootPath` shortcut property is also available for setting a `PhysicalFileAppender` with a custom root path. (This path must point to an existing directory.) |\n| **BasePath** | Path to the base directory of log files. | `\"\"` (none) | Base path is relative to (but cannot point outside of) the root path of the underlying file provider (`FileAppender.FileProvider`). (If this path does not exist, it will be created automatically.) |\n| **Files** | An array of `LogFileOptions`, which define the settings for the individual log files. | | **You need to explicitly define at least one log file**, otherwise the provider won't log anything. |\n\n### Log file settings\n\nThese settings can be configured at log file level only (`LogFileOptions`):\n\n|  | **Description** | **Default value** | **Notes** |\n|---|---|---|---|\n| **Path** | Path of the log file relative to `FileLoggerOptions.BasePath`. | | Can be a simple path or a path template.\u003cbr/\u003eTemplates specify placeholders for date and counter strings like `\"\u003cdate\u003e/app-\u003ccounter\u003e.log\"`.\u003cbr/\u003eThe file definition is ignored if `Path` is `null` or empty.\u003cbr/\u003e`Path` should be unique, **multiple file definitions with the same `Path` value may lead to erroneous behavior**. |\n| **MinLevel** | Defines log level switches for the individual file. | | Works similarly to standard [LogLevel](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/#log-level) switches but operates as an additional filter. It can only further restrict the log messages that have already passed through the standard `LogLevel` filter. |\n\n### Globally configurable log file settings\n\nThe log file settings below can be specified globally (at provider level) and individually (at log file level) as well. **File-level settings always override provider-level settings.** (In practice, this means if a setting property is `null` for a given file, the value of the same property of the provider-level settings applies. If that is `null` too, a default value is used.)\n\n|  | **Description** | **Default value** | **Notes** |\n|---|---|---|---|\n| **FileAccessMode** | Strategy for accessing log files. | `LogFileAccessMode.` `KeepOpenAndAutoFlush` | \u003cul\u003e\u003cli\u003e**KeepOpenAndAutoFlush**: Keeps open the log file until completion and flushes each entry into the file immediately.\u003c/li\u003e\u003cli\u003e**KeepOpen**: Keeps open the log file until completion but entries are flushed only when internal buffer gets full. (Provides the best performance but entries won't appear instantly in the log file.)\u003c/li\u003e\u003cli\u003e**OpenTemporarily**: Opens the log file only when an entry needs to be written and then closes it immediately. (Provides the worst performance but log files won't be locked by the process.)\u003c/li\u003e\u003c/ul\u003e |\n| **FileEncoding** | Character encoding to use. | `Encoding.UTF8` | The `FileEncodingName` shortcut property is also available for setting this option using an encoding name. |\n| **DateFormat** | Specifies the default date format to use in log file path templates. | `\"yyyyMMdd\"` | It must be a standard .NET format string which can be passed to [DateTimeOffset.ToString](https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset.tostring#System_DateTimeOffset_ToString_System_String_).\u003cbr/\u003eDate format can even be specified inline in the path template: e.g. `\"\u003cdate:yyyy\u003e/app.log\"` |\n| **CounterFormat** | Specifies the default counter format to use in log file path templates. | default integer to string conversion | It must be a standard .NET format string which can be passed to [Int32.ToString](https://learn.microsoft.com/en-us/dotnet/api/system.int32.tostring#System_Int32_ToString_System_String_).\u003cbr/\u003eCounter format can even be specified inline in the path template: e.g. `\"app-\u003ccounter:000\u003e.log\"` |\n| **MaxFileSize** | If set, new files will be created when file size limit is reached. | | `Path` must be a template containing a counter placeholder, otherwise the file size limit is not enforced. |\n| **TextBuilder** | Specifies a custom log text formatter. | `FileLogEntryTextBuilder.` `Instance` | For best performance, use a single formatter instance for all files that need the same formatting if possible.\u003cbr/\u003eThe `TextBuilderType` shortcut property is also available for setting this option using a type name. (However, when [trimming](https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-self-contained) is enabled, you need to ensure the referenced type is preserved.)\u003cbr/\u003eFor an example of usage, see [this sample application](https://github.com/adams85/filelogger/tree/master/samples/CustomLogFormat). |\n| **IncludeScopes** | Enables log scopes to be included in the output. | `false` | Works the same way as `ConsoleLogger`. |\n| **MaxQueueSize** | Defines the maximum capacity of the log processor queue (per file). | `0` (unbounded) | If set to a value greater than 0, log entries will be discarded when the queue is full, that is, when the specified limit is exceeded. |\n| **PathPlaceholderResolver** | Provides a way to hook into path template resolution. | | This is a callback that can be used to customize or extend the resolution of path template placeholders. Enables special formatting, custom placeholders, etc.\u003cbr/\u003eFor an example of usage, see [this sample application](https://github.com/adams85/filelogger/tree/master/samples/CustomPathPlaceholder). |\n\n### Sample JSON configuration\n``` json5\n{\n  \"Logging\": {\n    // global filter settings\n    \"LogLevel\": {\n      \"Default\": \"Information\"\n    },\n    // provider-level settings\n    \"File\": {\n      \"BasePath\": \"Logs\",\n      \"FileAccessMode\": \"KeepOpenAndAutoFlush\",\n      \"FileEncodingName\": \"utf-8\",\n      \"DateFormat\": \"yyyyMMdd\",\n      \"CounterFormat\": \"000\",\n      \"MaxFileSize\": 10485760,\n      \"TextBuilderType\": \"MyApp.CustomLogEntryTextBuilder, MyApp\",\n      // provider-level filters\n      \"LogLevel\": {\n        \"MyApp\": \"Information\",\n        \"Default\": \"Debug\" // provider-level filters can loosen the levels specified by the global filters\n      },\n      \"IncludeScopes\": true,\n      \"MaxQueueSize\": 100,\n      \"Files\": [\n        // a simple log file definition, which inherits all settings from the provider (will produce files like \"default-000.log\")\n        {\n          \"Path\": \"default-\u003ccounter\u003e.log\"\n        },\n        // another log file definition, which defines extra filters and overrides the Counter property (will produce files like \"2019/08/other-00.log\")\n        {\n          \"Path\": \"\u003cdate:yyyy\u003e/\u003cdate:MM\u003e/other-\u003ccounter\u003e.log\",\n          // file-level filters\n          \"MinLevel\": {\n            \"MyApp.SomeClass\": \"Warning\",\n            \"Default\": \"Trace\" // this will have no effect as file-level filters can only be more restrictive than provider-level filters!\n          },\n          \"CounterFormat\": \"00\"\n        }\n      ]\n    }\n  }\n}\n```\n\n## Troubleshooting\n\nIf you have [added the right NuGet package](#installation) and [configured logging as described above](#configuration) but your application outputs no log files, check the following:\n\n- Have you defined at least one log file in the `Files` collection? If so, have you specified the `Path` property of that file? (See also [this issue](https://github.com/adams85/filelogger/issues/12).)\n- Are the `Path` properties of the defined log files valid paths on the operating system you use? If you use path templates (that is, paths containing placeholders like `\u003cdate\u003e` or `\u003ccounter\u003e`), are they resolved to valid paths?\n- Do the combined paths of the defined log files point inside `RootPath` (or more precisely, inside the root path of `FileAppender.FileProvider`)? (See also [this issue](https://github.com/adams85/filelogger/issues/1).)\n- Does the application's process have the sufficient file system permissions to create and write files in the `\"{RootPath}/{BasePath}\"` directory? (See also [this issue](https://github.com/adams85/filelogger/issues/8#issuecomment-611755013).)\n\nIf none of these helps, you can track down the problem by observing the file logger's diagnostic events:\n\n```csharp\n// This subscription should happen before anything is logged, so place it\n// in your code early enough (preferably, before configuration of logging).\nFileLoggerContext.Default.DiagnosticEvent += e =\u003e\n{\n    // Examine the diagnostic event (print it to the debug window,\n    // set a breakpoint and inspect internal state on break, etc.)\n    Debug.WriteLine(e);\n};\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadams85%2Ffilelogger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadams85%2Ffilelogger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadams85%2Ffilelogger/lists"}