CodeGym /Các khóa học /C# SELF /Ghi log: Microsoft.Extensi...

Ghi log: Microsoft.Extensions.Logging

C# SELF
Mức độ , Bài học
Có sẵn

1. Giới thiệu

Trong lập trình ghi log không chỉ là "in cái gì đó ra console". Ghi log là hộp đen của bạn, GPS-tracker và indicator trong cùng một chai. Không có logs thì chương trình lớn trở thành một hộp đen bí ẩn: "tại sao service bị treo?", "tại sao người dùng không nhận được mail?", "có ai chạy module này trong 3 tháng qua không?" — những câu hỏi này thường chỉ có thể trả lời được nếu logs được lưu và có thể truy cập.

Tại sao logging cần thiết trong thực tế?

Tìm kiếm và chẩn đoán lỗi
Nếu chương trình crash — logs sẽ cho biết chỗ và lý do. Nếu không crash nhưng chạy bất thường — logs sẽ chỉ ra các bước dẫn đến tình trạng đó.

Giám sát trạng thái
Từ logs có thể biết hệ thống đang chạy hay không, có nhiều request tới server không, có lỗi xuất hiện không, ai đang làm gì.

Bảo mật
Ghi lại các cố gắng truy cập trái phép, hoạt động đáng ngờ, lỗi xác thực.

Audit
Ai đã làm gì và khi nào. Nếu một admin ác ý nghỉ việc và xóa dữ liệu, thông qua logs có thể phục hồi mọi thứ (hoặc ít nhất biết chính xác hành động của họ).

Hỗ trợ phát triển và vận hành
Logs không chỉ cho lập trình viên mà còn cho tester, operator, admin. Sau nửa năm bạn sẽ tự nói với mình: 'Cảm ơn vì đã thêm logs!'

Từ Console.WriteLine đến logging công nghiệp

Phản ứng đầu tiên của người mới: "Không lẽ chỉ cần Console.WriteLine là đủ sao?". Với chương trình học nhỏ đúng là đủ. Nhưng khi ứng dụng chạy trên server, chạy đồng thời, hoặc có hàng chục hàng trăm user sử dụng, console không còn giúp được. Cần:

  • Phân tách thông tin quan trọng và debug.
  • Gửi logs không chỉ tới console mà còn tới file, database, hệ thống tập trung.
  • Thay đổi mức chi tiết của logs (ít nhất — chỉ errors, nhiều nhất — mọi thứ).
  • Tự động bổ sung timestamp, thông tin thêm vào bản ghi.
  • Cấu hình linh hoạt.
  • Và quan trọng — không phải thay đổi code chỉ để chuyển hướng logs sang nơi khác.

Đó là lúc bắt đầu kỷ nguyên của logger "thật sự".

2. Cơ bản về hệ thống logging hiện đại trong .NET

Trong ecosystem .NET có một framework logging tiêu chuẩn, mạnh mẽ và linh hoạt — Microsoft.Extensions.Logging. Đây là một phần của "làn sóng mới" các thư viện .NET ra đời cùng ASP.NET Core, nhưng giờ được dùng khắp nơi: server, desktop, thậm chí mobile apps.

Framework này tốt ở điểm gì?

  • Abstraction — không ràng buộc bạn với implementation cụ thể (logger có thể ghi vào file, console, cloud, hoặc cùng lúc nhiều nơi).
  • Hỗ trợ các mức logging (Trace, Debug, Information, Warning, Error, Critical).
  • Được tích hợp vào DI container và kết hợp với tất cả ứng dụng .NET hiện đại.
  • Hệ sinh thái extension phong phú: hỗ trợ structured logging, formatter nâng cao và các integration.

Khái niệm và lớp chính

Hãy làm quen với các đối tượng then chốt cần cho làm việc với Microsoft.Extensions.Logging:

Class/Interface Mục đích
ILogger<T>
Interface để logging, được typed theo class
ILogger
Interface logger tổng quát
ILoggerFactory
Factory để tạo các instance logger
ILoggingBuilder
Cho phép cấu hình logging trong app
LogLevel
Enum các mức log (Trace, Debug, Information, ...)

Các mức logging

Phân loại message theo mức quan trọng giúp lọc "noise" và tìm thứ cần thiết:

Mức (LogLevel) Dùng cho gì?
Trace
Debug rất chi tiết, "noise", dữ liệu tạm thời
Debug
Thông tin debug chính
Information
Thông báo sự kiện quan trọng cho hoạt động bình thường
Warning
Cảnh báo về vấn đề có thể xảy ra, nhưng hệ thống vẫn chạy
Error
Lỗi cần chú ý, nhưng ứng dụng vẫn sống
Critical
Sự cố nghiêm trọng, đe dọa toàn bộ hệ thống

3. Thực hành: thêm logging vào ứng dụng của chúng ta

Đừng viết code trừu tượng, thay vào đó tiếp tục phát triển app demo. Giả sử chúng ta đã có một lớp Calculator đơn giản, sẽ mở rộng dần theo khóa học.

Ví dụ: calculator cơ bản

public class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }
    // Các phương thức còn lại...
}

Bây giờ tích hợp logging vào nó. Ta cần interface ILogger<Calculator>, sẽ nhận từ bên ngoài (ví dụ qua 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("Thực hiện phép cộng: {A} + {B} = {Result}", a, b, result);
        return result;
    }
}

Sự thật thú vị:
Thay vì nối chuỗi, loggers hỗ trợ templates và named parameters ({A}, {B}, {Result}), cho phép logs có cấu trúc và dễ xử lý/search tự động sau này.

4. Cách tạo và cấu hình logger trong ứng dụng console

1. Thêm NuGet packages

Trong project của bạn cần:

  • Microsoft.Extensions.Logging
  • Microsoft.Extensions.Logging.Console (nếu muốn ghi ra console)
  • (tùy chọn) các providers khác nếu cần

2. Cấu hình logger

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

// Tạo DI container
var serviceProvider = new ServiceCollection()
    .AddLogging(builder => {
        builder.AddConsole(); // In ra console
        builder.SetMinimumLevel(LogLevel.Debug); // Mức log tối thiểu
    })
    .BuildServiceProvider();

// Lấy instance logger cho kiểu cần thiết
var logger = serviceProvider.GetRequiredService<ILogger<Calculator>>();

var calculator = new Calculator(logger);
calculator.Add(5, 3); // Tin nhắn sẽ vào logs

Lỗi điển hình của người mới
"Tại sao log không xuất hiện trên console?" — kiểm tra xem provider cần thiết đã được thêm (.AddConsole()) và mức tối thiểu đã được set đúng (SetMinimumLevel) chưa. Nếu mức đặt cao hơn, các message có thể bị "lọc" đi!

5. Các chi tiết hữu ích

Ghi log lỗi và tình huống bất thường

Giả sử muốn log phép chia cho 0. Thêm phương thức tương ứng:

public int Divide(int a, int b)
{
    if (b == 0)
    {
        _logger.LogError("Thử chia cho 0! a={A}", a);
        throw new DivideByZeroException();
    }
    int result = a / b;
    _logger.LogInformation("Thực hiện phép chia: {A} / {B} = {Result}", a, b, result);
    return result;
}

Tại sao cần thế?
Trong app thực tế, khi có chuyện không ổn, logs mức Error thường được chú ý đặc biệt: tự động gửi tới admin, đánh dấu trong monitoring và dùng cho alerts/notifications.

Sử dụng categories và scopes

Logger trong .NET hỗ trợ cái gọi là scopes — metadata bổ sung được thêm tự động vào mọi log trong một block code. Ví dụ khi xử lý web request hoặc session người dùng, có thể thêm id của nó vào scope.

using (_logger.BeginScope("UserId: {UserId}", 42))
{
    _logger.LogInformation("Bắt đầu xử lý dữ liệu người dùng");
    // ...
}

Mọi message trong block sẽ có tag bổ sung UserId: 42, giúp sau này tìm logs theo user hoặc operation.

Ví dụ: các mức logging trong thực tế

_logger.LogTrace("Đây là Trace — hầu như không ai thấy");
_logger.LogDebug("Đây là Debug — cho dev");
_logger.LogInformation("Đây là Information — cho sự kiện hoạt động bình thường");
_logger.LogWarning("Đây là Warning — cảnh báo về vấn đề có thể xảy ra");
_logger.LogError("Đây là Error — lỗi cần chú ý");
_logger.LogCritical("Đây là Critical — hệ thống đang cháy, cần cứu hỏa!");

Nếu bạn cấu hình SetMinimumLevel(LogLevel.Information), bạn chỉ thấy messages của mức Information, Warning, Error, Critical.

Mẹo:
Giữ TraceDebug cho giai đoạn dev để debug chi tiết, còn trên production thường bật chỉ từ Information trở lên để không làm logs phình to và không làm mất các thông tin quan trọng trong "noise".

Sơ đồ trực quan: kiến trúc logging hiện đại

graph TD
A[Mã ứng dụng] --ILogger<YourClass>--> B[Microsoft.Extensions.Logging]
B --> C1[Console Provider]
B --> C2[File Provider]
B --> C3[Cloud/Database Provider]
C1 -.-> D1[Logs ra console]
C2 -.-> D2[Logs vào file]
C3 -.-> D3[Logs vào hệ thống monitoring]

subgraph Providers
    C1
    C2
    C3
end

6. Khả năng mở rộng và extensions

Structured logging:
Giá trị của parameters có thể lưu không chỉ trong chuỗi mà dưới dạng key-value, cho phép tìm kiếm và aggregate theo các tham số (ví dụ qua Seq, ELK/ElasticSearch hoặc Application Insights).

Providers:
Có thể thêm hàng tá providers khác nhau: file, Windows EventLog, Azure, v.v.

Cấu hình logging qua appsettings.json
Trong ASP.NET Core logging có thể cấu hình linh hoạt qua file config, không cần recompile app.

Ví dụ cấu hình mức tối thiểu qua config (appsettings.json):

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

7. Lỗi thường gặp khi làm việc với Microsoft.Extensions.Logging

Lỗi #1: Chọn sai mức logging.
Dùng LogInformation cho lỗi thay vì LogError hoặc LogCritical làm khó tìm lỗi trên production.

Lỗi #2: Bỏ qua structured logging.
Nối chuỗi thay vì dùng templates với placeholders ({Parameter}) làm mất lợi ích của structured logging như search theo parameter.

Lỗi #3: Cấu hình sai mức logging.
Nếu mức tối thiểu được set cao hơn cần thiết (ví dụ Warning thay vì Debug) thì message quan trọng có thể bị lọc.

Lỗi #4: Logging quá nhiều hoặc quá ít.
Logs quá chi tiết (ví dụ Trace trên production) tạo ra "noise", còn thiếu logs thì gây khó khăn cho debug.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION