Lunarium.Logging.Http 1.0.3

dotnet add package Lunarium.Logging.Http --version 1.0.3
                    
NuGet\Install-Package Lunarium.Logging.Http -Version 1.0.3
                    
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="Lunarium.Logging.Http" Version="1.0.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Lunarium.Logging.Http" Version="1.0.3" />
                    
Directory.Packages.props
<PackageReference Include="Lunarium.Logging.Http" />
                    
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 Lunarium.Logging.Http --version 1.0.3
                    
#r "nuget: Lunarium.Logging.Http, 1.0.3"
                    
#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 Lunarium.Logging.Http@1.0.3
                    
#: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=Lunarium.Logging.Http&version=1.0.3
                    
Install as a Cake Addin
#tool nuget:?package=Lunarium.Logging.Http&version=1.0.3
                    
Install as a Cake Tool

Lunarium.Logging

Build NuGet Coverage License

<p align="center">English | <a href="https://github.com/Cyanflower/Lunarium.Logging/blob/main/README.ZH.md">中文</a></p>

A lightweight, high-performance structured logging library for .NET — zero external dependencies, zero-allocation filter and parser hot path, ~200 ns full call path overhead, and native AOT compatible.

Designed for developers who want structured message template logging without pulling in multiple packages or wrestling with complex sink configurations.


Why Lunarium.Logging?

Many structured logging libraries split basic functionality across multiple packages — separate installs for the core, console sink, file sink, and formatters. Lunarium.Logging ships everything needed for everyday logging in a single package: console, file (with built-in rotation), and channel sinks are all included, with no external dependencies and no assembly sprawl.

Tired of complex logger configuration? This library was built to solve a specific, practical problem: making multi-sink and condition-based routing intuitive. Isolating high-frequency modules to dedicated files, aggregating global errors into a single file, and adapting console levels for different environments — Lunarium.Logging was born to address these real production pain points. You no longer need to wrestle with complex sub-loggers, obscure filter expression languages, or piece together multiple extra packages. Here, each sink declares its own level range and context rules inline, right within the same builder chain. No separate configuration steps, no expression syntax to learn. See Filtering & Multi-Sink Routing for a concrete example.

Beyond packaging, the filter and template parser hot paths run at zero allocation (~9 ns and ~11–19 ns respectively). It integrates with Microsoft.Extensions.Logging as a full drop-in provider via the Hosting package, so adoption in an existing ASP.NET Core or Generic Host application requires no changes to how loggers are injected or used. Filter-level changes can be applied at runtime via appsettings.json hot-reload without restarting.

The optional extension packages (Hosting, Configuration, Http) are genuinely optional — the core library works on its own.


Highlights

  • Simple, intuitive API — Fluent builder, sensible defaults, no ceremony.
  • Per-sink filtering and context routing — Each sink independently declares its level range and context-based include/exclude rules, inline in the builder chain. Route modules to dedicated files, aggregate errors separately, keep the console at a different level — all without sub-loggers or expression filter syntax.
  • Zero dependencies — The core library is complete on its own. Console, file (with built-in rotation), and Channel sinks are all included. No extra packages required for everyday use.
  • Low-overhead hot path / zero-allocation filter and parser — Filter cache hits at ~9 ns, template cache hits at ~11–19 ns, both at zero allocation. Full log calls run at ~186–212 ns with 128–240 B allocated, from LogEntry construction and params boxing — not from the logging infrastructure itself. See Performance Benchmarks for details.
  • Structured message templates{Property}, {@Object}, {Value,10:F2} syntax with alignment, formatting, and destructuring support.
  • Native AOT compatible — First-class AOT and trimming support throughout. Register a source-generated JsonSerializerContext for {@Object} destructuring, or implement IDestructurable/IDestructured for fully reflection-free structured output.

Console Example

Colored console output


Packages

Package Description NuGet
Lunarium.Logging Core library — structured logging, sinks, filters NuGet
Lunarium.Logging.Hosting IHostBuilder / ILoggingBuilder integration, MEL bridge (depends on Lunarium.Logging.Configuration) NuGet
Lunarium.Logging.Configuration appsettings.json integration with hot-reload support NuGet
Lunarium.Logging.Http HTTP batch sink — supports Seq (CLEF), Loki, and custom endpoints NuGet

Quick Start

dotnet add package Lunarium.Logging
using Lunarium.Logging;

ILogger logger = new LoggerBuilder()
    .SetLoggerName("MyApp")
    .AddConsoleSink()
    .AddFileSink("logs/app.log")
    .Build();

logger.Info("Server started on port {Port}", 8080);
// [2026-03-31 12:00:00.000] [INF] [MyApp] Server started on port 8080

logger.Warning("High memory usage: {UsageMB} MB", 1024);
// [2026-03-31 12:00:00.000] [WRN] [MyApp] High memory usage: 1024 MB

logger.Error(ex, "Request failed for user {UserId}", userId);
// [2026-03-31 12:00:00.000] [ERR] [MyApp] Request failed for user 42   (stderr)
// System.Exception: Connection timeout
//    at ...

File rotation is built in — no extra packages needed:

// rotate daily, keep up to 30 files
new LoggerBuilder()
    .SetLoggerName("MyApp")
    .AddTimedRotatingFileSink("logs/app.log", maxFile: 30)
    .Build();

// rotate when file exceeds 50 MB
new LoggerBuilder()
    .SetLoggerName("MyApp")
    .AddSizedRotatingFileSink("logs/app.log", maxFileSizeMB: 50)
    .Build();

// combine both: size limit and daily rotation
new LoggerBuilder()
    .SetLoggerName("MyApp")
    .AddRotatingFileSink("logs/app.log", maxFileSizeMB: 50, rotateOnNewDay: true, maxFile: 30)
    .Build();

Structured Destructuring

// {@} — render as structured JSON
logger.Info("Order created: {@Order}", order);
// [2026-03-31 12:00:00.000] [INF] [MyApp] Order created: {"Id":42,"Total":99.99}

// {$} — force ToString()
logger.Info("Status: {$Status}", myEnum);
// [2026-03-31 12:00:00.000] [INF] [MyApp] Status: Active

// Alignment and formatting
logger.Info("Price: {Amount,10:F2} USD", 42.5);
// [2026-03-31 12:00:00.000] [INF] [MyApp] Price:      42.50 USD

Context and Scoping

// Attach a source context by string
ILogger orderLog = logger.ForContext("Order.Processor");
orderLog.Info("Processing order {Id}", orderId);
// [2026-03-31 12:00:00.000] [INF] [MyApp] [Order.Processor] Processing order 1001

// Or by type — uses the class name as the context string
ILogger payLog = logger.ForContext<PaymentService>();

// Chain contexts: "Order.Processor" → ForContext("Validator") = "Order.Processor.Validator"
ILogger validatorLog = orderLog.ForContext("Validator");
validatorLog.Info("Validating order {Id}", orderId);
// [2026-03-31 12:00:00.000] [INF] [MyApp] [Order.Processor.Validator] Validating order 1001

Use it standalone as shown above, or integrate it into ASP.NET Core / Generic Host as a full drop-in replacement for the default Microsoft.Extensions.Logging provider — see Integration with Generic Host.


Filtering & Multi-Sink Routing

Each sink carries its own FilterConfig — a level range and optional context include/exclude rules. Everything is declared inline in the builder, with no separate configuration step.

ILogger logger = new LoggerBuilder()
    .SetLoggerName("MyApp")

    // Main log — exclude the high-frequency proxy module, keep everything else
    .AddSizedRotatingFileSink(
        logFilePath: "logs/app.log", 
        maxFileSizeMB: 10, 
        maxFile: 5,
        FilterConfig: new FilterConfig
        {
            LogMinLevel = LogLevel.Info,
            ContextFilterExcludes = ["MyApp.ProxyService"],
        })

    // Dedicated file for the proxy module only
    .AddSizedRotatingFileSink(
        logFilePath: "logs/proxy.log", 
        maxFileSizeMB: 10, 
        maxFile: 10,
        FilterConfig: new FilterConfig
        {
            LogMinLevel = LogLevel.Info,
            ContextFilterIncludes = ["MyApp.ProxyService"],
        })

    // Error aggregation — all modules, Error and above only
    .AddSizedRotatingFileSink(
        logFilePath: "logs/error.log", 
        maxFileSizeMB: 10, 
        maxFile: 5,
        FilterConfig: new FilterConfig 
        { 
            LogMinLevel = LogLevel.Error 
        })

    // Console — Debug and above during development
    .AddConsoleSink(
        FilterConfig: new FilterConfig 
        { 
            LogMinLevel = LogLevel.Debug 
        })
    .Build();

ContextFilterIncludes and ContextFilterExcludes match by prefix, so "MyApp.ProxyService" catches MyApp.ProxyService, MyApp.ProxyService.Handler, and any deeper context derived from it.

For a full production example with audit logs, warning-only sinks, and a channel sink for UI broadcasting, see Advanced Sink Configuration.


Integration with Generic Host

dotnet add package Lunarium.Logging.Hosting
Host.CreateDefaultBuilder(args)
    .UseLunariumLog(sinks => sinks
        .AddConsoleSink()
        .AddFileSink("logs/app.log"))
    .Build()
    .Run();

Use ILogger<T> from DI as usual — Lunarium.Logging acts as the MEL provider.


appsettings.json Configuration

dotnet add package Lunarium.Logging.Configuration
{
  "LunariumLogging": {
    "GlobalConfig": {
      "TextTimestampMode": "Custom",
      "TextCustomTimestampFormat": "yyyy-MM-dd HH:mm:ss.fff"
    },
    "LoggerConfigs": [
      {
        "LoggerName": "MyApp",
        "ConsoleSinks": {
          "main-console": {
            "IsColor": true,
            "FilterConfig": {
              "LogMinLevel": "Info"
            }
          }
        },
        "FileSinks": {
          "app": {
            "LogFilePath": "Logs/app.log",
            "MaxFileSizeMB": 10.0,
            "RotateOnNewDay": true,
            "MaxFile": 30,
            "ToJson": true
          }
        }
      }
    ]
  }
}

Supports hot-reload — filter changes apply at runtime without restarting.


HTTP Sinks (Seq / Loki)

dotnet add package Lunarium.Logging.Http
// Seq (CLEF format)
new LoggerBuilder()
    .SetLoggerName("MyApp")
    .AddSeqSink(
        httpClient:    new HttpClient(),
        seqEndpoint:   "http://localhost:5341/api/events/raw",
        apiKey:        "your-seq-api-key")
    .Build();

// Loki
new LoggerBuilder()
    .SetLoggerName("MyApp")
    .AddLokiSink(
        httpClient:    new HttpClient(),
        lokiEndpoint:  "http://localhost:3100/loki/api/v1/push",
        labels:        new Dictionary<string, string>
        {
            ["app"]         = "my-service",
            ["environment"] = "production",
        })
    .Build();

Provides lightweight HTTP sinks using native HttpClient for batching logs to Seq (CLEF format) or Grafana Loki.


Performance

Benchmarks run on i7-8750H, .NET 10.0, Release mode.

Hot path (filter + parser cache hits)

Scenario Time Allocations
Filter cache hit ~9 ns 0
Template cache hit ~11–19 ns 0
Full log call (no properties) ~186 ns 113 B
Full log call (5 properties) ~212 ns 227 B

Writer rendering

Format Time Allocations
Text / Color ~320–600 ns 32 B
JSON ~470–750 ns 64 B

The 32–64 B allocation in rendering is a minimal, fixed cost (related to pool management or internal struct wrapping), completely independent of the number of properties or message length. Filter and parser caches operate at strict zero allocation on the hot path.

For detailed analysis and cross-platform comparisons, see the benchmark reports:


Examples

Full annotated examples are available in the example/ directory, each provided as a Markdown guide and a raw C# file.

Example Description
Quick Start Log levels, exceptions, message template syntax, ForContext
Sink Configuration All sink types, FilterConfig, ISinkConfig, GlobalConfigurator
Hosting Integration Generic Host, DI, MEL bridge, UseLunariumLog
Configuration Integration appsettings.json binding, hot-reload
HTTP Sink Seq, Loki, custom serializers, AddHttpSink
Advanced Usage Custom ILogTarget, IDestructurable/IDestructured, AOT, LoggerManager

Raw C# source files (no Markdown) are in example/RawCSharp/.


Native AOT

Lunarium.Logging is marked IsAotCompatible=true and passes AOT/trimming analysis at build time.

{@Object} destructuring relies on JsonSerializer under the hood. In AOT environments, register a source-generated JsonSerializerContext so serialization is fully reflection-free. Types not registered will silently fall back to ToString() — no runtime exception is thrown.

Step 1 — declare a JsonSerializerContext for your types:

[JsonSerializable(typeof(Order))]
[JsonSerializable(typeof(UserRecord))]
[JsonSerializable(typeof(List<Order>))]
internal partial class MyAppJsonContext : JsonSerializerContext { }

Step 2 — register it before calling Build():

GlobalConfigurator.Configure()
    .UseJsonTypeInfoResolver(MyAppJsonContext.Default)
    .Apply();

// Multiple contexts can be combined:
// .UseJsonTypeInfoResolver(
//     JsonTypeInfoResolver.Combine(MyAppJsonContext.Default, AnotherContext.Default))

Alternatively, implement IDestructurable or IDestructured for structured output that requires no serializer at all.


Requirements

  • .NET 8, 9, or 10
  • No external NuGet dependencies (core library)

Test Coverage

776 tests across three test projects.

Project Tests
Lunarium.Logging.Tests 653
Lunarium.Logging.Http.Tests 102
Lunarium.Logging.IntegrationTests 21
Metric Coverage
Line 92.1%
Branch 88.8%
Method 98.7%

Per-package breakdown:

Package Line Coverage
Lunarium.Logging 91.3%
Lunarium.Logging.Hosting 100%
Lunarium.Logging.Configuration 98.1%
Lunarium.Logging.Http 92.8%

Attributions

The Structured Message Template syntax supported by this library, as well as its underlying abstract token model (e.g., MessageTemplate, TextToken, PropertyToken), are conceptually inspired by the excellent design of Serilog. See ATTRIBUTIONS.md for full details.


License

Apache 2.0 — see LICENSE.

Product Compatible and additional computed target framework versions.
.NET 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 is compatible.  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. 
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
1.0.3 111 4/1/2026
1.0.2 103 3/31/2026
1.0.1 99 3/31/2026
1.0.0 100 3/31/2026