Ini mungkin agak terkait dengan Pass ILogger atau ILoggerFactory ke konstruktor di AspNet Core? , namun ini secara khusus tentang Desain Perpustakaan , bukan tentang bagaimana aplikasi sebenarnya yang menggunakan perpustakaan tersebut mengimplementasikan pencatatannya.
Saya sedang menulis Pustaka .net Standard 2.0 yang akan diinstal melalui Nuget, dan untuk memungkinkan orang yang menggunakan Pustaka itu mendapatkan beberapa info debug, saya bergantung pada Microsoft.Extensions.Logging.Abstractions untuk memungkinkan Logger standar dimasukkan.
Namun, saya melihat banyak antarmuka, dan kode contoh di web terkadang menggunakan ILoggerFactory
dan membuat logger di ctor kelas. Ada juga ILoggerProvider
yang terlihat seperti versi hanya-baca dari Pabrik, tetapi implementasi mungkin atau mungkin tidak mengimplementasikan kedua antarmuka, jadi saya harus memilih. (Pabrik tampaknya lebih umum daripada Penyedia).
Beberapa kode yang saya lihat menggunakan ILogger
antarmuka non-generik dan bahkan mungkin berbagi satu contoh dari logger yang sama, dan beberapa mengambil ILogger<T>
di ctor mereka dan mengharapkan wadah DI untuk mendukung jenis generik terbuka atau pendaftaran eksplisit dari setiap ILogger<T>
variasi perpustakaan saya penggunaan.
Saat ini, saya pikir itu ILogger<T>
adalah pendekatan yang tepat, dan mungkin ctor yang tidak mengambil argumen itu dan hanya melewati Null Logger sebagai gantinya. Dengan begitu, jika tidak diperlukan logging, tidak ada yang digunakan. Namun, beberapa kontainer DI memilih ctor terbesar dan dengan demikian akan tetap gagal.
Saya ingin tahu tentang apa yang seharusnya saya lakukan di sini untuk mengurangi sakit kepala bagi pengguna, sambil tetap mengizinkan dukungan logging yang tepat jika diinginkan.
Semuanya valid kecuali
ILoggerProvider
.ILogger
danILogger<T>
apa yang seharusnya Anda gunakan untuk logging. Untuk mendapatkanILogger
, Anda menggunakanILoggerFactory
.ILogger<T>
adalah jalan pintas untuk mendapatkan logger untuk kategori tertentu (jalan pintas untuk tipe sebagai kategori).Saat Anda menggunakan
ILogger
untuk melakukan logging, setiap terdaftarILoggerProvider
mendapat kesempatan untuk menangani pesan log itu. Ini tidak benar-benar valid untuk menggunakan kode untuk dipanggilILoggerProvider
secara langsung.sumber
ILoggerFactory
akan menjadi cara yang bagus untuk menghubungkan DI bagi konsumen dengan hanya memiliki 1 ketergantungan ( "Beri saya Pabrik dan saya akan membuat logger saya sendiri" ), tetapi akan mencegah penggunaan logger yang ada ( kecuali konsumen menggunakan beberapa pembungkus). MengambilILogger
- generik atau tidak - memungkinkan konsumen untuk memberi saya logger yang mereka persiapkan secara khusus, tetapi membuat pengaturan DI berpotensi jauh lebih kompleks (kecuali wadah DI yang mendukung Open Generics digunakan). Apakah itu benar? Kalau begitu, saya pikir saya akan pergi dengan pabrik.ILogger<T>
, saya akan mengambilILogger<MyClass>
atau bahkan hanyaILogger
- dengan cara itu, pengguna dapat menghubungkannya hanya dengan satu pendaftaran, dan tanpa memerlukan generik terbuka dalam wadah DI mereka. Bersandar pada non-generikILogger
, tetapi akan banyak bereksperimen selama akhir pekan.Itu
ILogger<T>
yang sebenarnya dibuat untuk DI. ILogger datang untuk membantu menerapkan pola pabrik dengan jauh lebih mudah, daripada Anda menulis sendiri semua logika DI dan Pabrik, itu adalah salah satu keputusan paling cerdas dalam inti asp.net.Anda dapat memilih antara:
ILogger<T>
jika Anda memiliki kebutuhan untuk menggunakan pola pabrik dan DI dalam kode Anda atau Anda dapat menggunakanILogger
, untuk mengimplementasikan logging sederhana tanpa DI diperlukan.mengingat, The
ILoggerProvider
hanyalah jembatan untuk menangani setiap pesan log terdaftar. Tidak perlu menggunakannya, karena tidak mempengaruhi apa pun yang harus Anda campur tangan dalam kode, Ia mendengarkan ILoggerProvider terdaftar dan menangani pesan. Itu saja.sumber
this ILogger
.Berpegang pada pertanyaan, saya yakin
ILogger<T>
ini adalah opsi yang tepat, dengan mempertimbangkan sisi negatif dari opsi lain:ILoggerFactory
memaksa pengguna Anda untuk memberikan kontrol pabrik logger global yang dapat berubah ke perpustakaan kelas Anda. Selain itu, dengan menerimaILoggerFactory
kelas Anda sekarang dapat menulis ke log dengan sembarang nama kategori denganCreateLogger
metode. MeskipunILoggerFactory
biasanya tersedia sebagai singleton dalam wadah DI, saya sebagai pengguna akan meragukan mengapa perpustakaan mana pun perlu menggunakannya.ILoggerProvider.CreateLogger
terlihat seperti itu, itu tidak dimaksudkan untuk injeksi. Ini digunakan denganILoggerFactory.AddProvider
sehingga pabrik dapat membuat agregatILogger
yang menulis ke beberapaILogger
dibuat dari setiap penyedia terdaftar. Ini jelas saat Anda memeriksa penerapanLoggerFactory.CreateLogger
ILogger
juga tampak seperti cara yang harus dilakukan, tetapi tidak mungkin dengan .NET Core DI. Ini sebenarnya terdengar seperti alasan mengapa mereka perlu menyediakanILogger<T>
di tempat pertama.Jadi bagaimanapun, kita tidak memiliki pilihan yang lebih baik daripada
ILogger<T>
, jika kita memilih dari kelas-kelas itu.Pendekatan lain adalah dengan memasukkan sesuatu yang lain yang membungkus non-generik
ILogger
, yang dalam hal ini harus non-generik. Idenya adalah dengan membungkusnya dengan kelas Anda sendiri, Anda mengambil kendali penuh tentang bagaimana pengguna dapat mengkonfigurasinya.sumber
Pendekatan default dimaksudkan untuk menjadi
ILogger<T>
. Ini berarti bahwa dalam log log dari kelas tertentu akan terlihat jelas karena mereka akan menyertakan nama kelas lengkap sebagai konteksnya. Misalnya, jika nama lengkap kelasMyLibrary.MyClass
Anda adalah, Anda akan mendapatkan ini di entri log yang dibuat oleh kelas ini. Sebagai contoh:Anda harus menggunakan
ILoggerFactory
jika Anda ingin menentukan konteks Anda sendiri. Misalnya, semua log dari perpustakaan Anda memiliki konteks log yang sama, bukan setiap kelas. Sebagai contoh:Dan kemudian log akan terlihat seperti ini:
Jika Anda melakukan itu di semua kelas maka konteksnya hanya MyLibrary untuk semua kelas. Saya membayangkan Anda ingin melakukannya untuk perpustakaan jika Anda tidak ingin mengekspos struktur kelas dalam di log.
Mengenai penebangan opsional. Saya pikir Anda harus selalu memerlukan ILogger atau ILoggerFactory di konstruktor dan menyerahkannya kepada konsumen perpustakaan untuk mematikannya atau menyediakan Logger yang tidak melakukan apa pun dalam injeksi ketergantungan jika mereka tidak ingin melakukan logging. Sangat mudah untuk mengubah logging untuk konteks tertentu dalam konfigurasi. Sebagai contoh:
sumber
Untuk desain perpustakaan, pendekatan yang baik adalah:
1. Jangan memaksa konsumen untuk memasukkan logger ke kelas Anda. Cukup buat ctor lain yang meneruskan NullLoggerFactory.
2. Batasi jumlah kategori yang Anda gunakan saat Anda membuat logger untuk memungkinkan konsumen mengkonfigurasi pemfilteran log dengan mudah.
this._loggerFactory.CreateLogger(Consts.CategoryName)
sumber