PicoLog.Abs 2026.9.3

There is a newer version of this package available.
See the version list below for details.
dotnet add package PicoLog.Abs --version 2026.9.3
                    
NuGet\Install-Package PicoLog.Abs -Version 2026.9.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="PicoLog.Abs" Version="2026.9.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PicoLog.Abs" Version="2026.9.3" />
                    
Directory.Packages.props
<PackageReference Include="PicoLog.Abs" />
                    
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 PicoLog.Abs --version 2026.9.3
                    
#r "nuget: PicoLog.Abs, 2026.9.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 PicoLog.Abs@2026.9.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=PicoLog.Abs&version=2026.9.3
                    
Install as a Cake Addin
#tool nuget:?package=PicoLog.Abs&version=2026.9.3
                    
Install as a Cake Tool

PicoLog

High-performance structured logging for .NET Native AOT.

NuGet License: MIT

English | 简体中文 | 日本語 | Español | Português | 繁體中文 | 한국어 | Français | Deutsch | Русский


Performance

PicoLog vs Microsoft.Extensions.Logging on .NET 10.0.5 (Windows 10, X64, Release).

Test Case PicoLog (ns) MEL Baseline (ns) Speedup
Cached message, single write 303 434 1.43x
Cached + 1 scope 402 434 1.08x
Cached + 4 properties 324 434 1.34x
Timestamp acquisition only 59 434 7.36x
LogEntry allocation only 46 434 9.33x

Summary: 13/30 wins, average 3.51x speedup, max 25.76x.

PicoLog is faster than MEL on cached-message and allocation-heavy paths where source generation eliminates boxing and reflection. On single-message throughput (N=1), PicoLog is deliberately slower — roughly 700ns vs MEL's 200ns per call. This is not a bug; it's the cost of an async-first pipeline architecture.

Design Rationale: Async-First Pipeline

Every Log() call traverses the same pipeline regardless of whether you call the synchronous or asynchronous overload:

Log() → CreateEntry → CategoryPipeline → Channel → ProcessEntriesAsync → DispatchEntryAsync → Sink

This is fundamentally different from MEL's synchronous model:

Log() → Logger.Log() → foreach (logger in loggers) → logger.Log()  // direct, no channel
Aspect PicoLog (async-first) MEL (sync-first)
Delivery Guaranteed through bounded channel Best-effort; sync discard can drop if logger throws
Backpressure DropOldest / DropWrite / Wait — configurable per factory None — calling thread blocks or message is lost
Flush FlushAsync() drains all pending entries before shutdown No flush API; Dispose() is instant
Timestamp Precise — captured at CreateEntry() before enqueue Captured inside logger, may be delayed
Thread safety Channel-based; no lock on hot path Each logger manages its own synchronization
Per-call overhead ~500–800ns (Channel + dispatcher + sink) ~100–200ns (direct dispatch)

PicoLog chose the async pipeline because production logging systems need guaranteed delivery, backpressure, and graceful shutdown more than they need the absolute lowest single-message latency. If you're logging at 1M msg/s, the per-message overhead dominates. If you're logging at 1K msg/s with flush semantics, PicoLog's guarantees matter more.

For direct throughput comparisons across all scenarios, see benchmark results.


Quick Start

dotnet add package PicoLog
using PicoLog;
using PicoLog.Abs;

var sink = new ColoredConsoleSink(new ConsoleFormatter());
using var factory = new LoggerFactory([sink],
    new LoggerFactoryOptions { MinLevel = LogLevel.Info });
var logger = factory.CreateLogger("App");
logger.Info("Application started");

Log Levels

Level Value Usage
Emergency 0 System is unusable
Alert 1 Action must be taken immediately
Critical 2 Critical conditions
Error 3 Error conditions
Warning 4 Warning conditions
Notice 5 Normal but significant
Info 6 Informational messages
Debug 7 Debug-level messages
Trace 8 Detailed diagnostic tracing
None 255 Disables all logging

Core API

LoggerFactory

using var factory = new LoggerFactory(
    sinks: [new ColoredConsoleSink(new ConsoleFormatter())],
    options: new LoggerFactoryOptions
    {
        MinLevel = LogLevel.Info,
        QueueCapacity = 65535,
        QueueFullMode = LogQueueFullMode.DropOldest,
        SyncWriteTimeout = TimeSpan.FromMilliseconds(250),
        ShutdownTimeout = TimeSpan.FromSeconds(5)
    });

ILogger logger = factory.CreateLogger("MyComponent");

Typed Logger

public sealed class OrderService(ILoggerFactory factory)
{
    private readonly ILogger<OrderService> _logger = new Logger<OrderService>(factory);

    public void Process(Order order)
    {
        _logger.Info($"Processing order {order.Id}");
    }
}

Extension Methods

Level-specific convenience helpers provided as extension methods on ILogger. Not part of the interface contract — consumers only implement the 8 core interface methods.

logger.Trace("Detailed diagnostic");
logger.Debug("Debug information");
logger.Info("Application event");
logger.Notice("Significant event");
logger.Warning("Unexpected condition");
logger.Error("Operation failed", exception);
logger.Critical("Severe failure");
logger.Alert("Immediate action required");
logger.Emergency("System is down");

Each has a FormattableString overload for deferred formatting and an EventId overload for structured event identification. Async variants (TraceAsync, InfoAsync, etc.) are available with CancellationToken support.

Message Templates

// FormattableString — template and arguments preserved for deferred formatting
logger.Info($"Processing order {orderId} for {customer}");

// Structured properties — explicit key-value pairs
logger.LogStructured(LogLevel.Error, "Payment failed",
    properties: [new("OrderId", orderId), new("Amount", amount)],
    exception: ex);

// EventId — tagged for monitoring and alerting
logger.Info(new EventId(1001, "OrderPlaced"), $"Order {orderId} placed");

Source-Generated Messages

The [PicoLogMessage] generator is embedded in PicoLog.Abs (no extra package). Reference PicoLog.Abs and the analyzer activates automatically:

public static partial class AppLogs
{
    [PicoLogMessage(LogLevel.Info, EventId = 1001, Message = "Order {OrderId} placed")]
    public static partial void OrderPlaced(this ILogger logger, string orderId);

    [PicoLogMessage(LogLevel.Error, EventId = 2001,
        EventName = "PaymentFailed", Message = "Payment of {Amount} failed")]
    public static partial void PaymentFailed(this ILogger logger, decimal amount);
}

logger.OrderPlaced("ORD-12345");
logger.PaymentFailed(99.99m);

The generator emits Log calls with FormattableString at compile time — zero runtime reflection.

Sinks

Built-in

// Plain console
new ConsoleSink(new ConsoleFormatter())

// Color-coded console (Trace=Gray, Debug=Cyan, Info=Green, Error=Red, ...)
new ColoredConsoleSink(new ConsoleFormatter())

// File with batching
new FileSink(new ConsoleFormatter(), new FileSinkOptions
{
    FilePath = "logs/app.log",
    BatchSize = 32,
    QueueCapacity = 4096,
    FlushInterval = TimeSpan.FromMilliseconds(100)
})

Custom

public sealed class CustomSink : ILogSink
{
    public Task WriteAsync(LogEntry entry, CancellationToken ct = default)
    {
        // Write to your backend
        return Task.CompletedTask;
    }
    public ValueTask DisposeAsync() => ValueTask.CompletedTask;
}

Implement IFlushableLogSink to support explicit flush:

public sealed class BufferedSink : IFlushableLogSink
{
    public Task WriteAsync(LogEntry entry, CancellationToken ct = default) { /* buffer */ }
    public ValueTask FlushAsync(CancellationToken ct = default) { /* flush buffer */ }
    public ValueTask DisposeAsync() => ValueTask.CompletedTask;
}

Custom Formatter

public sealed class JsonFormatter : ILogFormatter
{
    public string Format(LogEntry entry)
    {
        return JsonSerializer.Serialize(new
        {
            entry.Timestamp,
            Level = entry.Level.ToString(),
            entry.Category,
            entry.Message,
            entry.Exception?.Message
        });
    }
}

Logging Scopes

using (logger.BeginScope(new { RequestId = "abc-123" }))
{
    logger.Info("Processing request");
    // Logs include scope context
}

Configuration

Queue Behavior

Three backpressure strategies when the queue is full:

Mode Behavior
DropOldest (default) Evict the oldest entry, accept the new one
DropWrite Discard the new entry
Wait Block or await until space is available
var options = new LoggerFactoryOptions
{
    QueueCapacity = 10000,
    QueueFullMode = LogQueueFullMode.Wait,
    SyncWriteTimeout = TimeSpan.FromMilliseconds(500)
};

Category Filtering

var options = new LoggerFactoryOptions
{
    MinLevel = LogLevel.Info,  // global default
    FilterRules =
    {
        new LogFilterRule("Microsoft", LogLevel.Warning),
        new LogFilterRule("MyApp.Data", LogLevel.Debug)
    }
};

Rules are applied in reverse order — the last matching rule wins.

Dropped Message Notification

var options = new LoggerFactoryOptions
{
    QueueCapacity = 1000,
    OnMessagesDropped = (category, count) =>
        Console.Error.WriteLine($"Dropped {count} messages for {category}")
};

Timestamp Control

var options = new LoggerFactoryOptions
{
    TimestampProvider = TimeProvider.System  // default; inject fake for testing
};

Flush & Shutdown

using var factory = new LoggerFactory(sinks, options);

// Flush without shutdown — drains pending entries, factory remains usable
await factory.FlushAsync();

// Dispose drains all pipelines and sinks, aggregates errors
await factory.DisposeAsync();

Set ShutdownTimeout to bound the drain:

var options = new LoggerFactoryOptions
{
    ShutdownTimeout = TimeSpan.FromSeconds(5)  // TimeSpan.Zero = wait indefinitely
};

On shutdown timeout, sinks receive a drain CancellationToken. The factory waits for the processing pipeline to finish draining before disposing resources.

DI Integration (PicoLog.DI)

dotnet add package PicoLog.DI
using PicoLog.DI;

container.AddPicoLog(o =>
{
    o.MinLevel = LogLevel.Info;
    o.WriteTo.ColoredConsole();
    o.WriteTo.File("logs/app.log");
    o.WriteTo.File(opts =>
    {
        opts.FilePath = "logs/errors.log";
        opts.BatchSize = 1;  // write immediately
    });
    o.Factory.QueueFullMode = LogQueueFullMode.DropOldest;
});

// Resolve typed logger
var logger = container.CreateScope().GetService<ILogger<Program>>();

Consume Registered Sinks

// Register a custom sink in DI, then tell PicoLog to use it
container.RegisterSingleton<ILogSink>(_ => new CustomSink());

container.AddPicoLog(o =>
{
    o.ReadFrom.RegisteredSinks();  // discover ILogSink from container
    o.WriteTo.ColoredConsole();    // additional owned sinks
});

Custom Sink Registration

container.AddPicoLog(o =>
{
    o.WriteTo.Sink(new CustomSink());
    o.WriteTo.Sink(() => new LazySink());         // factory
    o.WriteTo.Sink(fmt => new FormattedSink(fmt)); // formatter-aware factory
});

OpenTelemetry Metrics

PicoLogMetrics exposes standard counters via System.Diagnostics.Metrics:

Metric Description
picolog.entries.enqueued Total entries accepted
picolog.entries.dropped Entries dropped due to queue full
picolog.sinks.failures Sink write failures
picolog.writes.rejected_after_shutdown Writes rejected after factory shutdown
picolog.queue.entries Current queued entries (observable gauge)
picolog.shutdown.drain.duration Shutdown drain time in ms (histogram)

All metrics are AOT-compatible and integrate with OpenTelemetry collectors.

Packages

Package TFM Description
PicoLog net10.0 Logging runtime: LoggerFactory, sinks, formatters
PicoLog.Abs net10.0 ILogger, ILogSink, LogLevel, LogEntry, EventId
PicoLog.Gen netstandard2.0 [PicoLogMessage] source generator (embedded in PicoLog.Abs; not a standalone package)
PicoLog.DI net10.0 DI integration (AddPicoLog, WriteTo, ReadFrom)

← Back to PicoInfra

Product Compatible and additional computed target framework versions.
.NET 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.
  • net10.0

    • No dependencies.

NuGet packages (8)

Showing the top 5 NuGet packages that depend on PicoLog.Abs:

Package Downloads
PicoLog

High-performance structured logging with compile-time message templates and built-in sinks.

PicoLog.DI

PicoDI integration for PicoLog registration (AddPicoLog, WriteTo, ReadFrom).

PicoLog.Json

Structured JSON log sink for PicoLog via PicoJetson

PicoNode.Http

HTTP protocol layer for PicoNode. Implements HTTP/1.1, HTTP/2 (including h2c upgrade), WebSocket (RFC 6455), and HPACK (RFC 7541) with Huffman encoding. Includes request/response serialization and routing.

PicoNode.Web

Web application middleware framework for PicoNode. Provides WebApp builder, RadixTree router, middleware pipeline (CORS, compression, caching, security headers, static files), Server-Sent Events, and multipart form data parsing.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2026.10.0 362 9/18/2026
2026.9.3 454 9/15/2026
2026.9.2 120 9/14/2026
2026.9.1 255 8/24/2026
2026.9.0 531 8/23/2026
2026.8.9 142 8/22/2026
2026.8.8 137 8/22/2026
2026.8.7 131 8/21/2026
2026.8.6 133 8/21/2026
2026.8.5 268 8/16/2026
2026.8.4 187 8/13/2026
2026.8.3 154 8/8/2026
2026.8.2 195 8/5/2026
2026.8.1 145 8/5/2026
2026.8.0 135 8/5/2026
2026.7.3 634 7/12/2026
2026.7.2 151 7/12/2026
2026.7.1 154 7/12/2026
2026.6.17 163 7/12/2026
2026.6.16 157 7/9/2026
Loading failed