Understanding ILogger in .NET Core
A Complete Guide for Developers

Morteza Jangjoo, Senior .NET Backend Developer with 15+ years of experience in C#, ASP.NET Core, SQL Server, and Microservices. Skilled in building scalable, high-performance systems.
Logging is one of the most essential parts of any application — it helps developers track what’s happening inside their system, debug issues, and monitor production environments.
In .NET Core, logging is built-in through the ILogger interface, which is part of the Microsoft.Extensions.Logging namespace.
This powerful and flexible abstraction allows you to log messages to multiple providers like Console, File, Debug, Seq, Elasticsearch, and more — all through a unified API.
What is ILogger?
ILogger is a generic logging interface provided by .NET Core for writing log messages.
It abstracts the actual logging implementation, so you can easily switch between different log providers without changing your application code.
The interface is defined as:
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, in practice, we rarely call these methods directly.
Instead, we use the extension methods like LogInformation(), LogWarning(), and LogError().
Adding Logging to a .NET Core Application
Logging in .NET Core is configured through Dependency Injection (DI).
When you create a new project (like using dotnet new webapi), the logging infrastructure is already available.
Let’s see it in action.
Example: Logging in 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
{
// Simulate some logic
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");
}
}
}
}
In this example:
ILogger<T>automatically injects a logger for theWeatherController.We log an information message before processing and an error message if something goes wrong.
Log Levels
.NET Core supports the following log levels (from most to least severe):
| Log Level | Description |
Critical | For critical errors that cause complete failure |
Error | For runtime errors that require attention |
Warning | For abnormal or unexpected events |
Information | For general app flow messages |
Debug | For debugging information, often used during development |
Trace | For detailed, low-level debugging information |
You can filter logs based on these levels in appsettings.json.
Configuring Logging in appsettings.json
You can control what gets logged by editing the Logging section in your configuration file:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
This configuration means:
Your app logs everything from
Informationand above.Microsoft system logs (framework logs) are limited to
Warningor higher.
Adding Custom Logging Providers
Out of the box, .NET Core supports several logging providers like:
Console
Debug
EventSource
EventLog
You can also integrate third-party providers like:
Serilog
NLog
Seq
Elasticsearch
Example with Serilog:
public static class Program
{
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.WriteTo.File("logs/log.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
try
{
Log.Information("Starting web host");
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog() // Add Serilog here
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Structured Logging with Message Templates
Instead of concatenating strings, ILogger uses structured logging, which is both cleaner and more efficient.
Example:
_logger.LogInformation("User {UserId} logged in at {LoginTime}", userId, DateTime.UtcNow);
This logs data in a structured format like JSON (depending on the provider), making it easier to query and analyze logs in tools like Seq or Elastic Stack.
Using Scopes for Contextual Logging
Scopes allow you to group log messages that share a common context (for example, a request ID or user ID).
using (_logger.BeginScope("TransactionId: {TransactionId}", Guid.NewGuid()))
{
_logger.LogInformation("Processing started");
_logger.LogInformation("Processing completed");
}
All logs inside the scope will include the TransactionId automatically — very useful for debugging distributed or concurrent operations.
Summary
ILogger is the heart of .NET Core’s logging system.
It’s provider-independent and works seamlessly with dependency injection.
Supports structured logging, log levels, and scopes for context.
Easily extensible with providers like Serilog, NLog, or Seq.
Using ILogger effectively means you can keep your logs clean, consistent, and ready for deep analysis — essential for production-grade applications.
Example GitHub Repository
You can find a working example here:
👉 https://github.com/Morteza-Jangjoo/ILogging




