Why Is Your C# Application Screaming, and You Can't Hear It?
\nImagine: you deploy a new version of a C# microservice at midnight. Everything works perfectly... for the first 10 minutes. And then — bam! — users complain about slowdowns, and you're sitting in front of a 2 GB log file trying to find a needle in a haystack. Sound familiar? If not, you're either a genius or you've never done real monitoring.
\nIn this article, we'll break down how to turn log chaos into a structured system. You'll learn how to set up C# logging so you don't lose your mind during deployment, and how to build application monitoring that actually helps, not just draws pretty charts.
\n\nWhere to Start: The Philosophy of Proper Logging
\nMany developers think: "I'll just use Console.WriteLine — that's fine." Spoiler: no, it's not fine. Real logging in C# is structured logging. Instead of strings like:
\nLog.Info("User " + userId + " bought item " + itemId);\nYou write:
\nLog.Information("User {UserId} bought item {ItemId}", userId, itemId);\nThe difference is enormous. In the second case, you can filter logs by UserId, build dashboards, and quickly find issues. This is the foundation of C# error diagnostics in production.
Choosing a Library: Serilog vs NLog vs log4net
\nThere are three main players in the .NET ecosystem. Let's compare them based on criteria important for a real project:
\n- \n
- Serilog — the favorite of modern .NET. Ideal for structured logging, easy to configure, supports sinks (output to file, database, Elasticsearch). I recommend it for new projects. \n
- NLog — a veteran with flexible XML/JSON configuration. Great for legacy systems where it's already in use. \n
- log4net — an old-timer best left untouched unless absolutely necessary. Support is shaky, structured logging requires workarounds. \n
For .NET tracing in modern applications, Serilog is the #1 choice. Let's set it up in 5 minutes.
\n\nSetting Up Serilog in C#: A Step-by-Step Guide
\nInstall the packages via NuGet:
\ndotnet add package Serilog\ndotnet add package Serilog.Sinks.Console\ndotnet add package Serilog.Sinks.File\ndotnet add package Serilog.Sinks.Seq\nNow — a minimal configuration in Program.cs for ASP.NET Core:
using Serilog;\n\nLog.Logger = new LoggerConfiguration()\n .MinimumLevel.Information()\n .WriteTo.Console()\n .WriteTo.File("logs/app-.log", rollingInterval: RollingInterval.Day)\n .WriteTo.Seq("http://localhost:5341") // for advanced monitoring\n .CreateLogger();\n\ntry\n{\n Log.Information("Starting web application");\n var builder = WebApplication.CreateBuilder(args);\n builder.Host.UseSerilog(); // integration with the host\n // ... remaining setup\n var app = builder.Build();\n app.Run();\n}\ncatch (Exception ex)\n{\n Log.Fatal(ex, "Application terminated unexpectedly");\n}\nfinally\n{\n Log.CloseAndFlush();\n}\nNotice the rollingInterval — logs will rotate daily instead of growing indefinitely. This is the foundation of asynchronous logging in production.
Structured Logs: How to Get the Most Out of Them
\nSuppose we have an order processing method. Without structuring, you would write:
\nlogger.LogInformation("Processing order {OrderId} for user {UserId}", order.Id, user.Id);\nWith Serilog, this becomes a JSON object that can be parsed. And if you add context via ILogger<T>:
public class OrderService\n{\n private readonly ILogger<OrderService> _logger;\n\n public OrderService(ILogger<OrderService> logger)\n {\n _logger = logge