Pengelogan di C# dan .NET

.NET mendukung pengelogan terstruktur berkinerja tinggi melalui ILogger API untuk membantu memantau perilaku aplikasi dan mendiagnosis masalah. Log dapat ditulis ke tujuan yang berbeda dengan mengonfigurasi penyedia pengelogan yang berbeda. Penyedia pengelogan dasar juga bawaan dan ada banyak penyedia pihak ketiga yang tersedia.

Memulai

Contoh pertama ini menunjukkan dasar-dasarnya, tetapi hanya cocok untuk aplikasi konsol sepele. Di bagian berikutnya, Anda akan melihat cara meningkatkan kode dengan mempertimbangkan skala, performa, konfigurasi, dan pola pemrograman umum.

using Microsoft.Extensions.Logging;

using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");

Contoh sebelumnya:

  • Membuat sebuah ILoggerFactory. Menyimpan ILoggerFactory semua konfigurasi yang menentukan tempat pesan log dikirim. Dalam hal ini, Anda mengonfigurasi penyedia pengelogan konsol sehingga pesan log ditulis ke konsol.
  • ILogger Membuat dengan kategori bernama "Program". Kategori adalah string yang terkait dengan setiap pesan yang dicatat oleh ILogger objek. Ini digunakan untuk mengelompokkan pesan log dari kelas (atau kategori) yang sama bersama-sama saat mencari atau memfilter log.
  • LogInformation Panggilan untuk mencatat pesan di tingkat tersebutInformation. Tingkat log menunjukkan tingkat keparahan peristiwa yang dicatat dan digunakan untuk memfilter pesan log yang kurang penting. Entri log juga menyertakan templat"Hello World! Logging is {Description}." pesan dan pasangan Description = funkunci-nilai . Nama kunci (atau tempat penampung) berasal dari kata di dalam kurung kurawal dalam templat dan nilai berasal dari argumen metode yang tersisa.

File proyek untuk contoh ini mencakup dua paket NuGet:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
  </ItemGroup>

</Project>

Tip

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

Pengelogan di aplikasi non-sepele

Ada beberapa perubahan yang harus Anda pertimbangkan untuk membuat contoh sebelumnya saat pengelogan dalam skenario yang kurang sepele:

using Microsoft.Extensions.Logging;

internal partial class Program
{
    static void Main(string[] args)
    {
        using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
        ILogger logger = factory.CreateLogger("Program");
        LogStartupMessage(logger, "fun");
    }

    [LoggerMessage(Level = LogLevel.Information, Message = "Hello World! Logging is {Description}.")]
    static partial void LogStartupMessage(ILogger logger, string description);
}
  • Praktik yang direkomendasikan untuk nama kategori log adalah menggunakan nama kelas yang sepenuhnya memenuhi syarat yang membuat pesan log. Ini membantu menghubungkan pesan log kembali ke kode yang menghasilkannya dan menawarkan tingkat kontrol yang baik saat memfilter log. CreateLoggerType menerima untuk membuat penamaan ini mudah dilakukan.
using Microsoft.Extensions.Logging;

internal class Program
{
    static void Main(string[] args)
    {
        using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
        ILogger logger = factory.CreateLogger<Program>();
        logger.LogInformation("Hello World! Logging is {Description}.", "fun");
    }
}
using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;

using ILoggerFactory factory = LoggerFactory.Create(builder =>
{
    builder.AddOpenTelemetry(logging =>
    {
        logging.AddOtlpExporter();
    });
});
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");

Integrasi dengan host dan injeksi dependensi

Jika aplikasi Anda menggunakan Dependency Injection (DI) atau host seperti ASP. WebApplication NET atau Host Generik maka Anda harus menggunakan ILoggerFactory objek dan ILogger dari kontainer DI daripada membuatnya secara langsung.

Mendapatkan ILogger dari DI

Contoh ini mendapatkan objek ILogger di aplikasi yang dihosting menggunakan API Minimal ASP.NET:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<ExampleHandler>();

var app = builder.Build();

var handler = app.Services.GetRequiredService<ExampleHandler>();
app.MapGet("/", handler.HandleRequest);

app.Run();

partial class ExampleHandler(ILogger<ExampleHandler> logger)
{
    public string HandleRequest()
    {
        LogHandleRequest(logger);
        return "Hello World";
    }

    [LoggerMessage(LogLevel.Information, "ExampleHandler.HandleRequest was called")]
    public static partial void LogHandleRequest(ILogger logger);
}

Contoh sebelumnya:

  • Membuat layanan singleton yang disebut ExampleHandler dan memetakan permintaan web masuk untuk menjalankan ExampleHandler.HandleRequest fungsi.
  • Baris 8 mendefinisikan konstruktor utama untuk ExampleHandler, fitur yang ditambahkan di C# 12. Menggunakan konstruktor C# gaya lama akan bekerja sama baiknya tetapi sedikit lebih verbose.
  • Konstruktor mendefinisikan parameter jenis ILogger<ExampleHandler>. ILogger<TCategoryName> berasal dari ILogger dan menunjukkan kategori ILogger mana yang dimiliki objek. Kontainer DI menemukan dengan kategori yang ILogger benar dan menyediakannya sebagai argumen konstruktor. Jika belum ada ILogger dengan kategori tersebut, kontainer DI secara otomatis membuatnya dari ILoggerFactory penyedia layanan.
  • Parameter logger yang diterima dalam konstruktor digunakan untuk pengelogan HandleRequest dalam fungsi.

ILoggerFactory yang disediakan host

Penyusun host menginisialisasi konfigurasi default, lalu menambahkan objek yang dikonfigurasi ILoggerFactory ke kontainer DI host saat host dibuat. Sebelum host dibuat, Anda dapat menyesuaikan konfigurasi pengelogan melalui HostApplicationBuilder.Logging, , WebApplicationBuilder.Loggingatau API serupa di host lain. Host juga menerapkan konfigurasi pengelogan dari sumber konfigurasi default sebagai variabel appsettings.json dan lingkungan. Untuk informasi selengkapnya, lihat Konfigurasi di .NET.

Contoh ini diperluas pada yang sebelumnya untuk menyesuaikan yang ILoggerFactory disediakan oleh WebApplicationBuilder. Ini menambahkan OpenTelemetry sebagai penyedia pengelogan yang mengirimkan log melalui OTLP (protokol OpenTelemetry):

var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddOpenTelemetry(logging => logging.AddOtlpExporter());
builder.Services.AddSingleton<ExampleHandler>();
var app = builder.Build();

Membuat ILoggerFactory dengan DI

Jika Anda menggunakan kontainer DI tanpa host, gunakan AddLogging untuk mengonfigurasi dan menambahkan ILoggerFactory ke kontainer.

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

// Add services to the container including logging
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddConsole());
services.AddSingleton<ExampleService>();
IServiceProvider serviceProvider = services.BuildServiceProvider();

// Get the ExampleService object from the container
ExampleService service = serviceProvider.GetRequiredService<ExampleService>();

// Do some pretend work
service.DoSomeWork(10, 20);

class ExampleService(ILogger<ExampleService> logger)
{
    public void DoSomeWork(int x, int y)
    {
        logger.LogInformation("DoSomeWork was called. x={X}, y={Y}", x, y);
    }
}

Contoh sebelumnya:

  • Membuat kontainer layanan DI yang berisi yang dikonfigurasi ILoggerFactory untuk menulis ke konsol
  • Menambahkan singleton ExampleService ke kontainer
  • Membuat instans dari ExampleService kontainer DI yang juga secara otomatis membuat ILogger<ExampleService> untuk digunakan sebagai argumen konstruktor.
  • Dipanggil ExampleService.DoSomeWork yang menggunakan ILogger<ExampleService> untuk mencatat pesan ke konsol.

Mengonfigurasi pengelogan

Konfigurasi pengelogan diatur dalam kode atau melalui sumber eksternal, seperti, file konfigurasi dan variabel lingkungan. Menggunakan konfigurasi eksternal bermanfaat jika memungkinkan karena dapat diubah tanpa membangun kembali aplikasi. Namun, beberapa tugas, seperti mengatur penyedia pengelogan, hanya dapat dikonfigurasi dari kode.

Mengonfigurasi pengelogan tanpa kode

Untuk aplikasi yang menggunakan host, konfigurasi pengelogan umumnya disediakan oleh "Logging" bagian appsettings.{Environment}.json file. Untuk aplikasi yang tidak menggunakan host, sumber konfigurasi eksternal disiapkan secara eksplisit atau dikonfigurasi dalam kode sebagai gantinya.

Appsetting berikut . file Development.json dihasilkan oleh templat layanan .NET Worker:

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

Dalam JSON sebelumnya:

  • Kategori "Default"tingkat log , "Microsoft", dan "Microsoft.Hosting.Lifetime" ditentukan.
  • Nilai "Default" diterapkan ke semua kategori yang tidak ditentukan lain, secara efektif membuat semua nilai default untuk semua kategori "Information". Anda dapat mengambil alih perilaku ini dengan menentukan nilai untuk kategori.
  • Kategori "Microsoft" berlaku untuk semua kategori yang dimulai dengan "Microsoft".
  • Kategori "Microsoft" mencatat pada tingkat Warning log dan yang lebih tinggi.
  • Kategori "Microsoft.Hosting.Lifetime" ini lebih spesifik daripada "Microsoft" kategori, sehingga "Microsoft.Hosting.Lifetime" log kategori pada tingkat "Information" log dan lebih tinggi.
  • Penyedia log tertentu tidak ditentukan, jadi LogLevel berlaku untuk semua penyedia pengelogan yang diaktifkan kecuali untuk Windows EventLog.

Properti Logging dapat memiliki LogLevel dan properti penyedia log. LogLevel menentukan tingkat minimum yang akan dicatat untuk kategori yang dipilih. Di JSON sebelumnya, Information dan Warning tingkat log ditentukan. LogLevel menunjukkan tingkat keparahan log dan memiliki rentang 0 hingga 6:

Trace = 0, Debug = 1, Information = 2, Warning = 3, Error = 4, Critical = 5, dan None = 6.

Bila LogLevel ditetapkan, pengelogan diaktifkan untuk pesan pada tingkat yang ditentukan dan lebih tinggi. Di JSON sebelumnya, Default kategori dicatat dan Information lebih tinggi. Misalnya, pesan Information, Warning, Error, dan Critical dicatat. Jika tidak ada LogLevel yang ditentukan, pengelogan diatur ke default, yakni tingkat Information. Untuk informasi lebih lanjut, lihat Tingkat log.

Properti penyedia dapat menentukan properti LogLevel. LogLevel di bawah penyedia menentukan tingkat yang perlu dicatat untuk penyedia tersebut, dan menimpa pengaturan log non-penyedia. Pertimbangkan file appsettings.json berikut:

{
    "Logging": {
        "LogLevel": {
            "Default": "Error",
            "Microsoft": "Warning"
        },
        "Debug": {
            "LogLevel": {
                "Default": "Information",
                "Microsoft.Hosting": "Trace"
            }
        },
        "EventSource": {
            "LogLevel": {
                "Default": "Warning"
            }
        }
    }
}

Pengaturan di Logging.{ProviderName}.LogLevel menimpa pengaturan di Logging.LogLevel. Di JSON sebelumnya, Debug tingkat log default penyedia diatur ke Information:

Logging:Debug:LogLevel:Default:Information

Pengaturan sebelumnya menetapkan tingkat log Information untuk setiap kategori Logging:Debug: kecuali Microsoft.Hosting. Ketika kategori tertentu dicantumkan, kategori tersebut menimpa kategori default. Di JSON sebelumnya, Logging:Debug:LogLevel kategori "Microsoft.Hosting" dan "Default" ambil alih pengaturan di Logging:LogLevel

Tingkat log minimum dapat ditentukan untuk salah satu dari:

  • Penyedia tertentu: Misalnya, Logging:EventSource:LogLevel:Default:Information
  • Kategori tertentu: Misalnya, Logging:LogLevel:Microsoft:Warning
  • Semua penyedia dan semua kategori: Logging:LogLevel:Default:Warning

Setiap log di bawah tingkat minimum tidak:

  • Diteruskan ke penyedia.
  • Dicatat atau ditampilkan.

Untuk menyembunyikan semua log, tentukan LogLevel.None. LogLevel.None memiliki nilai 6, yang lebih tinggi dari LogLevel.Critical (5).

Jika penyedia mendukung cakupan log, IncludeScopes menunjukkan apakah cakupan diaktifkan. Untuk informasi lebih lanjut, lihat cakupan log

File appsettings.json berikut berisi pengaturan untuk semua penyedia bawaan:

{
    "Logging": {
        "LogLevel": {
            "Default": "Error",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Warning"
        },
        "Debug": {
            "LogLevel": {
                "Default": "Information"
            }
        },
        "Console": {
            "IncludeScopes": true,
            "LogLevel": {
                "Microsoft.Extensions.Hosting": "Warning",
                "Default": "Information"
            }
        },
        "EventSource": {
            "LogLevel": {
                "Microsoft": "Information"
            }
        },
        "EventLog": {
            "LogLevel": {
                "Microsoft": "Information"
            }
        },
        "AzureAppServicesFile": {
            "IncludeScopes": true,
            "LogLevel": {
                "Default": "Warning"
            }
        },
        "AzureAppServicesBlob": {
            "IncludeScopes": true,
            "LogLevel": {
                "Microsoft": "Information"
            }
        },
        "ApplicationInsights": {
            "LogLevel": {
                "Default": "Information"
            }
        }
    }
}

Dalam sampel sebelumnya:

  • Kategori dan tingkat bukanlah nilai yang disarankan. Sampel disediakan untuk menunjukkan semua penyedia default.
  • Pengaturan di Logging.{ProviderName}.LogLevel menimpa pengaturan di Logging.LogLevel. Misalnya, tingkat di Debug.LogLevel.Default menggantikan tingkat di LogLevel.Default.
  • Alias setiap penyedia digunakan. Setiap penyedia mendefinisikan alias yang dapat digunakan dalam konfigurasi, yang menggantikan nama jenis yang sepenuhnya memenuhi syarat. Alias penyedia bawaan adalah:
    • Console
    • Debug
    • EventSource
    • EventLog
    • AzureAppServicesFile
    • AzureAppServicesBlob
    • ApplicationInsights

Tetapkan tingkat log berdasarkan baris perintah, variabel lingkungan, dan konfigurasi lainnya

Tingkat log dapat diatur oleh penyedia konfigurasi mana pun. Misalnya, Anda dapat membuat variabel lingkungan yang bertahan bernama Logging:LogLevel:Microsoft dengan nilai Information.

Buat dan tetapkan variabel lingkungan yang bertahan, mengingat nilai tingkat log.

:: Assigns the env var to the value
setx "Logging__LogLevel__Microsoft" "Information" /M

Dalam instans baru Prompt Perintah, baca variabel lingkungan.

:: Prints the env var value
echo %Logging__LogLevel__Microsoft%

Pengaturan lingkungan sebelumnya dipertahankan di lingkungan. Untuk menguji pengaturan saat menggunakan aplikasi yang dibuat dengan templat layanan .NET Worker, gunakan dotnet run perintah di direktori proyek setelah variabel lingkungan ditetapkan.

dotnet run

Tip

Setelah menetapkan variabel lingkungan, mulai ulang lingkungan pengembangan terpadu (IDE) Anda untuk memastikan bahwa variabel lingkungan yang baru ditambahkan tersedia.

Pada Azure App Service, pilih Pengaturan aplikasi baru di halaman Pengaturan > Konfigurasi. Pengaturan aplikasi Azure App Service:

  • Dienkripsi saat tidak aktif dan ditransmisikan melalui saluran terenkripsi.
  • Diekspos sebagai variabel lingkungan.

Untuk informasi selengkapnya tentang mengatur nilai konfigurasi .NET menggunakan variabel lingkungan, lihat variabel lingkungan.

Mengonfigurasi pengelogan dengan kode

Untuk mengonfigurasi pengelogan dalam kode, gunakan ILoggingBuilder API. Ini dapat diakses dari berbagai tempat:

Contoh ini menunjukkan pengaturan penyedia pengelogan konsol dan beberapa filter.

using Microsoft.Extensions.Logging;

using var loggerFactory = LoggerFactory.Create(static builder =>
{
    builder
        .AddFilter("Microsoft", LogLevel.Warning)
        .AddFilter("System", LogLevel.Warning)
        .AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
        .AddConsole();
});

ILogger logger = loggerFactory.CreateLogger<Program>();
logger.LogDebug("Hello {Target}", "Everyone");

Dalam contoh AddFilter sebelumnya digunakan untuk menyesuaikan tingkat log yang diaktifkan untuk berbagai kategori. AddConsole digunakan untuk menambahkan penyedia pengelogan konsol. Secara default, log dengan Debug tingkat keparahan tidak diaktifkan, tetapi karena konfigurasi menyesuaikan filter, pesan debug "Halo Semua Orang" ditampilkan di konsol.

Bagaimana aturan pemfilteran diterapkan

Saat objek ILogger<TCategoryName> dibuat, objek ILoggerFactory memilih satu aturan per penyedia untuk diterapkan ke pencatat tersebut. Semua pesan yang ditulis oleh instans ILogger difilter berdasarkan aturan yang dipilih. Aturan paling spesifik untuk setiap pasangan penyedia dan kategori dipilih dari aturan yang tersedia.

Algoritma berikut digunakan untuk setiap penyedia saat ILogger dibuat untuk kategori tertentu:

  • Pilih semua aturan yang cocok dengan penyedia atau aliasnya. Jika tidak ditemukan kecocokan, pilih semua aturan dengan penyedia yang kosong.
  • Dari hasil langkah sebelumnya, pilih aturan dengan awalan kategori yang cocok terpanjang. Jika tidak ada kecocokan yang ditemukan, pilih semua aturan yang tidak menentukan kategori.
  • Jika beberapa aturan dipilih, ambil yang terakhir.
  • Jika tidak ada aturan yang dipilih, gunakan LoggingBuilderExtensions.SetMinimumLevel(ILoggingBuilder, LogLevel) untuk menentukan tingkat pengelogan minimum.

Kategori log

Saat objek ILogger dibuat, kategori ditentukan. Kategori tersebut disertakan dengan setiap pesan log yang dibuat oleh instans ILogger tersebut. String kategori bersifat arbitrer, tetapi konvensinya adalah menggunakan nama kelas yang sepenuhnya memenuhi syarat. Misalnya, dalam aplikasi dengan layanan yang ditentukan seperti objek berikut, kategorinya mungkin "Example.DefaultService":

namespace Example
{
    public class DefaultService : IService
    {
        private readonly ILogger<DefaultService> _logger;

        public DefaultService(ILogger<DefaultService> logger) =>
            _logger = logger;

        // ...
    }
}

Jika kategorisasi lebih lanjut diinginkan, konvensinya adalah menggunakan nama hierarkis dengan menambahkan subkategori ke nama kelas yang sepenuhnya memenuhi syarat, dan secara eksplisit menentukan kategori menggunakan LoggerFactory.CreateLogger:

namespace Example
{
    public class DefaultService : IService
    {
        private readonly ILogger _logger;

        public DefaultService(ILoggerFactory loggerFactory) =>
            _logger = loggerFactory.CreateLogger("Example.DefaultService.CustomCategory");

        // ...
    }
}

Panggilan CreateLogger dengan nama tetap dapat berguna saat digunakan dalam beberapa kelas/jenis sehingga peristiwa dapat diatur berdasarkan kategori.

ILogger<T> sama dengan memanggil CreateLogger, dengan nama jenis T yang sepenuhnya memenuhi syarat.

Tingkat log

Tabel berikut mencantumkan nilai LogLevel, metode ekstensi Log{LogLevel} yang praktis, dan penggunaan yang disarankan:

LogLevel Nilai Metode Deskripsi
Jejak 0 LogTrace Berisi pesan paling terperinci. Pesan ini mungkin berisi data aplikasi yang sensitif. Pesan-pesan ini dinonaktifkan secara default dan tidak boleh diaktifkan dalam produksi.
Debug 1 LogDebug Untuk penelusuran kesalahan dan pengembangan. Gunakan dengan hati-hati dalam produksi karena volume yang tinggi.
Informasi 2 LogInformation Melacak alur umum aplikasi. Mungkin memiliki nilai jangka panjang.
Peringatan 3 LogWarning Untuk peristiwa yang tidak normal atau tidak terduga. Biasanya menyertakan kesalahan atau kondisi yang tidak menyebabkan aplikasi gagal.
Kesalahan 4 LogError Untuk kesalahan dan pengecualian yang tidak dapat ditangani. Pesan-pesan ini menunjukkan kegagalan dalam operasi atau permintaan saat ini, bukan kegagalan di seluruh aplikasi.
Kritis 5 LogCritical Untuk kegagalan yang membutuhkan perhatian sesegera mungkin. Contoh: skenario kehilangan data, ruang disk yang habis.
Tidak 6 Menentukan bahwa tidak ada pesan yang harus ditulis.

Pada tabel sebelumnya, LogLevel dicantumkan dari tingkat keparahan terendah hingga tertinggi.

Parameter pertama metode Log, LogLevel, menunjukkan tingkat keparahan log. Alih-alih memanggil Log(LogLevel, ...), sebagian besar pengembang memanggil metode ekstensi Log{LogLevel}. Metode Log{LogLevel} ekstensi memanggil Log metode dan menentukan LogLevel. Misalnya, dua panggilan pengelogan berikut secara fungsional setara dan menghasilkan log yang sama:

public void LogDetails()
{
    var logMessage = "Details for log.";

    _logger.Log(LogLevel.Information, AppLogEvents.Details, logMessage);
    _logger.LogInformation(AppLogEvents.Details, logMessage);
}

AppLogEvents.Details adalah ID peristiwa, dan secara implisit diwakili oleh nilai konstanta Int32 . AppLogEvents adalah kelas yang mengekspos berbagai konstanta pengidentifikasi bernama dan ditampilkan di bagian ID peristiwa Log.

Kode berikut membuat log Information dan Warning:

public async Task<T> GetAsync<T>(string id)
{
    _logger.LogInformation(AppLogEvents.Read, "Reading value for {Id}", id);

    var result = await _repository.GetAsync(id);
    if (result is null)
    {
        _logger.LogWarning(AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
    }

    return result;
}

Dalam kode sebelumnya, parameter Log{LogLevel} pertama, AppLogEvents.Read, adalah ID peristiwa log. Parameter kedua adalah template pesan dengan tempat penampung untuk nilai argumen yang disediakan oleh parameter metode yang tersisa. Parameter metode dijelaskan di bagian templat pesan nanti di artikel ini.

Konfigurasikan tingkat log yang sesuai dan panggil metode yang benar Log{LogLevel} untuk mengontrol berapa banyak output log yang ditulis ke media penyimpanan tertentu. Contohnya:

  • Dalam produksi:
    • Pengelogan pada tingkat Trace atau Debug menghasilkan pesan log terperinci dengan volume yang tinggi. Untuk mengontrol biaya dan tidak melebihi batas penyimpanan data, catat pesan tingkat Trace dan Debug ke penyimpanan data bervolume tinggi dan berbiaya rendah. Pertimbangkan untuk membatasi Trace dan Debug pada kategori tertentu.
    • Pengelogan pada tingkat Warning sampai Critical akan menghasilkan beberapa pesan log.
      • Batas biaya dan penyimpanan biasanya tidak menjadi masalah.
      • Beberapa log memungkinkan fleksibilitas yang lebih besar dalam hal pilihan penyimpanan data.
  • Dalam pengembangan:
    • Atur ke Warning.
    • Tambahkan pesan Trace atau Debug saat memecahkan masalah. Untuk membatasi output, atur Trace atau Debug hanya untuk kategori yang sedang diselidiki.

Set Logging:Console:LogLevel:Microsoft:InformationJSON berikut :

{
    "Logging": {
        "LogLevel": {
            "Microsoft": "Warning"
        },
        "Console": {
            "LogLevel": {
                "Microsoft": "Information"
            }
        }
    }
}

ID peristiwa log

Setiap log dapat menentukan pengidentifikasi peristiwa, EventId adalah struktur dengan Id properti readonly opsional Name dan . Kode sumber sampel menggunakan AppLogEvents kelas untuk menentukan ID peristiwa:

using Microsoft.Extensions.Logging;

internal static class AppLogEvents
{
    internal static EventId Create = new(1000, "Created");
    internal static EventId Read = new(1001, "Read");
    internal static EventId Update = new(1002, "Updated");
    internal static EventId Delete = new(1003, "Deleted");

    // These are also valid EventId instances, as there's
    // an implicit conversion from int to an EventId
    internal const int Details = 3000;
    internal const int Error = 3001;

    internal static EventId ReadNotFound = 4000;
    internal static EventId UpdateNotFound = 4001;

    // ...
}

Tip

Untuk informasi selengkapnya tentang mengonversi int ke EventId, lihat Operator EventId.Implicit(Int32 ke EventId).

ID peristiwa mengaitkan serangkaian peristiwa. Misalnya, semua log yang terkait dengan membaca nilai dari repositori mungkin .1001

Penyedia pengelogan dapat mencatat ID peristiwa di bidang ID, dalam pesan pengelogan, atau tidak sama sekali. Penyedia Debug tidak menampilkan ID peristiwa. Penyedia konsol menunjukkan ID peristiwa dalam tanda kurung setelah kategori:

info: Example.DefaultService.GetAsync[1001]
      Reading value for a1b2c3
warn: Example.DefaultService.GetAsync[4000]
      GetAsync(a1b2c3) not found

Beberapa penyedia pengelogan menyimpan ID peristiwa di bidang, yang memungkinkan pemfilteran pada ID.

Template pesan log

Setiap API log menggunakan template pesan. Template pesan dapat berisi tempat penampung yang argumennya disediakan. Gunakan nama untuk tempat penampung, bukan angka. Urutan tempat penampung, bukan namanya, menentukan parameter mana yang digunakan untuk memberikan nilainya. Dalam kode berikut, nama parameter di luar urutan dalam templat pesan:

string p1 = "param1";
string p2 = "param2";
_logger.LogInformation("Parameter values: {p2}, {p1}", p1, p2);

Kode sebelumnya membuat pesan log dengan nilai parameter secara berurutan:

Parameter values: param1, param2

Catatan

Berhati-hatilah saat menggunakan beberapa tempat penampung dalam satu templat pesan, karena berbasis ordinal. Nama tidak digunakan untuk meratakan argumen ke tempat penampung.

Pendekatan ini memungkinkan penyedia pengelogan untuk mengimplementasikan pengelogan semantik atau terstruktur. Argumen itu sendiri diteruskan ke sistem pengelogan, bukan hanya template pesan yang diformat. Ini memungkinkan penyedia pengelogan untuk menyimpan nilai parameter sebagai bidang. Pertimbangkan metode pencatat berikut:

_logger.LogInformation("Getting item {Id} at {RunTime}", id, DateTime.Now);

Misalnya, saat mencatat ke Azure Table Storage:

  • Setiap entitas Azure Table dapat memiliki properti ID dan RunTime.
  • Tabel dengan properti menyederhanakan kueri pada data yang dicatat. Misalnya, kueri dapat menemukan semua log dalam rentang RunTime tertentu tanpa harus menguraikan waktu habis pesan teks.

Pemformatan templat pesan log

Templat pesan log mendukung pemformatan tempat penampung. Templat bebas untuk menentukan format yang valid untuk argumen jenis yang diberikan. Misalnya, pertimbangkan templat pesan pencatat berikut Information :

_logger.LogInformation("Logged on {PlaceHolderName:MMMM dd, yyyy}", DateTimeOffset.UtcNow);
// Logged on January 06, 2022

Dalam contoh sebelumnya, DateTimeOffset instans adalah jenis yang sesuai dengan PlaceHolderName di templat pesan pencatat. Nama ini bisa apa saja karena nilainya berbasis ordinal. MMMM dd, yyyy Format ini valid untuk jenis tersebutDateTimeOffset.

Untuk informasi selengkapnya tentang DateTime dan DateTimeOffset pemformatan, lihat String format tanggal dan waktu kustom.

Contoh

Contoh berikut menunjukkan cara memformat templat pesan menggunakan {} sintaks tempat penampung. Selain itu, contoh melarikan diri {} dari sintaks tempat penampung ditunjukkan dengan outputnya. Terakhir, interpolasi string dengan tempat penampung templat juga ditampilkan:

logger.LogInformation("Number: {Number}", 1);               // Number: 1
logger.LogInformation("{{Number}}: {Number}", 3);           // {Number}: 3
logger.LogInformation($"{{{{Number}}}}: {{Number}}", 5);    // {Number}: 5

Tip

  • Dalam kebanyakan kasus, Anda harus menggunakan pemformatan templat pesan log saat pengelogan. Penggunaan interpolasi string dapat menyebabkan masalah performa.
  • Aturan analisis kode CA2254: Templat harus berupa ekspresi statis membantu memperingatkan Anda ke tempat di mana pesan log Anda tidak menggunakan pemformatan yang tepat.

Pengecualian log

Metode pencatat memiliki overload yang mengambil parameter pengecualian:

public void Test(string id)
{
    try
    {
        if (id is "none")
        {
            throw new Exception("Default Id detected.");
        }
    }
    catch (Exception ex)
    {
        _logger.LogWarning(
            AppLogEvents.Error, ex,
            "Failed to process iteration: {Id}", id);
    }
}

Pengelogan pengecualian sifatnya khusus untuk penyedia.

Tingkat log default

Jika tingkat log bawaan tidak diatur, nilai tingkat log defaultnya adalah Information.

Misalnya, pertimbangkan aplikasi layanan pekerja berikut:

  • Dibuat dengan templat .NET Worker.
  • appsettings.json dan appsetting. Development.json dihapus atau diganti namanya.

Dengan penyiapan sebelumnya, membuka halaman privasi atau beranda akan menghasilkan banyak pesan Trace, Debug, dan Information dengan Microsoft dalam nama kategori.

Kode berikut menetapkan tingkat log default ketika tingkat log default tidak diatur dalam konfigurasi:

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.SetMinimumLevel(LogLevel.Warning);

using IHost host = builder.Build();

await host.RunAsync();

Fungsi filter

Fungsi filter dipanggil untuk semua penyedia dan kategori yang tidak memiliki aturan yang ditetapkan padanya oleh konfigurasi atau kode:

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.AddFilter((provider, category, logLevel) =>
{
    return provider.Contains("ConsoleLoggerProvider")
        && (category.Contains("Example") || category.Contains("Microsoft"))
        && logLevel >= LogLevel.Information;
});

using IHost host = builder.Build();

await host.RunAsync();

Kode sebelumnya menampilkan log konsol jika kategori berisi Example atau Microsoft dan tingkat log adalah Information atau yang lebih tinggi.

Cakupan log

Cakupan mengelompokkan serangkaian operasi logis. Pengelompokan ini dapat digunakan untuk melampirkan data yang sama ke setiap log yang dibuat sebagai bagian dari kumpulan. Misalnya, setiap log yang dibuat sebagai bagian dari pemrosesan transaksi dapat menyertakan ID transaksi.

Cakupan:

Penyedia berikut mendukung cakupan:

Gunakan cakupan dengan membungkus panggilan pencatat di blok using:

public async Task<T> GetAsync<T>(string id)
{
    T result;
    var transactionId = Guid.NewGuid().ToString();

    using (_logger.BeginScope(new List<KeyValuePair<string, object>>
        {
            new KeyValuePair<string, object>("TransactionId", transactionId),
        }))
    {
        _logger.LogInformation(
            AppLogEvents.Read, "Reading value for {Id}", id);

        var result = await _repository.GetAsync(id);
        if (result is null)
        {
            _logger.LogWarning(
                AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
        }
    }

    return result;
}

JSON berikut memungkinkan cakupan untuk penyedia konsol:

{
    "Logging": {
        "Debug": {
            "LogLevel": {
                "Default": "Information"
            }
        },
        "Console": {
            "IncludeScopes": true,
            "LogLevel": {
                "Microsoft": "Warning",
                "Default": "Information"
            }
        },
        "LogLevel": {
            "Default": "Debug"
        }
    }
}

Kode berikut memungkinkan cakupan untuk penyedia konsol:

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.ClearProviders();
builder.Logging.AddConsole(options => options.IncludeScopes = true);

using IHost host = builder.Build();

await host.RunAsync();

Membuat log di Utama

Kode berikut mencatat di Main dengan mendapatkan instans ILogger dari DI setelah membangun host:

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

using IHost host = Host.CreateApplicationBuilder(args).Build();

var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Host created.");

await host.RunAsync();

Kode sebelumnya bergantung pada dua paket NuGet:

File proyeknya akan terlihat mirip dengan yang berikut ini:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
    <PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
  </ItemGroup>

</Project>

Tidak ada metode pencatat asinkron

Pengelogan harus dilakukan dengan sangat cepat sehingga tidak sebanding dengan biaya performa dari kode asinkron. Jika datastore pengelogan lambat, jangan menulisnya secara langsung. Pertimbangkan untuk terlebih dahulu menulis pesan log ke penyimpanan cepat, lalu memindahkannya ke penyimpanan lambat nanti. Misalnya, saat mencatat ke SQL Server, jangan lakukan secara langsung dalam metode Log, karena metode Log bersifat sinkron. Sebagai gantinya, tambahkan pesan log secara sinkron ke antrean dalam memori dan minta pekerja latar belakang untuk menarik pesan keluar dari antrean untuk melakukan pekerjaan asinkron berupa mendorong data ke SQL Server.

Mengubah tingkat log di aplikasi yang sedang berjalan

Logging API tidak menyertakan skenario untuk mengubah tingkat log saat aplikasi sedang berjalan. Namun, beberapa penyedia konfigurasi mampu untuk memuat ulang konfigurasi, yang langsung berpengaruh pada konfigurasi pengelogan. Misalnya, Penyedia Konfigurasi File memuat ulang konfigurasi pengelogan secara default. Jika konfigurasi diubah dalam kode saat aplikasi berjalan, aplikasi dapat memanggil IConfigurationRoot.Reload untuk memperbarui konfigurasi pengelogan aplikasi.

Paket NuGet

Antarmuka ILogger<TCategoryName> dan ILoggerFactory dan implementasi disertakan dalam sebagian besar .NET SDK sebagai referensi paket implisit. Mereka juga tersedia secara eksplisit dalam paket NuGet berikut ketika tidak direferensikan secara implisit:

Untuk informasi selengkapnya tentang .NET SDK mana yang menyertakan referensi paket implisit, lihat .NET SDK: tabel ke namespace implisit.

Lihat juga