Bagikan melalui


Pengelogan Sederhana

Tip

Anda dapat mengunduh sampel artikel ini dari GitHub.

Pengelogan sederhana Entity Framework Core (EF Core) dapat digunakan untuk dengan mudah mendapatkan log saat mengembangkan dan men-debug aplikasi. Bentuk pengelogan ini membutuhkan konfigurasi minimal dan tidak ada paket NuGet tambahan.

Tip

EF Core juga terintegrasi dengan Microsoft.Extensions.Logging, yang membutuhkan lebih banyak konfigurasi, tetapi seringkali lebih cocok untuk pengelogan dalam aplikasi produksi.

Konfigurasi

Log EF Core dapat diakses dari segala jenis aplikasi melalui penggunaan LogTo saat mengonfigurasi instans DbContext. Konfigurasi ini pada umumnya dilakukan untuk mengambil alih DbContext.OnConfiguring. Contohnya:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(Console.WriteLine);

Secara bergantian, LogTo dapat dipanggil sebagai bagian AddDbContext dari atau saat membuat DbContextOptions instans untuk diteruskan ke DbContext konstruktor.

Tip

OnConfiguring masih dipanggil ketika AddDbContext digunakan atau instans DbContextOptions diteruskan ke konstruktor DbContext. Ini menjadikannya tempat yang ideal untuk menerapkan konfigurasi konteks terlepas dari bagaimana DbContext dibangun.

Mengarahkan log

Pengelogan ke konsol

LogToAction<T> memerlukan delegasi yang menerima string. EF Core akan memanggil delegasi ini dengan string untuk setiap pesan log yang dihasilkan. Kemudian terserah delegasi untuk melakukan sesuatu dengan pesan yang diberikan.

Metode Console.WriteLine ini sering digunakan untuk delegasi ini, seperti yang ditunjukkan di atas. Ini menghasilkan setiap pesan log yang ditulis ke konsol.

Pengelogan ke jendela debug

Debug.WriteLine dapat digunakan untuk mengirim output ke jendela Debug di Visual Studio atau ID Lainnya. Sintaks lambda harus digunakan dalam kasus ini karena kelas dikompilasi Debug dari build rilis. Contohnya:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(message => Debug.WriteLine(message));

Pengelogan ke file

Menulis ke file memerlukan pembuatan atau serupa StreamWriter untuk file. Metode ini WriteLine kemudian dapat digunakan seperti pada contoh lain di atas. Ingatlah untuk memastikan file ditutup dengan bersih dengan membuang penulis ketika konteks dibuang. Contohnya:

private readonly StreamWriter _logStream = new StreamWriter("mylog.txt", append: true);

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(_logStream.WriteLine);

public override void Dispose()
{
    base.Dispose();
    _logStream.Dispose();
}

public override async ValueTask DisposeAsync()
{
    await base.DisposeAsync();
    await _logStream.DisposeAsync();
}

Tip

Pertimbangkan untuk menggunakan Microsoft.Extensions.Logging untuk pengelogan ke file dalam aplikasi produksi.

Mendapatkan pesan terperinci

Data sensitif

Secara default, EF Core tidak akan menyertakan nilai data apa pun dalam pesan pengecualian. Ini karena data tersebut mungkin bersifat rahasia, dan dapat diungkapkan dalam penggunaan produksi jika pengecualian tidak ditangani.

Namun, mengetahui nilai data, terutama untuk kunci, bisa sangat membantu saat men-debug. Ini dapat diaktifkan di EF Core dengan memanggil EnableSensitiveDataLogging(). Contohnya:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .LogTo(Console.WriteLine)
        .EnableSensitiveDataLogging();

Pengecualian kueri terperinci

Untuk alasan performa, EF Core tidak membungkus setiap panggilan untuk membaca nilai dari penyedia database dalam blok try-catch. Namun, ini terkadang menghasilkan pengecualian yang sulit didiagnosis, terutama ketika database mengembalikan NULL ketika tidak diizinkan oleh model.

Mengaktifkan EnableDetailedErrors akan menyebabkan EF memperkenalkan blok try-catch ini dan dengan demikian memberikan kesalahan yang lebih rinci. Contohnya:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .LogTo(Console.WriteLine)
        .EnableDetailedErrors();

Filter

Tingkat log

Setiap pesan log EF Core ditetapkan ke tingkat yang ditentukan oleh LogLevel enum. Secara default, pengelogan sederhana EF Core mencakup setiap pesan pada tingkat atau di Debug atasnya. LogTo dapat diteruskan tingkat minimum yang lebih tinggi untuk memfilter beberapa pesan. Misalnya, meneruskan Information menghasilkan sekumpulan log minimal yang terbatas pada akses database dan beberapa pesan housekeeping.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);

Pesan tertentu

Setiap pesan log diberi EventId. ID ini dapat diakses dari CoreEventId kelas atau RelationalEventId kelas untuk pesan khusus relasional. Penyedia database mungkin juga memiliki ID khusus penyedia di kelas serupa. Misalnya, SqlServerEventId untuk penyedia SQL Server.

LogTo dapat dikonfigurasi untuk hanya mencatat pesan yang terkait dengan satu atau beberapa ID peristiwa. Misalnya, untuk mencatat hanya pesan untuk konteks yang diinisialisasi atau dibuang:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .LogTo(Console.WriteLine, new[] { CoreEventId.ContextDisposed, CoreEventId.ContextInitialized });

Kategori pesan

Setiap pesan log ditetapkan ke kategori pencatat hierarkis bernama. Kategorinya adalah:

Kategori Pesan
Microsoft.EntityFrameworkCore Semua pesan EF Core
Microsoft.EntityFrameworkCore.Database Semua interaksi database
Microsoft.EntityFrameworkCore.Database. Koneksi ion Penggunaan koneksi database
Microsoft.EntityFrameworkCore.Database.Command Penggunaan perintah database
Microsoft.EntityFrameworkCore.Database.Transaction Penggunaan transaksi database
Microsoft.EntityFrameworkCore.Update Menyimpan entitas, tidak termasuk interaksi database
Microsoft.EntityFrameworkCore.Model Semua interaksi model dan metadata
Microsoft.EntityFrameworkCore.Model.Validation Validasi model
Microsoft.EntityFrameworkCore.Query Kueri, tidak termasuk interaksi database
Microsoft.EntityFrameworkCore.Infrastructure Peristiwa umum, seperti pembuatan konteks
Microsoft.EntityFrameworkCore.Scaffolding Rekayasa terbalik database
Microsoft.EntityFrameworkCore.Migrations Migrasi
Microsoft.EntityFrameworkCore.ChangeTracking Interaksi pelacakan perubahan

LogTo dapat dikonfigurasi untuk hanya mencatat pesan dari satu atau beberapa kategori. Misalnya, untuk mencatat hanya interaksi database:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .LogTo(Console.WriteLine, new[] { DbLoggerCategory.Database.Name });

Perhatikan bahwa DbLoggerCategory kelas menyediakan API hierarkis untuk menemukan kategori dan menghindari kebutuhan untuk string hard-code.

Karena kategori bersifat hierarkis, contoh ini menggunakan Database kategori akan menyertakan semua pesan untuk subkategori Database.Connection, , Database.Commanddan Database.Transaction.

Filter khusus

LogTo memungkinkan filter kustom digunakan untuk kasus di mana tidak ada opsi pemfilteran di atas yang cukup. Misalnya, untuk mencatat pesan apa pun pada tingkat Information atau di atasnya, serta pesan untuk membuka dan menutup koneksi:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .LogTo(
            Console.WriteLine,
            (eventId, logLevel) => logLevel >= LogLevel.Information
                                   || eventId == RelationalEventId.ConnectionOpened
                                   || eventId == RelationalEventId.ConnectionClosed);

Tip

Pemfilteran menggunakan filter kustom atau menggunakan salah satu opsi lain yang ditampilkan di sini lebih efisien daripada pemfilteran di LogTo delegasi. Ini karena jika filter menentukan pesan tidak boleh dicatat, maka pesan log bahkan tidak dibuat.

Konfigurasi untuk pesan tertentu

EF Core ConfigureWarnings API memungkinkan aplikasi untuk mengubah apa yang terjadi ketika peristiwa tertentu ditemui. Ini dapat digunakan untuk:

  • Mengubah tingkat log tempat peristiwa dicatat
  • Lewati pengelogan peristiwa sama sekali
  • Melemparkan pengecualian saat peristiwa terjadi

Mengubah tingkat log untuk suatu peristiwa

Contoh sebelumnya menggunakan filter kustom untuk mencatat setiap pesan serta LogLevel.Information dua peristiwa yang ditentukan untuk LogLevel.Debug. Hal yang sama dapat dicapai dengan mengubah tingkat log dari dua Debug peristiwa menjadi Information:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .ConfigureWarnings(
            b => b.Log(
                (RelationalEventId.ConnectionOpened, LogLevel.Information),
                (RelationalEventId.ConnectionClosed, LogLevel.Information)))
        .LogTo(Console.WriteLine, LogLevel.Information);

Menyembunyikan pengelogan peristiwa

Dengan cara yang sama, peristiwa individu dapat ditekan dari pengelogan. Ini sangat berguna untuk mengabaikan peringatan yang telah ditinjau dan dipahami. Contohnya:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .ConfigureWarnings(b => b.Ignore(CoreEventId.DetachedLazyLoadingWarning))
        .LogTo(Console.WriteLine);

Melemparkan untuk suatu peristiwa

Terakhir, EF Core dapat dikonfigurasi untuk dilemparkan untuk peristiwa tertentu. Ini sangat berguna untuk mengubah peringatan menjadi kesalahan. (Memang, ini adalah tujuan ConfigureWarnings asli metode, oleh karena itu namanya.) Misalnya:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .ConfigureWarnings(b => b.Throw(RelationalEventId.MultipleCollectionIncludeWarning))
        .LogTo(Console.WriteLine);

Isi dan pemformatan pesan

Konten default dari LogTo diformat di beberapa baris. Baris pertama berisi metadata pesan:

  • LogLevel sebagai awalan empat karakter
  • Tanda waktu lokal, diformat untuk budaya saat ini
  • EventId dalam formulir yang dapat disalin/ditempelkan untuk mendapatkan anggota dari CoreEventId atau salah satu kelas lainnyaEventId, ditambah nilai ID mentah
  • Kategori peristiwa, seperti yang dijelaskan di atas.

Contohnya:

info: 10/6/2020 10:52:45.581 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "Blogs" (
          "Id" INTEGER NOT NULL CONSTRAINT "PK_Blogs" PRIMARY KEY AUTOINCREMENT,
          "Name" INTEGER NOT NULL
      );
dbug: 10/6/2020 10:52:45.582 RelationalEventId.TransactionCommitting[20210] (Microsoft.EntityFrameworkCore.Database.Transaction)
      Committing transaction.
dbug: 10/6/2020 10:52:45.585 RelationalEventId.TransactionCommitted[20202] (Microsoft.EntityFrameworkCore.Database.Transaction)
      Committed transaction.

Konten ini dapat dikustomisasi dengan meneruskan nilai dari DbContextLoggerOptions, seperti yang ditunjukkan di bagian berikut.

Tip

Pertimbangkan untuk menggunakan Microsoft.Extensions.Logging untuk kontrol lebih besar atas pemformatan log.

Menggunakan waktu UTC

Secara default, tanda waktu dirancang untuk konsumsi lokal saat penelusuran kesalahan. Gunakan DbContextLoggerOptions.DefaultWithUtcTime untuk menggunakan tanda waktu UTC budaya-agnostik sebagai gantinya, tetapi menjaga segala sesuatu yang lain tetap sama. Contohnya:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(
        Console.WriteLine,
        LogLevel.Debug,
        DbContextLoggerOptions.DefaultWithUtcTime);

Contoh ini menghasilkan pemformatan log berikut:

info: 2020-10-06T17:55:39.0333701Z RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
      Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      CREATE TABLE "Blogs" (
          "Id" INTEGER NOT NULL CONSTRAINT "PK_Blogs" PRIMARY KEY AUTOINCREMENT,
          "Name" INTEGER NOT NULL
      );
dbug: 2020-10-06T17:55:39.0333892Z RelationalEventId.TransactionCommitting[20210] (Microsoft.EntityFrameworkCore.Database.Transaction)
      Committing transaction.
dbug: 2020-10-06T17:55:39.0351684Z RelationalEventId.TransactionCommitted[20202] (Microsoft.EntityFrameworkCore.Database.Transaction)
      Committed transaction.

Pengelogan baris tunggal

Terkadang berguna untuk mendapatkan tepat satu baris per pesan log. Ini dapat diaktifkan oleh DbContextLoggerOptions.SingleLine. Contohnya:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(
        Console.WriteLine,
        LogLevel.Debug,
        DbContextLoggerOptions.DefaultWithLocalTime | DbContextLoggerOptions.SingleLine);

Contoh ini menghasilkan pemformatan log berikut:

info: 10/6/2020 10:52:45.723 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) -> Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']CREATE TABLE "Blogs" (    "Id" INTEGER NOT NULL CONSTRAINT "PK_Blogs" PRIMARY KEY AUTOINCREMENT,    "Name" INTEGER NOT NULL);
dbug: 10/6/2020 10:52:45.723 RelationalEventId.TransactionCommitting[20210] (Microsoft.EntityFrameworkCore.Database.Transaction) -> Committing transaction.
dbug: 10/6/2020 10:52:45.725 RelationalEventId.TransactionCommitted[20202] (Microsoft.EntityFrameworkCore.Database.Transaction) -> Committed transaction.

Opsi konten lainnya

Bendera lain di DbContextLoggerOptions dapat digunakan untuk memangkas jumlah metadata yang disertakan dalam log. Ini dapat berguna bersama dengan pengelogan baris tunggal. Contohnya:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(
        Console.WriteLine,
        LogLevel.Debug,
        DbContextLoggerOptions.UtcTime | DbContextLoggerOptions.SingleLine);

Contoh ini menghasilkan pemformatan log berikut:

2020-10-06T17:52:45.7320362Z -> Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']CREATE TABLE "Blogs" (    "Id" INTEGER NOT NULL CONSTRAINT "PK_Blogs" PRIMARY KEY AUTOINCREMENT,    "Name" INTEGER NOT NULL);
2020-10-06T17:52:45.7320531Z -> Committing transaction.
2020-10-06T17:52:45.7339441Z -> Committed transaction.

Berpindah dari EF6

Pengelogan sederhana EF Core berbeda dari Database.Log di EF6 dengan dua cara penting:

  • Pesan log tidak terbatas hanya pada interaksi database
  • Pengelogan harus dikonfigurasi pada waktu inisialisasi konteks

Untuk perbedaan pertama, pemfilteran yang dijelaskan di atas dapat digunakan untuk membatasi pesan mana yang dicatat.

Perbedaan kedua adalah perubahan yang disengaja untuk meningkatkan performa dengan tidak menghasilkan pesan log saat tidak diperlukan. Namun, masih mungkin untuk mendapatkan perilaku yang sama dengan EF6 dengan membuat Log properti pada Anda DbContext dan kemudian menggunakannya hanya ketika telah ditetapkan. Contohnya:

public Action<string> Log { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.LogTo(s => Log?.Invoke(s));