Understanding ILogger in .NET Core — A Complete Guide
Logging is an essential part of every modern application. It helps you understand what’s happening inside your system, debug problems faster, and monitor production environments effectively.
In .NET Core, logging is built-in through the ILogger interface, part of the Microsoft.Extensions.Logging namespace.
This powerful abstraction allows developers to log messages to multiple providers like Console, File, Seq, Elasticsearch, and more — all using a consistent API.
What is ILogger?
ILogger is the core logging interface in .NET Core that provides a unified way to write log messages.
It abstracts away the underlying implementation, so you can change the logging provider without touching your application logic.
public interface ILogger
{
IDisposable BeginScope<TState>(TState state);
bool IsEnabled(LogLevel logLevel);
void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter);
}
However, developers typically use extension methods like LogInformation(), LogWarning(), and LogError() instead of calling these methods directly.
🔹 Basic Usage in .NET Core
Logging in .NET Core is automatically registered in the Dependency Injection (DI) container.
When you create a new Web API project, it’s already wired up.
Here’s how to use it inside a controller:
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace LoggingDemo.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherController : ControllerBase
{
private readonly ILogger<WeatherController> _logger;
public WeatherController(ILogger<WeatherController> logger)
{
_logger = logger;
}
[HttpGet]
public IActionResult Get()
{
_logger.LogInformation("Weather forecast requested at {Time}", DateTime.UtcNow);
try
{
var data = new[] { "Sunny", "Cloudy", "Rainy" };
return Ok(data);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error while fetching weather data");
return StatusCode(500, "Internal Server Error");
}
}
}
}
✅ Here we:
- Inject
ILogger<WeatherController>using DI. - Log an information message and an error message.
- Use structured logging (with placeholders like
{Time}).
Log Levels in .NET Core
| Log Level | Description |
|---|---|
Critical |
Application crash or serious failure |
Error |
Recoverable runtime error |
Warning |
Unexpected or abnormal events |
Information |
General application flow messages |
Debug |
Detailed debug info (used during development) |
Trace |
Very detailed, low-level diagnostic info |
You can control what gets logged using appsettings.json.
Configure Logging in appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
This configuration:
- Logs all messages from
Informationlevel and higher for your app. - Limits Microsoft framework logs to
Warningand higher.
Setting up Logging in Program.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
var builder = WebApplication.CreateBuilder(args);
// Configure logging
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
Structured Logging
Structured logging helps you avoid messy string concatenation.
Instead of writing:
_logger.LogInformation("User " + userId + " logged in at " + DateTime.UtcNow);
Write this instead:
_logger.LogInformation("User {UserId} logged in at {LoginTime}", userId, DateTime.UtcNow);
This logs data in a structured, queryable format (like JSON) — great for tools like Seq, Serilog, or Elastic Stack.
Using Scopes for Contextual Logs
Scopes allow you to associate related log entries with shared context (e.g., request ID, transaction ID).
using (_logger.BeginScope("TransactionId: {TransactionId}", Guid.NewGuid()))
{
_logger.LogInformation("Transaction started");
_logger.LogInformation("Transaction completed");
}
All log entries inside the scope will include the same TransactionId, which helps with tracing.
Adding Third-Party Providers (Serilog Example)
Want to store logs in files or send them to a log server?
Integrate Serilog with just a few lines.
Install packages:
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.File
Then update your Program.cs:
using Serilog;
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File("logs/log.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog();
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
Example Project
You can download a full working example here:
👉 Github
This includes:
Program.csWeatherController.csappsettings.json-
.csprojfile
Summary
ILogger is the foundation of logging in .NET Core.
It’s framework-independent, provider-based, and fully configurable.
Supports structured logging, log levels, and scopes.
Easily integrates with Serilog, NLog, Seq, or Elastic Stack.
With a good logging strategy, you can understand user behavior, detect issues early, and monitor your application health in production.
I’m Morteza Jangjoo and “Explaining things I wish someone had explained to me”
Top comments (3)
Hello Morteza,
Thank you for sharing the example and code.
Are you able to create a project that logs to file also in 'Production' mode?
All the examples I have found (including chatgpt and claude) work fine in debugging mode, but in production they do not create the log file.
Best regards,
Pertti
Hi Pertti,
file logging works in Production as well, but only if a proper logging provider (such as Serilog or NLog) is configured.
ILogger itself does not support file logging. In most cases where it works in Development but not in Production, the root causes are:
File logging is configured only in appsettings.Development.json
The log directory does not exist (ASP.NET does not create it automatically)
The production process (IIS, Docker, Linux service) does not have write permission to the folder
Using a provider like Serilog, placing the configuration in appsettings.json, ensuring the log folder exists, and granting write permissions resolves the issue completely.
I’ve added a detailed article with a full production-ready example that explains this in depth.
Hi Morteza,
Thank you for reply. However the problem I had was much more complex than these solutions proposed everywhere.
ASP.NET Minimal Api has a peculiar way of handling parameter binding at endpoints. If there is error in serializing JSON to DTO it throws exception. Exception gets caught and logged at my endpoint error handler that is the first in middleware chain. That's how it is in debug mode. Quite obvious I think.
But, when you publish and run the code in Production mode, exception is not thrown. It just returns 400 Bad request. And of course it does not get logged in my error handler.
The solution turned out to be very simple but hard to find:
builder.Services.Configure(options =>
{
options.ThrowOnBadRequest = true;
});
Hope this helps someone.
Best regards,
Pertti