CodeGym /Các khóa học /C# SELF /Ghi log có cấu trúc và Ser...

Ghi log có cấu trúc và Serilog

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

1. Giới thiệu

Nếu ghi log dạng text bình thường giống như nhật ký với ghi chú "Hôm nay mình đã ăn", thì ghi log có cấu trúc biến mỗi entry thành một thẻ với các trường: {Ngày: ..., Sự kiện: "Ăn", Calo: 500, Món: "Thịt chiên"}. Điều đó có nghĩa là sau này bạn không chỉ đọc nhật ký mà còn có thể vẽ biểu đồ calo theo tháng, lọc sự kiện theo món ăn và biết khi nào bạn ăn quá muộn.

Tại sao chỉ text thì không đủ?

Với text thuần thì mọi thứ đơn giản... cho tới lúc cần phân tích. Thử nghĩ thu thập thống kê về lỗi trong log, doanh số trong cửa hàng online, chuỗi hành động của một user theo ID (ví dụ 42) nếu mọi dữ liệu chỉ là trang text. Ghi log có cấu trúc cho phép gắn phân tích và thậm chí AI vào log. Với nó bạn có thể tìm anomaly, build dashboard và phản ứng tự động khi có vấn đề.

Lợi ích

  • Cho phép log không chỉ message mà còn dữ liệu liên quan (fields/properties).
  • Log có thể phân tích tự động: đếm, lọc, tạo báo cáo.
  • Sử dụng các định dạng chuẩn như JSON, dễ parse bằng máy.

Serilog: là gì và tại sao dùng?

Serilog (website chính thức: serilog.net, docs: github.com/serilog/serilog/wiki) — thư viện phổ biến để ghi log có cấu trúc trong .NET. Nó tích hợp tốt với Microsoft.Extensions.Logging, hỗ trợ xuất ra hàng chục hệ thống khác nhau (file, console, Seq, ElasticSearch, Grafana, Azure, v.v.), ảnh hưởng ít đến performance và dễ cấu hình.

Serilog khác gì so với "ghi log bình thường"?

  • Cấu trúc: log là object có fields, có thể filter (ví dụ tất cả lỗi của user ID 42).
  • Định dạng: có thể ghi không chỉ text mà còn JSON, XML, tiện cho xử lý tiếp theo.
  • Flexibility: nhiều package sink sẵn để gửi log đi bất cứ đâu.

Cấu trúc một entry log với Serilog

Hãy xem một log có cấu trúc trước khi viết code.

{
  "Timestamp": "2024-06-22T10:23:45.123Z",
  "Level": "Information",
  "MessageTemplate": "Người dùng {UserId} đã đăng nhập vào hệ thống",
  "Properties": {
    "UserId": 42,
    "IpAddress": "127.0.0.1"
  }
}

Ngay cả phân tích cơ bản cũng hiểu: đây là user số 42 và IP của họ.

2. Cài đặt và cấu hình cơ bản Serilog trong project C#

Bước 1. Cài NuGet‑packages

Trong Rider/Visual Studio qua NuGet Package Manager cài:

  • Serilog
  • Serilog.Sinks.Console (xuất ra console)
  • Serilog.Extensions.Logging (để tích hợp với Microsoft.Extensions.Logging)

Bằng command line:

dotnet add package Serilog
dotnet add package Serilog.Sinks.Console

Bước 2. Cấu hình tối thiểu

Thêm cấu hình vào Program.cs và ghi log đầu tiên.

using System;
using Serilog;

namespace MySuperApp
{
    class Program
    {
        static void Main(string[] args)
        {
            // 1. Cấu hình cơ bản Serilog: xuất ra console
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Debug()
                .WriteTo.Console()
                .CreateLogger();

            // 2. Ví dụ ghi log có cấu trúc
            int userId = 42;
            string ip = "127.0.0.1";
            Log.Information("Người dùng {UserId} đã đăng nhập vào hệ thống với IP {IpAddress}", userId, ip);

            Log.CloseAndFlush();
        }
    }
}

Trên console bạn sẽ thấy khoảng như sau:

[10:30:16 INF] Người dùng 42 đã đăng nhập vào hệ thống với IP 127.0.0.1

Sau đó dễ dàng chuyển output sang file JSON, Seq hoặc hệ thống khác.

3. Format log: Message Template

Trong Serilog thay vì concat chuỗi ta dùng syntax template:

Log.Information("Thao tác {Operation} trên file {FileName}", "xóa", "test.txt");

Đây không chỉ là cú pháp đẹp — đó là ghi log có cấu trúc: entry sẽ có fields OperationFileName, có thể filter và aggregate sau này.

Khác gì so với string.Format?

string.Format("Thao tác {0} trên file {1}", operation, fileName) chỉ ghép chuỗi với placeholders {0}, {1}. Serilog tạo ra các trường riêng, để phân tích sau này.

Cấu hình linh hoạt: levels, filters, nhiều "sink"

Serilog có thể ghi đồng thời vào nhiều nơi.

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .WriteTo.Console()
    .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)
    .CreateLogger();

Bây giờ log được ghi cả ra console và file với rolling hàng ngày.

4. Ví dụ

Giả sử ta làm app console kiểu "notes", cho phép tạo các entry của user. Thêm logging có cấu trúc cho các hành động.

using System;
using Serilog;

namespace NotesApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Information()
                .WriteTo.Console()
                .WriteTo.File("notes-log.json", rollingInterval: RollingInterval.Day, 
                              formatter: new Serilog.Formatting.Json.JsonFormatter())
                .CreateLogger();

            Console.WriteLine("Nhập tên người dùng:");
            string userName = Console.ReadLine();

            Log.Information("Người dùng {UserName} đã khởi động ứng dụng NotesApp", userName);

            while (true)
            {
                Console.WriteLine("Nhập nội dung ghi chú (hoặc gõ 'thoát'):");
                string note = Console.ReadLine();

                if (note == "thoát")
                {
                    Log.Information("Người dùng {UserName} đã kết thúc phiên làm việc", userName);
                    break;
                }

                Log.Information("Người dùng {UserName} đã tạo ghi chú: {NoteText}", userName, note);
            }

            Log.CloseAndFlush();
        }
    }
}

Ghi chú:
Log ghi lại ai khởi động chương trình, nội dung nhập và khi nào kết thúc. Trong file notes-log.json mỗi entry là một object JSON, dễ để phân tích.

5. Những chi tiết hữu ích

Best practices cho ghi log có cấu trúc

  • Đừng lạm dụng levels Debug/Trace trong production — dùng InformationWarning hợp lý.
  • Dùng named parameters trong template thay vì concat string.
  • Không bao giờ log dữ liệu nhạy cảm (passwords, tokens, keys).
  • Log các business-event quan trọng, không chỉ error/exceptions.
  • Cấu hình rotation và cleanup để không bị đầy disk.

Visualization và phân tích: Seq, Kibana, Application Insights

Serilog hỗ trợ nhiều sink — điểm đích để gửi log.

Sink Mô tả ngắn Sử dụng ở đâu
Console Trực tiếp ra console Phát triển, test
File Ra file local hoặc network Project nhỏ, dev
Seq Web UI có filter và dashboard Enterprise, analytics
ElasticSearch Hệ thống lưu trữ và phân tích mạnh Công ty lớn
Azure Application Insights Monitoring và telemetry trên cloud Dịch vụ chạy nhiều trên Azure

Seq (datalust.co/seq) — giải pháp rất phổ biến cho dev và internal: filter tiện, search theo fields và deploy nhanh.

Bảng và visualization

Dưới đây là bảng ngắn những thứ có thể log có cấu trúc:

Ghi gì Trông như thế nào trong Serilog Ví dụ giá trị
ID người dùng
{UserId}
123
Hành động
{Action}
"Xóa"
Lỗi
Log.Error(ex, "Lỗi ở {Module}")
"Module đăng ký"
Thời gian thao tác {Elapsed:0.000} giây 1.234
Tên file
{FileName}
"report.pdf"

Mẹo hay và các khả năng mở rộng của Serilog

  • Enrichers: thêm thuộc tính cho mỗi log (ví dụ .Enrich.WithMachineName()).
  • Correlation: thêm RequestId để nối chuỗi sự kiện.
  • Cấu hình qua appsettings.json: thuận tiện cho production.
{
  "Serilog": {
    "MinimumLevel": "Debug",
    "WriteTo": [
      { "Name": "Console" },
      { "Name": "File", "Args": { "path": "log.txt" } }
    ]
  }
}

Advanced sinks: có thể gửi log vào Slack, Telegram, email (nhưng cẩn thận để không nhận cả nghìn mail cho mỗi lỗi).

6. Thực hành: tích hợp với Microsoft.Extensions.Logging

Trong .NET thường dùng interface chuẩn ILogger để không bị ràng buộc vào thư viện cụ thể. Serilog có thể cài như provider.

Bước 1. Cài package

dotnet add package Serilog.Extensions.Logging

Bước 2. Cấu hình

using Microsoft.Extensions.Logging;
using Serilog;

// ...

// Cấu hình Serilog như bình thường:
Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateLogger();

// Giờ dùng Microsoft.Extensions.Logging
var loggerFactory = LoggerFactory.Create(builder =>
{
    builder.AddSerilog();
});

ILogger<Program> logger = loggerFactory.CreateLogger<Program>();
logger.LogInformation("Thông điệp thử: {TestValue}", 123);

// Đừng quên đóng:
Log.CloseAndFlush();

Ghi chú:
Giờ code của bạn dùng ILogger<T> sẽ không phụ thuộc provider — nếu muốn chuyển sang NLog hoặc Log4Net đều được.

7. Lỗi thường gặp khi dùng Serilog

Spam log: nếu ghi mọi thứ thì thông tin hữu ích sẽ bị chìm.

Ghi exception như text: hãy dùng overload có exception — như vậy cấu trúc lỗi sẽ vào log.

try
{
    // some code
}
catch (Exception ex)
{
    Log.Error(ex, "Đã xảy ra lỗi khi thực thi request");
}

Lạm dụng cấu hình: đừng biến config thành đống hỗn độn — thêm chỉ các sink và level cần thiết.

2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Ghi log có cấu trúc cơ bản với Serilog
Ghi log có cấu trúc cơ bản với Serilog
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION