CodeGym /Courses /C# SELF /Logging: Microsoft.Extensi...

Logging: Microsoft.Extensions.Logging

C# SELF
Level 64, Lesson 0
Available

1. Introduction

In programming logging is not just "print something to the console". Logging is your black box, GPS tracker, and indicator all in one. Without logs a large program becomes a mysterious black box: "why did the service hang?", "why didn't the user get the email?", "did anyone run this module in the last 3 months?" — often you can answer these questions only if logs are stored and accessible.

Why logging matters in real life?

Finding and diagnosing bugs
If the program crashed — logs will tell you where and why. If it didn't crash but behaves strangely — logs will point to the steps that led to that.

Monitoring health
From logs you can tell if the system is up, how many requests the server gets, whether errors are occurring, who is doing what.

Security
Logging of unauthorized access attempts, suspicious activity, authentication failures.

Audit
Who did what and when. If a malicious admin left and deleted data, logs can help recover (or at least understand) exactly what they did.

Support for development and operations
Logs are not only for developers but also for QA, operators, and admins. In six months you'll thank yourself: 'Thanks for adding logs!'

From Console.WriteLine to production logging

A beginner's first reaction: "Wait, isn't Console.WriteLine enough?". For tiny tutorial programs that's true. But when an app runs on a server, runs concurrently, or is used by dozens or hundreds of users, the console isn't enough. You need to:

  • Separate important info from debug info.
  • Send logs not only to the console but also to files, databases, centralized systems.
  • Change verbosity level (minimum — errors only, maximum — everything).
  • Automatically augment entries with date, time, and extra context.
  • Configure flexibly.
  • And most importantly — not change code just to redirect logs to another place.

That's where "real" loggers come in.

2. Basics of a modern logging system in .NET

In the .NET ecosystem there is a standard, powerful and flexible logging framework — Microsoft.Extensions.Logging. It's part of the "new wave" of .NET libraries that appeared with ASP.NET Core, but it's used everywhere now: server, desktop, even mobile apps.

What's good about this framework?

  • Abstraction — it doesn't lock you into a specific implementation (the logger can write to file, console, cloud, or all at once).
  • Support for log levels (Trace, Debug, Information, Warning, Error, Critical).
  • Integration into the DI container and into all modern .NET apps.
  • Rich ecosystem of extensions: support for structured logging, advanced formatters and integrations.

Key concepts and classes

Let's meet the key objects you'll use with Microsoft.Extensions.Logging:

Class/Interface Purpose
ILogger<T>
Interface for logging, typed by class
ILogger
Non-generic logger interface
ILoggerFactory
Factory for creating logger instances
ILoggingBuilder
Allows configuring logging in the app
LogLevel
Enum of log levels (Trace, Debug, Information, ...)

Log levels

Splitting messages by importance lets you filter the noise and find what's needed:

Level (LogLevel) When to use?
Trace
Maximum-detail debugging, "noise", timing data
Debug
Main debug information
Information
Key event messages for normal operation
Warning
Warnings about potential issues, but system continues
Error
Errors that need attention, but app is alive
Critical
Critical failures threatening the whole system

3. Practice: adding logging to our app

Let's not write abstract code — instead we'll keep evolving our demo app. Suppose we already have a simple Calculator class that we extend as the course goes on.

Example: basic calculator

public class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }
    // Other methods...
}

Now let's embed logging into it. For that we need the ILogger<Calculator> interface, which we'll get from outside (for example via Dependency Injection, DI).

using Microsoft.Extensions.Logging;

public class Calculator
{
    private readonly ILogger<Calculator> _logger;

    public Calculator(ILogger<Calculator> logger)
    {
        _logger = logger;
    }

    public int Add(int a, int b)
    {
        int result = a + b;
        _logger.LogInformation("Performed addition: {A} + {B} = {Result}", a, b, result);
        return result;
    }
}

Fun fact:
Instead of string concatenation loggers support templates and named parameters ({A}, {B}, {Result}), which makes logs structured and easier for automated processing and searching.

4. How to create and configure a logger in a console app

1. Add NuGet packages

Your project will need:

  • Microsoft.Extensions.Logging
  • Microsoft.Extensions.Logging.Console (if you want console output)
  • (optionally) other providers as needed

2. Configure the logger

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

// Create DI container
var serviceProvider = new ServiceCollection()
    .AddLogging(builder => {
        builder.AddConsole(); // Console output
        builder.SetMinimumLevel(LogLevel.Debug); // Minimum log level
    })
    .BuildServiceProvider();

// Get a logger instance for the desired type
var logger = serviceProvider.GetRequiredService<ILogger<Calculator>>();

var calculator = new Calculator(logger);
calculator.Add(5, 3); // This will emit a log message

Common newbie mistake
"Why doesn't the log show up in the console?" — check if the required provider is added (.AddConsole()) and whether the minimum log level is set correctly (SetMinimumLevel). If the level is higher, messages may simply be "filtered out"!

5. Useful nuances

Log errors and unusual situations

Suppose we want to log division by zero. Add the following method:

public int Divide(int a, int b)
{
    if (b == 0)
    {
        _logger.LogError("Attempt to divide by zero! a={A}", a);
        throw new DivideByZeroException();
    }
    int result = a / b;
    _logger.LogInformation("Performed division: {A} / {B} = {Result}", a, b, result);
    return result;
}

Why do this?
In a real app when something goes wrong logs with level Error usually get special attention: they are automatically sent to admins, highlighted in monitoring and used for alerts/notifications.

Using categories and scopes

The .NET logger supports so-called scopes — extra metadata automatically added to all logs within a certain block of code. For example, if you're handling a web request or a user session you can add its id to the scope.

using (_logger.BeginScope("UserId: {UserId}", 42))
{
    _logger.LogInformation("Started processing user data");
    // ...
}

All messages inside the block will get the extra tag UserId: 42, which later helps find logs by user or operation.

Example: log levels in action

_logger.LogTrace("This is Trace — almost nobody will see it");
_logger.LogDebug("This is Debug — for developers");
_logger.LogInformation("This is Information — for normal events");
_logger.LogWarning("This is Warning — a potential issue");
_logger.LogError("This is Error — needs attention");
_logger.LogCritical("This is Critical — the system is on fire, call the firefighter!");

If you set SetMinimumLevel(LogLevel.Information), you'll see only messages of levels Information, Warning, Error, Critical.

Tip:
Keep Trace and Debug for deep diagnostics during development, but on production servers usually enable only Information and above to avoid log bloat and not miss important stuff among the "noise".

Visual diagram: modern logging architecture

graph TD
A[Application code] --ILogger<YourClass>--> B[Microsoft.Extensions.Logging]
B --> C1[Console Provider]
B --> C2[File Provider]
B --> C3[Cloud/Database Provider]
C1 -.-> D1[Logs to console]
C2 -.-> D2[Logs to file]
C3 -.-> D3[Logs to monitoring system]

subgraph Providers
    C1
    C2
    C3
end

6. Additional features and extensions

Structured logging:
Parameter values can be stored not only in a string but as key-value pairs, enabling search and aggregation by those parameters (for example via Seq, ELK/ElasticSearch or Application Insights).

Logging providers:
You can add dozens of different providers: file, Windows EventLog, Azure, etc.

Configuring logging via appsettings.json
In ASP.NET Core logs are flexibly configured via a configuration file without recompiling the app.

Example of configuring minimum log levels via config (appsettings.json):

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "MyApp.Calculator": "Debug",
      "Microsoft": "Warning"
    }
  }
}

7. Common mistakes when working with Microsoft.Extensions.Logging

Mistake #1: Wrong choice of log level.
Using LogInformation for errors instead of LogError or LogCritical makes it hard to find issues in production.

Mistake #2: Ignoring structured logging.
String concatenation instead of templates with placeholders ({Parameter}) removes the benefits of structured logging like searching by parameters.

Mistake #3: Misconfiguring log levels.
If the minimum log level is set too high (e.g. Warning instead of Debug), important messages may be filtered out.

Mistake #4: Too much or too little logging.
Overly verbose logs (e.g. Trace in production) create "noise", while lack of logs makes diagnosis hard.

2
Task
C# SELF, level 64, lesson 0
Locked
Basic Logging with 'Microsoft.Extensions.Logging'
Basic Logging with 'Microsoft.Extensions.Logging'
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION