Logaffe.Serilog 0.9.0

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

Logaffe.Serilog

A Serilog sink that delivers to a logaffe installation — a self-hostable, central logging tool for a single operator and their AI agent.

logaffe ingests CLEF, the format Serilog.Formatting.Compact already writes. The sink is therefore configuration rather than a mapping layer: nothing is translated on the way out, and what an operator searches is what Serilog produced.

Use

Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .WriteTo.Logaffe(
        new Uri("https://logs.example.com"),
        "logaffe_ingest_…")
    .CreateLogger();

Keep the console or file sink. logaffe is additive — it does not replace the log the application already writes, and that is what makes a lost delivery affordable.

A delivery never names a project: the token is the project. The address is scheme and host as the operator reaches the installation — the ingest path is appended and is not a setting.

Naming the instance

Three replicas of one service are only separable if they say who they are. The default is Environment.MachineName; the overload takes something else, and null leaves it off:

.WriteTo.Logaffe(delivery, instance: Environment.GetEnvironmentVariable("HOSTNAME"))

An event that already carries its own instance property keeps it.

Everything else

.WriteTo.Logaffe(
    new EntryDeliveryOptions
    {
        Installation  = new Uri("https://logs.example.com"),
        IngestToken   = "logaffe_ingest_…",
        QueueCapacity = 50_000,
    },
    restrictedToMinimumLevel: LogEventLevel.Information)

restrictedToMinimumLevel and levelSwitch work as they do on any sink.

From appsettings.json

{
  "Serilog": {
    "WriteTo": [
      {
        "Name": "Logaffe",
        "Args": {
          "installation": "https://logs.example.com",
          "ingestToken": "logaffe_ingest_…",
          "instance": "ops/example",
          "queueCapacity": 50000,
          "batchInterval": "00:00:01",
          "flushTimeout": "00:00:05",
          "deliveryTimeout": "00:00:10"
        }
      }
    ]
  }
}

Every setting is a parameter of its own on this route, because a settings binder reads parameters and can construct nothing. Spell them as they are named above: an argument it cannot place is dropped rather than refused, and nothing says so.

What it promises

Fire-and-forget. A bounded in-memory queue, dropping the oldest entries when full; never throwing into the calling application and never blocking it; a flush with a timeout when the logger is closed. An unbounded queue would turn a logging outage into an application outage, which is the thing this exists to prevent.

Failures go to SelfLog, Serilog's own channel for what a sink cannot report through the logger it is part of — reporting a failed delivery through Serilog would hand it straight back to this sink. Turn it on while setting things up:

Serilog.Debugging.SelfLog.Enable(Console.Error);

That holds however the sink was configured, including the overloads taking EntryDeliveryOptions. Set OnFailure on those options to send the reports somewhere else instead; what you set is kept.

Each one is a single line — the exception's type and message, not its stack trace — and a failure that keeps repeating is written once and then at most every five minutes, with one closing line when deliveries get through again. An OnFailure of your own receives the Exception itself and may render it however it likes.

A token the installation refuses is reported as the sink is configured, in one line, before anything has been logged. Nothing else about the probe is audible: an installation that is merely unreachable is left to the delivery path to report.

Call Log.CloseAndFlush() on shutdown, as with any Serilog sink. Without it the application's last entries never leave.

What logaffe does with what you send

The server renders the template. logaffe stores @mt and the properties, not a pre-rendered string, and substitutes only those placeholders for which a property of the same name arrived — everything else stays character for character. The rule is narrower than Serilog's on purpose: log content is untrusted, and Serilog's escaping would rewrite an application that logs a raw request body. A format specifier such as {Elapsed:0.000} is read for the name and then dropped, because values are stored as they arrived.

SourceContext, TraceId and SpanId are promoted to indexed fields when present — and Serilog sets all three on its own in an ordinary ASP.NET Core application, so the most useful filter for cutting framework noise costs you nothing.

Exceptions land in @x as text, stored as delivered and never parsed, so they can be shown, collapsed, or searched on their own.

Full detail: docs/ingestion.md.

Requirements

.NET 8 or later. MIT licensed.

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 was computed.  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 was computed.  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
0.9.0 90 9/6/2026
0.8.0 92 9/6/2026
0.7.1 101 9/6/2026
0.7.0 93 9/6/2026
0.6.0 1,191 8/23/2026
0.5.0 97 8/23/2026
0.4.0 103 8/20/2026
0.3.0 100 8/19/2026
0.2.8 99 8/19/2026
0.2.7 89 8/19/2026
0.2.6 95 8/19/2026
0.2.5 94 8/19/2026
0.2.4 110 8/19/2026
0.2.3 379 8/19/2026
0.2.2 94 8/18/2026
0.2.1 96 8/17/2026
0.2.0 101 8/17/2026
0.1.1 120 8/13/2026
0.1.0 100 8/13/2026
0.1.0-alpha.1 64 8/13/2026