Saya ingin menggunakan nlogger di aplikasi saya, mungkin di masa mendatang saya perlu mengubah sistem logging. Jadi saya ingin menggunakan fasad kayu.
Apakah Anda tahu ada rekomendasi untuk contoh yang ada bagaimana menulis yang itu? Atau beri saya tautan ke beberapa praktik terbaik di bidang ini.
Jawaban:
Saya dulu menggunakan fasad logging seperti Common.Logging (bahkan untuk menyembunyikan library CuttingEdge.Logging saya sendiri ), tetapi saat ini saya menggunakan pola Dependency Injection dan ini memungkinkan saya untuk menyembunyikan logger di balik abstraksi saya sendiri (sederhana) yang menganut kedua Dependency Prinsip Inversi dan Prinsip Segregasi Antarmuka(ISP) karena memiliki satu anggota dan karena antarmuka ditentukan oleh aplikasi saya; bukan perpustakaan eksternal. Meminimalkan pengetahuan yang dimiliki bagian inti aplikasi Anda tentang keberadaan perpustakaan eksternal, semakin baik; bahkan jika Anda tidak berniat untuk mengganti perpustakaan logging Anda. Ketergantungan keras pada pustaka eksternal mempersulit pengujian kode Anda, dan memperumit aplikasi Anda dengan API yang tidak pernah dirancang secara khusus untuk aplikasi Anda.
Ini adalah abstraksi yang sering terlihat dalam aplikasi saya:
public interface ILogger { void Log(LogEntry entry); } public enum LoggingEventType { Debug, Information, Warning, Error, Fatal }; // Immutable DTO that contains the log information. public class LogEntry { public readonly LoggingEventType Severity; public readonly string Message; public readonly Exception Exception; public LogEntry(LoggingEventType severity, string message, Exception exception = null) { if (message == null) throw new ArgumentNullException("message"); if (message == string.Empty) throw new ArgumentException("empty", "message"); this.Severity = severity; this.Message = message; this.Exception = exception; } }
Secara opsional, abstraksi ini dapat diperluas dengan beberapa metode ekstensi sederhana (memungkinkan antarmuka tetap sempit dan tetap mengikuti ISP). Ini membuat kode untuk konsumen antarmuka ini jauh lebih sederhana:
public static class LoggerExtensions { public static void Log(this ILogger logger, string message) { logger.Log(new LogEntry(LoggingEventType.Information, message)); } public static void Log(this ILogger logger, Exception exception) { logger.Log(new LogEntry(LoggingEventType.Error, exception.Message, exception)); } // More methods here. }
Karena antarmuka hanya berisi satu metode, Anda dapat dengan mudah membuat
ILogger
implementasi yang merupakan proxy ke log4net , untuk Serilog , Microsoft.Extensions.Logging , NLog atau perpustakaan logging lain dan mengkonfigurasi kontainer DI Anda untuk menyuntikkan itu di kelas yang memilikiILogger
di mereka konstruktor.Perhatikan bahwa memiliki metode ekstensi statis di atas antarmuka dengan metode tunggal sangat berbeda dari memiliki antarmuka dengan banyak anggota. Metode ekstensi hanyalah metode pembantu yang membuat
LogEntry
pesan dan meneruskannya melalui satu-satunya metode padaILogger
antarmuka. Metode ekstensi menjadi bagian dari kode konsumen; bukan bagian dari abstraksi. Hal ini tidak hanya memungkinkan metode ekstensi berkembang tanpa perlu mengubah abstraksi, metode ekstensi, dan ekstensiLogEntry
konstruktor selalu dieksekusi ketika abstraksi pencatat digunakan, bahkan ketika pencatat tersebut dihentikan / diolok-olok. Ini memberikan lebih banyak kepastian tentang kebenaran panggilan ke logger saat menjalankan rangkaian pengujian. Antarmuka satu anggota membuat pengujian lebih mudah juga; Memiliki abstraksi dengan banyak anggota mempersulit pembuatan implementasi (seperti tiruan, adaptor, dan dekorator).Saat Anda melakukan ini, hampir tidak pernah ada kebutuhan untuk beberapa abstraksi statis yang mungkin ditawarkan oleh fasad logging (atau pustaka lainnya).
sumber
Saya menggunakan pembungkus antarmuka kecil + adaptor dari https://github.com/uhaciogullari/NLog. Antarmuka yang juga tersedia melalui NuGet :
sumber
Saat ini, taruhan terbaik adalah menggunakan paket Microsoft.Extensions.Logging ( seperti yang ditunjukkan oleh Julian ). Sebagian besar kerangka kerja logging dapat digunakan dengan ini.
Mendefinisikan antarmuka Anda sendiri, seperti yang dijelaskan dalam jawaban Steven tidak masalah untuk kasus-kasus sederhana, tetapi beberapa hal yang saya anggap penting terlewatkan:
IsEnabled(LogLevel)
yang mungkin Anda inginkan, untuk alasan pertunjukan sekali lagiAnda mungkin dapat menerapkan semua ini dalam abstraksi Anda sendiri, tetapi pada saat itu Anda akan menemukan kembali roda.
sumber
Umumnya saya lebih suka membuat antarmuka seperti
public interface ILogger { void LogInformation(string msg); void LogError(string error); }
dan dalam runtime saya menyuntikkan kelas beton yang diimplementasikan dari antarmuka ini.
sumber
LogWarning
danLogCritical
metode dan semua overloads mereka. Saat melakukan ini, Anda akan melanggar Prinsip Segregasi Antarmuka . Lebih suka menentukanILogger
antarmuka dengan satuLog
metode.LogEntry
, dan dengan demikian ketergantungan padaLoggingEventType
. TheILogger
pelaksanaan harus berurusan dengan iniLoggingEventTypes
, mungkin meskipuncase/switch
, yang merupakan kode bau . Mengapa menyembunyikanLoggingEventTypes
ketergantungan? Pelaksanaannya harus menangani tingkat penebangan pula , sehingga akan lebih baik untuk eksplisit tentang apa yang harus melakukan implementasi, bukan menyembunyikannya di balik metode tunggal dengan argumen umum.ICommand
yang memiliki aHandle
yang mengambilobject
. Implementasi haruscase/switch
melebihi jenis yang mungkin untuk memenuhi kontrak antarmuka. Ini tidak ideal. Tidak memiliki abstraksi yang menyembunyikan ketergantungan yang tetap harus ditangani. Alih-alih memiliki antarmuka yang menyatakan dengan jelas apa yang diharapkan: "Saya mengharapkan semua penebang untuk menangani Peringatan, Kesalahan, Fatals, dll". Ini lebih disukai daripada "Saya mengharapkan semua logger menangani pesan yang mencakup Peringatan, Kesalahan, Fatals, dll."LoggingEventType
harus dipanggilLoggingEventLevel
karena tipe adalah kelas dan harus diberi kode seperti itu dalam OOP. Bagi saya tidak ada perbedaan antara tidak menggunakan metode antarmuka vs tidak menggunakan nilai yang sesuaienum
. Sebagai gantinya gunakanErrorLoggger : ILogger
, diInformationLogger : ILogger
mana setiap logger menentukan levelnya sendiri. Kemudian DI perlu memasukkan logger yang diperlukan, mungkin melalui kunci (enum), tetapi kunci ini tidak lagi menjadi bagian dari antarmuka. (Anda sekarang SOLID).Solusi hebat untuk masalah ini telah muncul dalam bentuk proyek LibLog .
LibLog adalah abstraksi logging dengan dukungan built-in untuk logger besar termasuk Serilog, NLog, Log4net dan logger Enterprise. Ini diinstal melalui manajer paket NuGet ke perpustakaan target sebagai file sumber (.cs), bukan referensi .dll. Pendekatan tersebut memungkinkan abstraksi logging disertakan tanpa memaksa perpustakaan untuk mengambil dependensi eksternal. Hal ini juga memungkinkan penulis perpustakaan untuk menyertakan logging tanpa memaksa aplikasi yang memakan untuk secara eksplisit menyediakan logger ke perpustakaan. LibLog menggunakan refleksi untuk mencari tahu logger beton apa yang digunakan dan menghubungkannya tanpa kode pengkabelan eksplisit dalam proyek perpustakaan.
Jadi, LibLog adalah solusi yang bagus untuk masuk dalam proyek perpustakaan. Cukup rujuk dan konfigurasikan logger beton (Serilog untuk kemenangan) di aplikasi atau layanan utama Anda dan tambahkan LibLog ke perpustakaan Anda!
sumber
Alih-alih menulis fasad Anda sendiri, Anda bisa menggunakan Castle Logging Services atau Simple Logging Façade .
Keduanya menyertakan adaptor untuk NLog dan Log4net.
sumber
Sejak 2015 Anda juga dapat menggunakan .NET Core Logging jika Anda membuat aplikasi inti .NET.
Paket untuk NLog untuk menghubungkan adalah:
sumber