Bagikan melalui


Pengelogan berkinerja tinggi di .NET

Untuk skenario logging dengan performa tinggi di .NET 6 dan versi yang lebih baru, gunakan LoggerMessageAttribute dengan generasi sumber daya waktu kompilasi. Pendekatan ini memberikan performa terbaik dengan menghilangkan pembungkusan, alokasi sementara, dan penguraian template pesan selama waktu eksekusi.

Pengelogan yang dihasilkan sumber memberikan keunggulan performa berikut daripada metode ekstensi pencatat, seperti LogInformation dan LogDebug:

  • Menghilangkan proses boxing: Metode ekstensi logger memerlukan konversi tipe nilai, seperti int, menjadi object. Pengelogan yang dihasilkan sumber menghindari tinju dengan menggunakan parameter yang ditik dengan kuat.
  • Mengurai templat pada waktu kompilasi: Metode ekstensi pencatat harus mengurai templat pesan (string format bernama) setiap kali pesan log ditulis. Pengelogan yang dihasilkan sumber mengurai templat sekaligus pada waktu kompilasi.
  • Mengurangi alokasi: Generator sumber membuat kode yang dioptimalkan yang meminimalkan alokasi objek dan penggunaan memori sementara.

Aplikasi sampel menunjukkan fitur pencatatan log berkinerja tinggi dengan layanan pemroses antrian prioritas. Aplikasi memproses item kerja dalam urutan prioritas. Saat operasi ini terjadi, pesan log dihasilkan menggunakan pengelogan yang dihasilkan sumber.

Petunjuk / Saran

Semua contoh kode sumber pencatatan log tersedia di Sampel Browser untuk diunduh. Untuk informasi selengkapnya, lihat Menelusuri sampel kode: Pengelogan di .NET.

Menentukan pesan pencatat dengan pembuatan sumber

Untuk membuat pesan log berkinerja tinggi di .NET 6 dan yang lebih baru, tentukan partial metode yang dihiasi dengan LoggerMessageAttribute. Generator sumber membuat implementasi pada waktu kompilasi.

Metode pengelogan dasar

Untuk pesan log sederhana, tentukan metode parsial dengan atribut yang menentukan ID peristiwa, tingkat log, dan templat pesan:

public static partial class Log
{
    [LoggerMessage(
        EventId = 13,
        Level = LogLevel.Critical,
        Message = "Epic failure processing item!")]
    public static partial void FailedToProcessWorkItem(
        ILogger logger, Exception ex);
}

Templat pesan menggunakan placeholder yang diisi oleh parameter dari metode. Nama placeholder harus deskriptif dan konsisten di seluruh templat. Mereka berfungsi sebagai nama properti dalam data log terstruktur. Kami merekomendasikan casing Pascal untuk nama placeholder. Misalnya, {Item}, {DateTime}.

Panggil metode pengelogan dari kode Anda. Misalnya, ketika pengecualian terjadi selama pemrosesan item kerja:

try
{
    // Process work item.
}
catch (Exception ex)
{
    Log.FailedToProcessWorkItem(logger, ex);
}

Kode ini menghasilkan output konsol seperti:

crit: WorkerServiceOptions.Example.Worker[13]
      Epic failure processing item!
      System.Exception: Failed to verify communications.

Pengelogan dengan parameter

Untuk meneruskan parameter ke dalam pesan log, tambahkan parameter tersebut sebagai parameter dalam metode. Nama parameter cocok dengan placeholder dalam templat pesan:

public static partial class Log
{
    [LoggerMessage(
        EventId = 1,
        Level = LogLevel.Information,
        Message = "Processing priority item: {Item}")]
    public static partial void PriorityItemProcessed(
        ILogger logger, WorkItem item);
}

Panggil metode dengan nilai pencatat dan parameter:

var workItem = queue.Dequeue();
Log.PriorityItemProcessed(logger, workItem);

Kode ini menghasilkan output konsol seperti:

info: WorkerServiceOptions.Example.Worker[1]
      Processing priority item: Priority-Extreme (50db062a-9732-4418-936d-110549ad79e4): 'Verify communications'

Penyimpanan pencatatan log terstruktur dapat menggunakan nama peristiwa ketika disertakan dengan ID peristiwa untuk memperkaya pencatatan log. Misalnya, Serilog menggunakan nama peristiwa.

Mengatur cakupan pesan logger dengan pembuatan kode sumber

Anda dapat menentukan cakupan log untuk membungkus serangkaian pesan log dengan konteks tambahan. Dengan pengelogan yang dihasilkan oleh sumber, Anda menggabungkan metode LoggerMessageAttribute dengan metode ILogger.BeginScope standar.

Aktifkan IncludeScopes di bagian pencatat konsol appsettings.json:

{
    "Logging": {
        "Console": {
            "IncludeScopes": true
        },
        "LogLevel": {
            "Default": "Information",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Information"
        }
    }
}

Buat metode pengelogan yang dihasilkan dari sumber dan bungkus dalam cakupan menggunakan BeginScope:

public static partial class Log
{
    [LoggerMessage(
        EventId = 1,
        Level = LogLevel.Information,
        Message = "Processing priority item: {Item}")]
    public static partial void PriorityItemProcessed(
        ILogger logger, WorkItem item);
}

Gunakan metode pencatatan log dalam ruang lingkup dalam kode aplikasi Anda.

using (_logger.BeginScope("Processing scope, started at: {DateTime}", DateTime.Now))
{
    Log.PriorityItemProcessed(_logger, workItem);
}

Periksa pesan log di output konsol aplikasi. Hasil berikut menunjukkan urutan prioritas pesan log dengan pesan cakupan log yang disertakan:

info: WorkerServiceOptions.Example.Worker[1]
      => Processing scope, started at: 04/11/2024 11:27:52
      Processing priority item: Priority-Extreme (7d153ef9-8894-4282-836a-8e5e38319fb3): 'Verify communications'

Pendekatan warisan: LoggerMessage.Define (untuk .NET Framework dan .NET Core 3.1)

Sebelum pengelogan yang dihasilkan sumber diperkenalkan di .NET 6, pendekatan pengelogan berkinerja tinggi yang direkomendasikan adalah menggunakan LoggerMessage.Define metode untuk membuat delegasi yang dapat di-cache. Meskipun pendekatan ini masih didukung untuk kompatibilitas ke belakang, kode baru harus menggunakan logging yang dihasilkan oleh sumber dengan LoggerMessageAttribute sebagai gantinya.

Kelas LoggerMessage mengekspos fungsionalitas untuk membuat delegasi yang dapat di-cache yang memerlukan lebih sedikit alokasi objek dan mengurangi overhead komputasi dibandingkan dengan metode ekstensi pencatat, seperti LogInformation dan LogDebug. LoggerMessage memberikan keunggulan performa berikut daripada metode ekstensi pencatat:

  • Metode ekstensi pencatat memerlukan jenis nilai "tinju" (mengonversi), seperti int, menjadi object. Pola menghindari LoggerMessage tinju dengan menggunakan bidang statis Action dan metode ekstensi dengan parameter yang sangat ditik.
  • Metode ekstensi pencatat harus mengurai templat pesan (string format bernama) setiap kali pesan log ditulis. LoggerMessage hanya memerlukan penguraian templat sekali ketika pesan ditentukan.

Nota

Jika Anda mempertahankan kode yang menggunakan LoggerMessage.Define, pertimbangkan untuk bermigrasi ke pengelogan yang dihasilkan sumber. Untuk aplikasi .NET Framework atau .NET Core 3.1, lanjutkan menggunakan LoggerMessage.Define.

Menentukan pesan pencatat

Gunakan Define(LogLevel, EventId, String) untuk membuat Action delegasi untuk mencatat pesan. Define kelebihan beban mengizinkan meneruskan hingga enam parameter jenis ke string format bernama (templat).

String yang disediakan untuk Define metode adalah templat dan bukan string terinterpolasi. Tempat penampung diisi sesuai urutan tipe yang ditentukan. Nama pengganti dalam templat harus deskriptif dan konsisten di seluruh templat. Mereka berfungsi sebagai nama properti dalam data log terstruktur. Kami merekomendasikan casing Pascal untuk nama placeholder. Misalnya, {Item}, {DateTime}.

Setiap pesan log disimpan Action di bidang statis yang dibuat oleh LoggerMessage.Define. Misalnya, aplikasi sampel membuat bidang untuk menjelaskan pesan log untuk pemrosesan item kerja:

private static readonly Action<ILogger, Exception> s_failedToProcessWorkItem;

Untuk Action, tentukan:

  • Tingkatan log.
  • Pengidentifikasi peristiwa unik (EventId) dengan nama metode ekstensi statis.
  • Templat pesan (string format bernama).

Saat item kerja dikeluarkan dari antrean untuk diproses, aplikasi layanan pekerja mengatur:

  • Tingkat log ke LogLevel.Critical.
  • ID Peristiwa untuk 13 dengan nama metode FailedToProcessWorkItem.
  • Templat pesan (string format bernama) ke string.
s_failedToProcessWorkItem = LoggerMessage.Define(
    LogLevel.Critical,
    new EventId(13, nameof(FailedToProcessWorkItem)),
    "Epic failure processing item!");

Metode LoggerMessage.Define ini digunakan untuk mengonfigurasi dan menentukan Action delegasi, yang mewakili pesan log.

Penyimpanan pencatatan log terstruktur dapat menggunakan nama peristiwa ketika disertakan dengan ID peristiwa untuk memperkaya pencatatan log. Misalnya, Serilog menggunakan nama peristiwa.

Action dipanggil melalui metode ekstensi bertipe kuat. Metode PriorityItemProcessed mencatat pesan setiap kali item kerja diproses. FailedToProcessWorkItem dipanggil jika dan ketika pengecualian terjadi:

protected override async Task ExecuteAsync(
    CancellationToken stoppingToken)
{
    using (IDisposable? scope = logger.ProcessingWorkScope(DateTime.Now))
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                WorkItem? nextItem = priorityQueue.ProcessNextHighestPriority();

                if (nextItem is not null)
                {
                    logger.PriorityItemProcessed(nextItem);
                }
            }
            catch (Exception ex)
            {
                logger.FailedToProcessWorkItem(ex);
            }

            await Task.Delay(1_000, stoppingToken);
        }
    }
}

Periksa output konsol aplikasi:

crit: WorkerServiceOptions.Example.Worker[13]
      Epic failure processing item!
      System.Exception: Failed to verify communications.
         at WorkerServiceOptions.Example.Worker.ExecuteAsync(CancellationToken stoppingToken) in
         ..\Worker.cs:line 27

Untuk meneruskan parameter ke pesan log, tentukan hingga enam jenis saat membuat bidang statis. Aplikasi sampel mencatat detail item kerja saat memproses item dengan menentukan WorkItem jenis untuk Action bidang :

private static readonly Action<ILogger, WorkItem, Exception> s_processingPriorityItem;

Template pesan log dari delegasi menerima nilai placeholder dari tipe yang disediakan. Aplikasi sampel menentukan delegasi untuk menambahkan item kerja di mana parameter item adalah WorkItem:

s_processingPriorityItem = LoggerMessage.Define<WorkItem>(
    LogLevel.Information,
    new EventId(1, nameof(PriorityItemProcessed)),
    "Processing priority item: {Item}");

Metode ekstensi statis untuk pengelogan bahwa item kerja sedang diproses, PriorityItemProcessed, menerima nilai argumen item kerja dan meneruskannya ke Action delegasi:

public static void PriorityItemProcessed(
    this ILogger logger, WorkItem workItem) =>
    s_processingPriorityItem(logger, workItem, default!);

Dalam metode servis pekerja ExecuteAsync, PriorityItemProcessed dipanggil untuk mencatat pesan:

protected override async Task ExecuteAsync(
    CancellationToken stoppingToken)
{
    using (IDisposable? scope = logger.ProcessingWorkScope(DateTime.Now))
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                WorkItem? nextItem = priorityQueue.ProcessNextHighestPriority();

                if (nextItem is not null)
                {
                    logger.PriorityItemProcessed(nextItem);
                }
            }
            catch (Exception ex)
            {
                logger.FailedToProcessWorkItem(ex);
            }

            await Task.Delay(1_000, stoppingToken);
        }
    }
}

Periksa output konsol aplikasi:

info: WorkerServiceOptions.Example.Worker[1]
      Processing priority item: Priority-Extreme (50db062a-9732-4418-936d-110549ad79e4): 'Verify communications'

Pengoptimalan tingkat log yang terkendali

Anda dapat mengoptimalkan kinerja dengan memeriksa LogLevel dan ILogger.IsEnabled(LogLevel) sebelum memanggil metode Log* yang sesuai. Saat pencatatan tidak dikonfigurasi untuk LogLevel yang diberikan, ILogger.Log tidak dipanggil. Selain itu, boxing tipe nilai dan alokasi object[] (untuk parameter) dihindari.

Untuk informasi selengkapnya, lihat:

Lihat juga