Serilog: structured logging for .NET, from LoggerConfiguration to sinks
Simple .NET logging with fully-structured events
At a glance
- What is it?
- Serilog records named event properties instead of formatted strings, and routes them through a pluggable sink pipeline. It suits distributed and asynchronous .NET systems, but the core package is only half the story: sinks and configuration packages are separate NuGet installs.
- Who is it for?
- Adopt Serilog when your logs need to be queried by property rather than grepped as text, and when you accept that each output is a separate NuGet package with its own release cadence. Do not adopt it if you only want a drop-in replacement for an existing text logger and will never search by field: the message template syntax and sink wiring are extra work for no gain.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 17 days ago.
- What is it written in?
- Mainly C#, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem Serilog solves: logs as events, not strings
Most .NET logging libraries format a message into a string at the call site. Once that string is written, the structure is gone. If you log "Processed order 1234 in 34 ms", finding every order that took longer than a second means parsing text with a regular expression, and the moment someone changes the message wording, the query breaks.
Serilog takes the opposite position. The README describes it as "built from the ground up to record structured event data". A call such as log.Information("Processed {@Position} in {Elapsed} ms", position, elapsedMs) does not build a string first. It captures the values associated with the named parameters Position and Elapsed and carries them through the pipeline as properties alongside the timestamp, level and message. The @ prefix on Position tells Serilog to serialize the object rather than call ToString() on it.
The audience is therefore not every .NET application. A small console tool that prints progress to a terminal gains nothing from this. The README is explicit that the structured support "shines when instrumenting complex, distributed, and asynchronous applications and systems", which is where a back end that indexes properties pays for itself: searches and analysis happen without log parsing.
Message templates, the @ operator and the sink pipeline
Serilog's message templates are a small DSL that extends .NET format strings with named as well as positional parameters. The names become property names in the resulting event. That is the whole mechanism, and it is worth understanding before choosing sinks, because the sink decides what happens to those properties.
The library calls its output pipeline "format-agnostic". The same LogEvent can be rendered as human-readable text for a console or file, or emitted as compact JSON by a formatting package. The README shows the JSON rendering of a two-property event:
{"Position": {"Latitude": 25, "Longitude": 134}, "Elapsed": 34}The text rendering keeps the properties visible too, so you are not forced to give up readable files:
09:14:22 [INF] Processed {"Latitude": 25, "Longitude": 134} in 34 ms.Two design points in the README are easy to skim past. First, Logger objects are described as "zero-shared-state", with the static Log class as an optional global. That means you can construct independent loggers for different components instead of funnelling everything through one process-wide instance. Second, enrichment is a distinct stage: scoped properties via LogContext, thread and process identifiers, and domain-specific correlation ids such as HttpRequestId can be attached to events before they reach a sink. Correlation ids are what make the distributed case work, since they let you reassemble one logical operation across services.
Installing Serilog and writing your first events
Serilog is installed from NuGet, and the README is direct that the core package alone will not show you anything: "To view log events, one or more sinks need to be installed as well". The getting-started example uses the pretty-printing console sink and a rolling file set.
dotnet add package Serilog
dotnet add package Serilog.Sinks.Console
dotnet add package Serilog.Sinks.FileThe simplest setup assigns a logger to the static Log class, normally in Program.cs. The configuration below writes to the console and to a file that rolls daily and also rolls when it hits a size limit, then flushes on the way out so buffered events are not dropped at exit.
using Serilog;
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File("log.txt",
rollingInterval: RollingInterval.Day,
rollOnFileSizeLimit: true)
.CreateLogger();
try
{
const string name = "Serilog";
Log.Information("Hello, {Name}!", name);
throw new InvalidOperationException("Oops...");
}
catch (Exception ex)
{
Log.Error(ex, "Unhandled exception");
}
finally
{
await Log.CloseAndFlushAsync();
}Running this prints the hello line at information level, then the caught exception at error level with the exception attached. The finally block matters: without CloseAndFlushAsync, events still sitting in a sink's buffer when the process exits are simply gone. The README points to a runnable example application under the Getting Started topic in the wiki if you want a fuller project to compare against.
Where Serilog is the wrong tool
The packaging model is the first real cost. Every destination is a separate package with its own repository and its own release schedule. That is a deliberate split, and it keeps the core small, but it means an upgrade is rarely a single version bump. Serilog itself moved from v4.3.0 in May 2025 to v4.3.1 in February 2026 and v4.4.0 in July 2026; sinks version independently of that, so a compatible-looking set of packages is something you verify rather than assume.
The second limitation is conceptual. Structured logging only pays off if something downstream can use the structure. If your logs go to a plain text file that nobody queries by field, you have taken on message template syntax and sink configuration for output that looks much like what a traditional logger would have produced. The README's own text rendering is the proof: it is friendly, but it is still text.
Third, the README does not document rollback behaviour, nor does it describe what happens to in-flight events when a sink fails during a write. The troubleshooting guide in the wiki is where the project directs you when Serilog "isn't working the way you expect", which is a fair signal that diagnosis is a documented topic rather than something the README covers. If you need a guarantee about delivery under sink failure, that guarantee is not stated in the README and should be established from the specific sink's documentation before you depend on it.
Serilog versus log4net and NLog
log4net and NLog are the two comparisons people search for most often. The difference is not a feature checklist, it is the data model. Both alternatives grew up around formatted text messages with appenders or targets attached; Serilog was designed so that named properties survive all the way to the sink, and the README states that this is what distinguishes it from other logging libraries.
That shows up in the API. A log4net or NLog call typically interpolates values into a message. A Serilog call names them, and the @ operator decides whether an object is serialized or stringified. If you later want every event carrying a particular property, that is a filter on the event store rather than a regex over lines.
The cost of the difference is migration. Existing log statements have to be rewritten into message templates, and the configuration surface is different: Serilog offers a discoverable C# configuration syntax plus optional XML or JSON configuration packages, which map onto appsettings.json in ASP.NET Core rather than onto an app.config or NLog.config file. The README also notes rich integration with ASP.NET Core through a separate package. If your application is already heavily invested in one of the others and its logs are consumed as text, the migration buys you little.
Maintenance, licensing and what an upgrade actually costs
The repository is not archived, and the last push was on 2026-09-13, so the project is being worked on rather than frozen. The most recent release listed is v4.4.0 from 2026-07-10. The README describes the project as community-backed and points to Stack Overflow as the best place to start with questions, with the GitHub issue tracker reserved for reproducible bug reports and detailed feature requests. That split is worth respecting: usage questions filed as issues are likely to be redirected.
Serilog is licensed under Apache-2.0. That is a permissive licence, but it is not legal advice and the terms, including any patent grant and notice requirements, are set out in the LICENSE file at the repository root rather than in the README.
The upgrade cost is dominated by the sink graph, not the core library. A realistic version bump means checking the release notes for the core version, then checking each sink and configuration package you depend on for its own compatibility statement. The README links to the releases page for exactly this purpose when upgrading from an earlier version. If you use the XML or JSON configuration packages, the configuration schema is another surface that can shift independently of the code that reads it.
Editorial conclusion
Adopt Serilog when your logs need to be queried by property rather than grepped as text, and when you accept that each output is a separate NuGet package with its own release cadence. Do not adopt it if you only want a drop-in replacement for an existing text logger and will never search by field: the message template syntax and sink wiring are extra work for no gain. Before committing, verify that a sink exists for your target back end, check the release notes for the version you are upgrading from, and confirm that Log.CloseAndFlushAsync is called on your shutdown path so buffered events are not lost.
Frequently asked questions
What is Serilog?
Serilog is a diagnostic logging library for .NET applications that records structured event data using message templates with named parameters. It runs on recent .NET platforms and writes events through pluggable sinks such as console, file and log servers.
Is Serilog a NuGet package?
Yes. The README states that Serilog is installed from NuGet, and sinks are installed as additional packages, for example Serilog.Sinks.Console and Serilog.Sinks.File.
Which is better, Serilog or NLog?
Serilog is built from the ground up to record structured event data, so named properties survive to the sink instead of being formatted into a string at the call site. NLog is not covered in the Serilog README, so the practical difference to weigh is whether your logs are consumed as queryable events or as text.
What are the benefits of using Serilog?
Events carry named properties that back ends can record without log parsing or regular expressions, and the README notes the library is efficient when enabled with very low overhead when a logging level is switched off. Enrichment can attach scoped properties and correlation ids such as HttpRequestId.
How do I install Serilog?
Add the core package with dotnet add package Serilog, then add at least one sink such as Serilog.Sinks.Console or Serilog.Sinks.File. The README notes that without a sink, there is nothing to view the events.
How do I set up Serilog in a .NET application?
Build a LoggerConfiguration in Program.cs, call WriteTo.Console or WriteTo.File, and assign the result to Log.Logger. The README's example wraps the program in a try/catch/finally and calls await Log.CloseAndFlushAsync() so all logs are written before the app exits.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/serilog-serilog)