Kitaplık için ILogger, ILogger <T>, ILoggerFactory veya ILoggerProvider almalı mıyım?


113

Bu, AspNet Core'daki kuruculara ILogger veya ILoggerFactory ile ilgili olabilir mi? ancak bu, özellikle Kitaplık Tasarımı ile ilgilidir , bu kitaplıkları kullanan gerçek uygulamanın günlük kaydını nasıl gerçekleştirdiği ile ilgili değildir.

Nuget aracılığıyla yüklenecek bir .net Standart 2.0 Kitaplığı yazıyorum ve bu Kitaplığı kullanan kişilerin bazı hata ayıklama bilgilerini almasına izin vermek için, standartlaştırılmış bir Kaydedicinin enjekte edilmesine izin vermek için Microsoft.Extensions.Logging.Abstractions'a güveniyorum .

Ancak, birden çok arabirim görüyorum ve web üzerinde örnek kod bazen ILoggerFactorysınıfın ctor'unda bir günlükçü kullanıyor ve oluşturuyor. ILoggerProviderFabrikanın salt okunur bir sürümü gibi görünen bir de var , ancak uygulamalar her iki arayüzü de uygulayabilir veya uygulamayabilir, bu yüzden seçmem gerekir. (Fabrika, Sağlayıcıdan daha yaygın görünüyor).

Gördüğüm bazı kodlar, jenerik olmayan ILoggerarayüzü kullanıyor ve hatta aynı logger'ın bir örneğini paylaşıyor olabilir ve bazıları ILogger<T>kendi ctor'unu alıyor ve DI kapsayıcısının açık genel türleri veya ILogger<T>kütüphanemdeki her varyasyonun açık kaydını desteklemesini bekliyor. kullanımları.

Şu anda, bunun ILogger<T>doğru yaklaşım olduğunu düşünüyorum ve belki de bu argümanı almayan ve onun yerine bir Null Logger geçiren bir ctor. Bu şekilde, günlük kaydı gerekmiyorsa, hiçbiri kullanılmaz. Bununla birlikte, bazı DI kapları en büyük ctoru seçer ve bu nedenle yine de başarısız olur.

Kullanıcılar için en az miktarda baş ağrısını yaratmak için burada ne yapmam gerektiğini merak ediyorum ve istenirse uygun günlük kaydı desteğine izin vermeye devam ediyor.

Yanıtlar:


113

Tanım

Biz 3 arayüzleri vardır: ILogger, ILoggerProviderve ILoggerFactory. Sorumluluklarını öğrenmek için kaynak koduna bakalım :

ILogger : belirli bir Günlük Seviyesinin günlük mesajını yazmaktan sorumludur .

ILoggerProvider : bir örnek oluşturmaktan sorumludur ILogger( ILoggerProviderdoğrudan bir günlük kaydedici oluşturmak için kullanmamanız gerekir)

ILoggerFactory : Bir veya daha fazla ILoggerProviders'yi fabrikaya kaydedebilirsiniz , bu da bunların bir örneğini oluşturmak için hepsini kullanır ILogger. ILoggerFactorykoleksiyonuna sahiptir ILoggerProviders.

Aşağıdaki örnekte, fabrikaya 2 sağlayıcı (konsol ve dosya) kaydettiriyoruz. Bir kaydedici oluşturduğumuzda, fabrika bir kaydedici örneği oluşturmak için bu sağlayıcıların her ikisini de kullanır:

ILoggerFactory factory = new LoggerFactory().AddConsole();    // add console provider
factory.AddProvider(new LoggerFileProvider("c:\\log.txt"));   // add file provider
Logger logger = factory.CreateLogger(); // <-- creates a console logger and a file logger

Böylece kaydedicinin kendisi bir ILoggers koleksiyonunu sürdürür ve günlük mesajını hepsine yazar. Logger kaynak koduna baktığımızda Loggerbir dizi ILoggers(ie LoggerInformation[]) olduğunu ve aynı zamanda ILoggerarabirimi uyguladığını doğrulayabiliriz .


Bağımlılık Enjeksiyonu

MS belgeleri , bir kaydediciyi enjekte etmek için 2 yöntem sağlar:

1. Fabrikayı enjekte etmek:

public TodoController(ITodoRepository todoRepository, ILoggerFactory logger)
{
    _todoRepository = todoRepository;
    _logger = logger.CreateLogger("TodoApi.Controllers.TodoController");
}

Category = TodoApi.Controllers.TodoController ile bir Logger oluşturur .

2. Jenerik ilaç enjekte etmek ILogger<T>:

public TodoController(ITodoRepository todoRepository, ILogger<TodoController> logger)
{
    _todoRepository = todoRepository;
    _logger = logger;
}

Kategori = tam nitelikli TodoController tip adıyla bir günlükçü oluşturur


Kanımca, dokümantasyonu kafa karıştırıcı yapan şey, jenerik olmayanın enjekte edilmesine dair hiçbir şeyden bahsetmemesidir ILogger. Yukarıdaki aynı örnekte, jenerik olmayan birITodoRepository ve yine de neden aynı şeyi yapmadığımızı açıklamıyor ILogger.

Mark Seemann'a göre :

Bir Enjeksiyon Yapıcı, bağımlılıkları almaktan fazlasını yapmamalıdır.

Kontrolöre fabrika enjekte etmek iyi bir yaklaşım değildir, çünkü Kontrolörün Kaydediciyi başlatmak sorumluluğu değildir (SRP'nin ihlali). Aynı zamanda jenerik enjekte etmek ILogger<T>gereksiz gürültüye neden olur. Daha fazla ayrıntı için Simple Injector bloguna bakın: ASP.NET Core DI soyutlamasında sorun nedir?

Enjekte edilmesi gereken şey (en azından yukarıdaki makaleye göre) jenerik değildir ILogger, ancak bu, Microsoft'un Yerleşik DI Container'ın yapabileceği bir şey değildir ve 3. taraf bir DI Kitaplığı kullanmanız gerekir. Bu ikisi belge, 3. taraf kitaplıklarını .NET Core ile nasıl kullanabileceğinizi açıklar.


Bu, Nikola Malovic'in 5 IoC yasasını açıkladığı başka bir makaledir .

Nikola'nın 4. IoC yasası

Çözümlenen bir sınıfın her kurucusu, kendi bağımlılıkları kümesini kabul etmekten başka herhangi bir uygulamaya sahip olmamalıdır.


3
Cevabınız ve Steven'ın makalesi aynı zamanda hem en doğru hem de en iç karartıcı.
bokibeg

30

Bunların dışında hepsi geçerlidir ILoggerProvider. ILoggerve ILogger<T>günlük kaydı için kullanmanız gereken şeylerdir. Bir almak için ILoggerbir ILoggerFactory. ILogger<T>belirli bir kategori için bir günlük kaydedici almak için bir kısayoldur (kategori olarak tür için kısayol).

ILoggerGünlük kaydını gerçekleştirmek için kullandığınızda, kayıtlı her bir kayıt ILoggerProvidermesajını işleme şansı elde eder. ILoggerProviderDoğrudan çağırmak için kod tüketmek gerçekten geçerli değil .


Teşekkürler. Bu bana ILoggerFactory, yalnızca 1 bağımlılığa sahip olarak ( "Bana bir Fabrika verin ve kendi kaydedicilerimi oluşturacağım" ) tüketiciler için DI'yi bağlamanın kolay bir yolu olarak harika olacağını, ancak mevcut bir kaydediciyi kullanmayı engelleyeceğini düşündürüyor ( tüketici biraz sarıcı kullanmadıkça). Bir ILogger- jenerik olsun ya da olmasın - almak, tüketicinin bana özel olarak hazırladıkları bir kaydediciyi vermesine izin verir, ancak DI kurulumunu potansiyel olarak çok daha karmaşık hale getirir (Open Generics'i destekleyen bir DI kapsayıcı kullanılmadıkça). Bu kulağa doğru geliyor mu? Bu durumda fabrika ile gideceğimi düşünüyorum.
Michael Stum

3
@MichaelStum - Burada mantığınızı takip ettiğimden emin değilim. Tüketicilerinizin bir DI sistemi kullanmasını bekliyorsunuz, ancak daha sonra kitaplığınızdaki bağımlılıkları devralmak ve manuel olarak yönetmek mi istiyorsunuz? Neden bu doğru yaklaşım gibi görünüyor?
Damien_The_Unbeliever

@Damien_The_Unbeliever Bu iyi bir nokta. Bir fabrika almak tuhaf görünüyor. Sanırım, bir almak yerine, ILogger<T>bir ILogger<MyClass>veya hatta ILoggerbunun yerine bir tane alacağım - bu şekilde, kullanıcı sadece tek bir kayıtla ve DI konteynerlerinde açık jeneriklere ihtiyaç duymadan onu bağlayabilir. Jenerik olmayanlara yönelmek ILogger, ancak hafta sonu boyunca çok fazla deneyecek.
Michael Stum

10

ILogger<T>DI yapılır fiili biriydi. ILogger, asp.net çekirdeğindeki en akıllı kararlardan biri olan tüm DI ve Fabrika mantığını kendi başınıza yazmak yerine fabrika modelini çok daha kolay uygulamaya yardımcı olmak için geldi.

Şunlardan birini seçebilirsiniz:

ILogger<T>kodunuzda fabrikası ve DI desenleri kullanmak için bir ihtiyaç varsa ya sen kullanabilirsinizILogger DI gerekmeden basit günlük kaydı uygulamak için .

Bu ILoggerProvider, kayıtlı günlük mesajlarının her birini işlemek için yalnızca bir köprüdür. Kodda müdahale etmeniz gereken hiçbir şeyi etkilemediği için kullanmaya gerek yoktur, kayıtlı ILoggerProvider'ı dinler ve mesajları işler. Bu onunla ilgili.


1
ILogger ve ILogger <T> 'nin DI ile ne ilgisi var? Ya enjekte edilir, değil mi?
Matthew Hostetler

Aslında MS Docs içindeki ILogger <TCategoryName>. ILogger'dan türetilir ve günlüğe kaydedilecek sınıf adı dışında hiçbir yeni işlev eklemez. Bu aynı zamanda benzersiz bir tür sağlar, böylece DI doğru adlandırılmış günlükçüyü enjekte eder. Microsoft uzantıları genel olmayanları genişletir this ILogger.
Samuel Danielson

8

Soruya ILogger<T>bağlı kalarak, diğer seçeneklerin dezavantajlarını göz önünde bulundurarak doğru seçenek olduğuna inanıyorum :

  1. Enjekte etme ILoggerFactory, kullanıcınızı, değiştirilebilir küresel kaydedici fabrikasının kontrolünü sınıf kitaplığınıza vermeye zorlar. Üstelik ILoggerFactorysınıfınızı kabul ederek artık CreateLoggeryöntem ile herhangi bir rastgele kategori adı ile log yazabilirsiniz . ILoggerFactoryDI kapsayıcıda genellikle tekli olarak mevcut olsa da, bir kullanıcı olarak herhangi bir kütüphanenin neden onu kullanması gerektiğinden şüphe duyarım.
  2. Yöntem ILoggerProvider.CreateLoggeröyle görünmekle birlikte enjeksiyon amaçlı değildir. ILoggerFactory.AddProviderFabrikanın, her kayıtlı sağlayıcıdan oluşturulan ILoggerbirden çok kişiye yazan toplu oluşturabilmesi için birlikte kullanılır ILogger. Uygulamasını incelediğinizde bu açıktırLoggerFactory.CreateLogger
  3. Kabul etmek ILoggerde gidilecek yol gibi görünüyor, ancak .NET Core DI ile bu imkansız. Bu aslında ILogger<T>ilk etapta sağlamaya ihtiyaç duymalarının nedeni gibi görünüyor .

Sonuçta ILogger<T>, bu sınıflar arasından seçim yapmaktan daha iyi bir seçeneğimiz yok .

Başka bir yaklaşım, jenerik olmayanı saran başka bir şey enjekte etmektir ILogger, bu durumda jenerik olmayan bir yaklaşım olmalıdır. Buradaki fikir, onu kendi sınıfınızla sarmalayarak, kullanıcının onu nasıl yapılandırabileceği konusunda tam kontrol sahibi olmanızdır.


6

Varsayılan yaklaşım olması amaçlanmıştır ILogger<T>. Bu, günlükte belirli sınıftaki günlüklerin net bir şekilde görülebileceği anlamına gelir çünkü bunlar bağlam olarak tam sınıf adını içereceklerdir. Örneğin, sınıfınızın tam adı ise, MyLibrary.MyClassbunu bu sınıf tarafından oluşturulan günlük girişlerinde alacaksınız. Örneğin:

MyLibrary.MyClass: Bilgi: Bilgi günlüğüm

ILoggerFactoryKendi bağlamınızı belirtmek istiyorsanız , kullanmalısınız . Örneğin, kitaplığınızdaki tüm günlüklerin her sınıf yerine aynı günlük bağlamına sahip olması. Örneğin:

loggerFactory.CreateLogger("MyLibrary");

Ve sonra günlük şöyle görünecek:

MyLibrary: Bilgi: Bilgi günlüğüm

Bunu tüm sınıflarda yaparsanız, bağlam tüm sınıflar için yalnızca MyLibrary olacaktır. Günlüklerde iç sınıf yapısını açığa çıkarmak istemiyorsanız, bunu bir kütüphane için yapmak isteyeceğinizi hayal ediyorum.

İsteğe bağlı günlük kaydı ile ilgili olarak. Yapıcıda her zaman ILogger veya ILoggerFactory'ye ihtiyaç duymanız ve onu kapatmak için kütüphanenin tüketicisine bırakmanız veya günlük kaydı istemiyorlarsa bağımlılık enjeksiyonunda hiçbir şey yapmayan bir Logger sağlamanız gerektiğini düşünüyorum. Yapılandırmada belirli bir bağlam için günlük kaydını döndürmek çok kolaydır. Örneğin:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "MyLibrary": "None"
    }
  }
}

5

Kütüphane tasarımı için iyi bir yaklaşım şöyle olacaktır:

1. Tüketicileri, sınıflarınıza logger enjekte etmeye zorlamayın. NullLoggerFactory'yi geçen başka bir ctor oluşturun.

class MyClass
{
    private readonly ILoggerFactory _loggerFactory;

    public MyClass():this(NullLoggerFactory.Instance)
    {

    }
    public MyClass(ILoggerFactory loggerFactory)
    {
      this._loggerFactory = loggerFactory ?? NullLoggerFactory.Instance;
    }
}

2. Tüketicilerin günlük filtrelemesini kolayca yapılandırmasına olanak tanımak için günlük kaydediciler oluşturduğunuzda kullandığınız kategori sayısını sınırlayın. this._loggerFactory.CreateLogger(Consts.CategoryName)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.