serilog
Skill1 starsChanged 7 days agoMITView on GitHub
How logging works with Serilog - registration with AddSerilog, configuration from appsettings, sinks per environment, logs over OTLP, the enrichers and properties on every event, the request log, the LoggingBehaviour, who logs an unexpected exception, levels, and source-generated log messages. Use when adding a log line, changing log configuration or levels, adding a property, or looking for where logs go.
2 other projects use a version of this.
Adopted by
Most recently taken up by
- codewithmukesh/dotnet-claude-kittheir own version, first seen Nov 8, 2026
- Resgrid/Coretheir own version, first seen Oct 8, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- codewithmukesh/dotnet-claude-kit★ 808same name
- Resgrid/Core★ 238same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository40 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- add-a-use-caseSkill
- api-conventionsSkill
- branches-and-pull-requestsSkill
- cancellationSkill
- ci-for-your-solutionSkill
- ci-pipelineSkill
- clean-architecture-layersSkill
- configuration-and-environmentsSkill
- coverageSkill
- csharp-house-styleSkill
- data-access-ef-coreSkill
- database-migrationsSkill
- docker-and-composeSkill
- docs-and-voiceSkill
- errors-and-exceptionsSkill
- expose-a-use-caseSkill
- family-syncSkill
- health-checksSkill
- integration-testsSkill
- json-serializationSkill
- mapping-with-mapperlySkill
- mcp-inspectorSkill
- mediatorSkill
- optional-token-authSkill
- packages-and-dependenciesSkill
- pagingSkill
- presentation-integration-testsSkill
- releasesSkill
- sample-dataSkill
- security-findingsSkill
- telemetrySkill
- template-authoringSkill
- test-designSkill
- time-providerSkill
- unit-testsSkill
- validationSkill
- writing-a-skillSkill
- xml-docs-and-openapiSkill
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 1 is distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 23
Every event carries these properties:
- Very specific: line 49
Write each message as a [LoggerMessage] method on a partial class, with named holes in the template.
- Very specific: line 52
Add a sink or change a level in the appsettings files.
- Very specific: line 97
Use string interpolation in a log message ($"Created {id}").
- Very specific: line 98
Call logger.LogInformation(...) and its friends directly.
- Very specific: line 104
Add a second request logger, such as UseHttpLogging.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Serilog
The rule
Serilog is the logger. Application and Infrastructure code log through Microsoft.Extensions.Logging.ILogger<T>, and Serilog does the writing. The presentation layer's startup registers it with builder.Services.AddSerilog(...), which reads the Serilog section of the appsettings files and also sets the static Log.Logger that Program.cs uses for the start and fatal messages.
| Environment | Sink | Where to look |
local | Console, with the output template in appsettings.local.json | The terminal |
docker | Console, and OTLP through the collector to the Aspire dashboard | The Aspire dashboard, on the port docs/RUN.md lists, or docker compose logs |
| Lambda, anywhere | Console, as compact JSON, and OTLP when the endpoint is set | CloudWatch Logs, or docker compose logs for the emulator, and the Aspire dashboard in Docker Compose |
Any, when OTEL_EXPORTER_OTLP_ENDPOINT is set | Also OTLP, through Serilog.Sinks.OpenTelemetry | Whatever receives the OTLP (telemetry) |
The OTLP sink is added in code, not in the appsettings files, because it follows the standard OTEL_* settings like the trace and metric exporters do. It sends the same service.name, service.version and deployment.environment.name as the traces, and each event carries the trace and span IDs of whatever was running when it was written, so a log line links to its trace.
Every event carries these properties:
| Property | Comes from |
ApplicationName | Serilog:Properties in the environment's appsettings |
EnvironmentName, MachineName | Serilog.Enrichers.Environment, listed under Serilog:Enrich |
AssemblyVersion, AssemblyInformationalVersion | Added in code at startup |
Anything pushed with LogContext | FromLogContext |
Levels are set in configuration, never in code: Information by default and Debug in local, with Information for Microsoft, Warning for Microsoft.AspNetCore and Microsoft.EntityFrameworkCore.Database.Command (a line per SQL statement buries everything else, and EF Core's meter already counts them), and Error for System. The built-in Logging section is set to None so nothing is written twice.
Three things log a request, and each says something the others don't:
| What | Where | Logs |
| The request log | UseSerilogRequestLogging(), in web templates only | One line per request with its method, path, status code and time. It's registered before the exception handler, so it logs the status code the client actually got |
LoggingBehaviour | src/Application/Common/Behaviours/, around every use case | Information when a request is handled, fails validation or finds nothing, with the request name and its time in milliseconds |
| The presentation error handler | The exception handler or error filter | Error, with the exception, for anything it reports as unexpected |
An unexpected exception is logged once, by the class that turns it into a response, because handling it stops the framework logging it. LoggingBehaviour leaves it alone. In Lambda the exception goes back to the caller as a function error and the runtime writes it to CloudWatch Logs, so nothing logs it a second time.
The Lambda template has no appsettings files. Its startup sets the same levels and properties in code, and environment variables such as Serilog__MinimumLevel__Default override them.
Steps
- Take
ILogger<T>through the constructor where you need it. Never reference Serilog types outside the presentation layer. - Write each message as a
[LoggerMessage]method on apartialclass, with named holes in the template. The source generator writes the code, so nothing is formatted when the level is off. Put the methods in a#region Log Messagesat the bottom of the class. - Pick the level for the reader at 3am:
Informationfor something that happened,Warningfor something odd that was handled,Errorfor a failed request,Debugfor detail you'd only want while developing. - Name new properties in PascalCase with no spaces, so they can be used in templates and queried in any backend without quoting.
- Add a sink or change a level in the appsettings files. A new sink package goes in
Directory.Packages.props(packages-and-dependencies). - In the Aspire dashboard's structured logs, filter on any property, or open a log's trace from its trace ID.
Example
// src/Application/Common/Behaviours/LoggingBehaviour.cs
[LoggerMessage(Level = LogLevel.Information, Message = "{RequestName} handled in {ElapsedMilliseconds:0.0} ms")]
private static partial void LogHandled(ILogger logger, string requestName, double elapsedMilliseconds);// src/Presentation/Extensions, where startup registers services
_ = builder.Services.AddSerilog((services, loggerConfiguration) =>
{
var configuration = services.GetRequiredService<IConfiguration>();
_ = loggerConfiguration.ReadFrom.Configuration(configuration)
.Enrich.WithProperty(
"AssemblyVersion",
assembly.GetCustomAttribute<AssemblyFileVersionAttribute>()?.Version);
if (telemetry.Enabled)
{
_ = loggerConfiguration.WriteTo.OpenTelemetry(
options =>
{
options.ResourceAttributes = telemetry.ResourceAttributes;
options.OnBeginSuppressInstrumentation = SuppressInstrumentationScope.Begin;
},
key => configuration[key]);
}
});// src/Presentation/appsettings.docker.json
"Serilog": {
"WriteTo": [ { "Name": "Console", "Args": { "outputTemplate": "..." } } ],
"Properties": { "ApplicationName": "CleanModelContextProtocol-Docker" }
}Don't
- Use string interpolation in a log message (
$"Created {id}"). It loses the property and allocates on every call. - Call
logger.LogInformation(...)and its friends directly. Write a[LoggerMessage]method instead. - Log an exception that something else will log, such as one you rethrow.
- Log tokens, connection strings, passwords or personal data.
- Use
Console.WriteLine. - Reference Serilog from Application or Infrastructure.
- Register Serilog with
builder.Host.UseSerilog.AddSerilogis the current registration. - Add a second request logger, such as
UseHttpLogging. Requests are logged once, by Serilog. - Move the request log after the exception handler. It would log every handled error as a
500.
Check
dotnet run --project ./src/Presentation # watch the console, then make a request
docker compose up -d --build # then open the Aspire dashboard on the port docs/RUN.md lists
dotnet test tests/Application.Tests.Unit # LoggingBehaviourTests check the messages with FakeLoggerDoes it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

