Understanding Serilog’s log levels and when to use each severity level
Log levels (also called severity levels) indicate the importance and urgency of log events. Serilog provides six log levels, defined in the LogEventLevel enum, each serving a specific purpose in application diagnostics and monitoring.
Definition: “Anything and everything you might want to know about a running block of code.”
Use for extremely detailed tracing and diagnostic information, typically only needed when investigating specific issues.
Log.Verbose("Entering method ProcessOrder with parameters {@OrderRequest}", request);Log.Verbose("Cache lookup for key {CacheKey} returned {Result}", key, result);Log.Verbose("Iteration {Iteration} of {TotalIterations} processing {ItemId}", i, total, item.Id);
When to use:
Detailed method entry/exit points
Loop iterations and internal state changes
Fine-grained performance measurements
Development and troubleshooting
Note: Verbose events are typically disabled in production due to volume and performance impact.
Definition: “Internal system events that aren’t necessarily observable from the outside.”
Use for internal system events useful during development and debugging.
Log.Debug("Loading configuration from {ConfigPath}", configPath);Log.Debug("Cache miss for key {Key}, fetching from database", key);Log.Debug("Resolved dependency {ServiceType} to implementation {ImplementationType}", typeof(IService), typeof(ServiceImpl));
When to use:
Configuration loading and validation
Caching operations (hits, misses, evictions)
Dependency resolution
Internal state transitions
Algorithm checkpoints
Note: Debug events may be enabled in development/staging environments but are often disabled in production.
Definition: “The lifeblood of operational intelligence - things happen.”
Use to record normal, significant events in the application flow that demonstrate the system is working correctly.
Log.Information("User {UserName} logged in from {IpAddress}", userName, ipAddress);Log.Information("Order {OrderId} created with total {Amount:C}", orderId, total);Log.Information("Background job {JobName} started at {StartTime}", jobName, startTime);Log.Information("Email sent to {RecipientEmail} with subject {Subject}", email, subject);
When to use:
User actions and business events
Service lifecycle events (startup, shutdown)
Significant operations completing successfully
Background job execution
External service interactions
Note: Information is typically the default minimum level in production. These events tell the story of what your application is doing.
Use when something unexpected happened, but the application can continue working. Warnings indicate potential problems that should be investigated.
Log.Warning("Request to {Url} took {ElapsedMs}ms, exceeding threshold of {ThresholdMs}ms", url, elapsed, threshold);Log.Warning("Failed to send email to {Email}, will retry. Attempt {Attempt} of {MaxAttempts}", email, attempt, maxAttempts);Log.Warning("API rate limit at {Percentage:P0}, throttling enabled", currentRate / maxRate);Log.Warning("Deprecated API version {Version} used by client {ClientId}", apiVersion, clientId);
Definition: “Functionality is unavailable, invariants are broken or data is lost.”
Use when an operation fails and cannot be completed, but the application continues running.
Log.Error("Failed to process order {OrderId}: {ErrorMessage}", orderId, ex.Message);Log.Error(ex, "Database connection failed for {ConnectionString}", connectionString);Log.Error("Payment processing failed for transaction {TransactionId}", transactionId);Log.Error(ex, "Failed to publish message to queue {QueueName} after {Attempts} attempts", queueName, attempts);
When to use:
Exceptions that prevent an operation from completing
Failed external service calls (after retries)
Data validation failures that block processing
Resource unavailability (file not found, database down)
You can set the minimum log level globally or per-sink:
var log = new LoggerConfiguration() .MinimumLevel.Information() // Only Information and above .WriteTo.Console() .WriteTo.File("log.txt") .CreateLogger();
Use LoggingLevelSwitch to change the minimum level at runtime:
var levelSwitch = new LoggingLevelSwitch(LogEventLevel.Information);var log = new LoggerConfiguration() .MinimumLevel.ControlledBy(levelSwitch) .WriteTo.Console() .CreateLogger();// Later, enable debug logging temporarilylevelSwitch.MinimumLevel = LogEventLevel.Debug;// Process some requests with detailed logging...// Return to normallevelSwitch.MinimumLevel = LogEventLevel.Information;
This is useful for:
Troubleshooting production issues without restarting
Avoid expensive operations if the level isn’t enabled:
if (log.IsEnabled(LogEventLevel.Debug)){ var expensiveData = CollectDetailedDiagnostics(); log.Debug("Diagnostics: {@DiagnosticData}", expensiveData);}// Or use the level-specific methodsif (log.IsEnabled(LogEventLevel.Verbose)){ log.Verbose("Detailed trace: {Details}", BuildExpensiveDetails());}
Avoid logging an exception if you’re re-throwing it - let a higher layer log it once:
public void ProcessOrder(Order order){ try { SaveToDatabase(order); } catch (DbException ex) { Log.Error(ex, "Failed to save order {OrderId}", order.Id); // Handle or propagate, but don't log again throw; }}
Sudden spike in Warnings? Investigate potential issues
Increasing Error rate? System health is degrading
Regular Fatal events? Critical problem needs immediate attention
Log levels are evaluated before the log event is created. If a level is disabled, the event creation is skipped entirely, avoiding any performance overhead from parameter evaluation.