Why ILogger File Logging Fails in Production (and How to Fix It)
Many .NET developers face a confusing issue:
Logging to file works perfectly in Development,
but no log file is created in Production.
This problem appears so often that it’s easy to think it’s a bug in .NET or ILogger.
In reality, it’s a misunderstanding of how logging works in ASP.NET Core.
In this article, we’ll explain:
- Why this happens
- What
ILoggeractually does (and doesn’t) - The most common production pitfalls
- A correct, production-ready file logging setup
1. ILogger Does NOT Support File Logging
The first and most important fact:
Microsoft.Extensions.Logginghas no built-in file logger
ILogger is just an abstraction.
It forwards logs to providers, such as:
- Console
- Debug
- EventSource
- Third-party providers (Serilog, NLog, etc.)
So this will never create a file by itself:
ILogger<MyService> logger;
logger.LogInformation("Hello World");
If file logging “worked” in Development, it was because:
- You added a provider indirectly
- Or your IDE showed Debug output
- Or configuration was loaded only in Development
2. Why It Works in Development but Fails in Production
Here are the real reasons behind the issue.
Reason 1: Logging configured only in appsettings.Development.json
ASP.NET loads configuration in this order:
appsettings.jsonappsettings.{Environment}.json- Environment variables
If file logging is configured only in:
appsettings.Development.json
Then in Production:
- That config is never loaded
- File logging simply doesn’t exist
Reason 2: Log directory does not exist
ASP.NET does not create directories automatically.
If you configure:
"path": "Logs/app.log"
But /Logs does not exist → no file is created
No exception. No warning. Just silence.
Reason 3: Missing write permissions (MOST COMMON)
In Production, your app usually runs as:
- IIS App Pool identity
- Docker container user
- Linux service account
That user often cannot write to disk.
So file creation fails silently.
3. The Correct Way: Use a Real File Logger (Serilog)
Let’s fix everything properly.
4. Install Required Packages
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.File
5. Configure Serilog in Program.cs
using Serilog;
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog((context, services, configuration) =>
{
configuration
.ReadFrom.Configuration(context.Configuration)
.Enrich.FromLogContext();
});
var app = builder.Build();
app.MapGet("/", (ILogger<Program> logger) =>
{
logger.LogInformation("Application started successfully");
return "Hello World";
});
app.Run();
6. Configure Logging in appsettings.json (NOT Development-only)
{
"Serilog": {
"Using": [ "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "Logs/app-.log",
"rollingInterval": "Day",
"shared": true
}
}
]
}
}
This configuration works in Development AND Production
7. Ensure the Log Folder Exists
Create it manually:
/Logs
Or programmatically:
Directory.CreateDirectory("Logs");
Best practice: create it before logging starts
8. Fix Production Permissions
IIS
Grant write access to:
IIS AppPool\YourAppPoolName
Linux
chmod -R 755 Logs
chown -R appuser:appuser Logs
Docker
Mount a writable volume:
volumes:
- ./Logs:/app/Logs
9. Final Checklist (Production Safe)
Before blaming .NET, check this list:
- Using Serilog / NLog
- Logging config in
appsettings.json - Log directory exists
- App has write permissions
- Not relying on Debug output
Conclusion
ILogger is not broken in Production.
It simply does exactly what it’s designed to do — nothing more.
File logging failures are almost always caused by:
- Missing providers
- Wrong configuration scope
- File system permissions
Once you understand this, logging becomes predictable, stable, and production-safe.
I’m Morteza Jangjoo and “Explaining things I wish someone had explained to me”
Top comments (0)