Introduction
Serilog's file sink supports rolling log files based on time intervals (hour, day, month). When the file path template does not include the date token, when the rolling interval is misconfigured, or when file permissions prevent new file creation, logs continue appending to a single file or stop being written entirely. This makes log management difficult and can cause disk space issues in production.
Symptoms
- Log file grows indefinitely without rolling
- Date token
{Date}appears literally in filename instead of being replaced - New log file not created at midnight as expected
- File sink stops writing after first file reaches certain size
- Log entries missing after file should have rolled
Check file sink configuration:
``csharp
Log.Logger = new LoggerConfiguration()
.WriteTo.File(
path: "logs/app-.log", // MISSING date token
rollingInterval: RollingInterval.Day)
.CreateLogger();
Common Causes
- Path template missing the date token (should be
app-{Date}.log) - Directory does not exist and cannot be created
- File permissions prevent creating new files
RollingIntervalset but path does not support it- RetainedFileCountLimit deleting files too aggressively
Step-by-Step Fix
- 1.**Configure rolling file with correct path template":
- 2.```csharp
- 3.using Serilog;
- 4.using Serilog.Events;
Log.Logger = new LoggerConfiguration() .MinimumLevel.Information() .WriteTo.File( // IMPORTANT: Include {Date} token for rolling path: "logs/app-.log", // Serilog replaces - with the date rollingInterval: RollingInterval.Day, retainedFileCountLimit: 30, // Keep 30 days of logs fileSizeLimitBytes: 100_000_000, // 100MB max per file rollOnFileSizeLimit: true, // Roll when size limit hit shared: true, // Multiple processes can write flushToDiskInterval: TimeSpan.FromSeconds(1)) .CreateLogger();
// Output filenames will be: // app-20260409.log // app-20260410.log // app-20260411.log ```
- 1.**Configure different rolling intervals":
- 2.```csharp
- 3.// Daily rolling (most common)
- 4..WriteTo.File("logs/app-.log", rollingInterval: RollingInterval.Day)
- 5.// Output: app-20260409.log
// Hourly rolling (high-volume apps) .WriteTo.File("logs/app-.log", rollingInterval: RollingInterval.Hour) // Output: app-20260409-14.log
// Monthly rolling (low-volume apps) .WriteTo.File("logs/app-.log", rollingInterval: RollingInterval.Month) // Output: app-202604.log
// Infinite (no rolling, NOT recommended for production) .WriteTo.File("logs/app.log") // No rolling ```
- 1.**Ensure directory exists and has correct permissions":
- 2.```csharp
- 3.// Ensure log directory exists before configuring Serilog
- 4.var logDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs");
- 5.if (!Directory.Exists(logDirectory))
- 6.{
- 7.Directory.CreateDirectory(logDirectory);
- 8.}
Log.Logger = new LoggerConfiguration() .WriteTo.File( path: Path.Combine(logDirectory, "app-.log"), rollingInterval: RollingInterval.Day, // Run as the same user as the app pool / service // Ensure the user has write permissions to the log directory shared: true) .CreateLogger(); ```
- 1.**Use async file sink for better performance":
- 2.```csharp
- 3.// Serilog.Sinks.Async wraps any sink for async writing
- 4.// Install: Serilog.Sinks.Async
Log.Logger = new LoggerConfiguration() .WriteTo.Async(async => async.File( path: "logs/app-.log", rollingInterval: RollingInterval.Day, bufferSize: 1000, // Buffer 1000 log events before flushing shared: true)) .CreateLogger(); ```
Prevention
- Always include the date placeholder in the file path for rolling
- Set
retainedFileCountLimitto prevent disk space exhaustion - Use
shared: truewhen multiple processes write to the same log - Enable
flushToDiskIntervalfor critical log delivery - Monitor log file sizes and rotation in production
- Add log directory existence check during application startup