Logaffe.Serilog
0.9.0
dotnet add package Logaffe.Serilog --version 0.9.0
NuGet\Install-Package Logaffe.Serilog -Version 0.9.0
<PackageReference Include="Logaffe.Serilog" Version="0.9.0" />
<PackageVersion Include="Logaffe.Serilog" Version="0.9.0" />
<PackageReference Include="Logaffe.Serilog" />
paket add Logaffe.Serilog --version 0.9.0
#r "nuget: Logaffe.Serilog, 0.9.0"
#:package Logaffe.Serilog@0.9.0
#addin nuget:?package=Logaffe.Serilog&version=0.9.0
#tool nuget:?package=Logaffe.Serilog&version=0.9.0
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 | Versions 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. |
-
net8.0
- Logaffe.Client (>= 0.9.0)
- Serilog (>= 4.3.0)
- Serilog.Formatting.Compact (>= 3.0.0)
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 |