Czy wstrzykujecie ILogger<> ?

0

Czy wstrzykujecie ILogger<SomeType> ?

Nie łatwiej jest po prostu napisać w klasie:

static Logger _logger = LogManager.GetLogger("someLogger"); //NLog

Czemu logger należy traktować jako zależność, którą należy przekazać przez konstruktor / właściwość?
Czemu wstrzykiwać coś tak długiego, zaciemniającego konstruktor?

2

Hm, to ja może częściowo nie na temat. W Scali standardem są standardem traity LazyLogging/StrictLogging. Rozszerza się je i dostaje się z automatu pole logger i potem już można pisać:

logger.debug("This is Logging ;-)")

IHMO najlepsze podejście. Jak pracowałem w Javie to widziałem trzy różne podejścia (kolejność ze względu na częstotliwość występowania)

  1. statyczne pole z logerem. Rozwiązane najprostsze, ale podobno może prowadzić do wycieków pamięci (przynajmniej na starym TomCatcie)
  2. niestatyczne pole z logerem od razu inicjalizowane (możliwe że teraz już najczęstrze rozwiązanie)
  3. wstrzykiwanie loggera z zewnątrz, ale zawsze to były jakieś dziwne patologiczne sytuacje gdzie ktoś coś totalnie pokręcił i w rezyltacie w klasie A był loger który mówił że loguje z klasy B (W Javie klasę dla której ma logować logger podaje się jako parametr w metodzie fabrykującej i zawsze wtedy podawano złą klasę)
0

Chyba odpowiem sobie na pytanie:
Jeżeli korzystamy z biblioteki innego zespołu / nugeta, to mamy możliwość zalogowania tego co nam oferuje dana biblioteka, np. provider Entity Framework, który loguje SQL.
Można takie coś realizować przez delegaty czy implementacje dedykowanych interfejsów, no ale teraz dostaliśmy abstrakcję, którą można wykorzystać, aby potem można było dołożyć konfigurację.

Nazywanie tego jak w moim przykładzie: GetLogger("someLogger"), niosłoby ze sobą problemy związane z możliwością duplikowania nazw dlatego mamy typowanie.

W związku z tym, żeby każdy umiał na wszelki wypadek z tego skorzystać, podaje się to wstrzykiwanie jako przykład w większości tutorialli.

Szkoda, że domyślnie nie ma czegoś takiego:

static ILogger log = LoggerFactory.Create<SomeType>();

Wydaje mi się, że wstrzykiwanie ILogger w wielu przypadkach może być zbędne na rzecz powyższej linii kogu

2

Wstrzykiwanie pozwala łatwo przetestować logowanie, można łatwo w jednym miejscu zmienić bibliotekę do logowania. Koniec zalet.
Przy czym wystarczy opakować aktualną bibliotekę logującą w swoją klasę zwracającą ILogger i już nie ma tej drugiej zalety. Szkoda tylko że nikt tak nie robił i pewnie byś jeszcze w przeszłości za to dostał zjebkę na code review.

static ILogger log = LoggerFactory.Create<SomeType>();

Takie coś bardzo łatwo samemu napisać w paru linijkach kodu, wystarczy zapamiętać ILoggerFactory z DI gdzieś statycznie zaraz po stworzeniu kontenera.
Wstrzykiwanie ILogger się robi problematyczne jeśli piszemy biblioteki. Osobiście sam właśnie zrobiłem takie statyczne LoggerFactory bo inaczej w całej bibliotece trzeba w konstruktorach przekazywać ILoggerFactory i jakiekolwiek logowanie z data obiektów czy metod statycznych staje się bez tego niemożliwe (już nie rozważając czy ma to sens, ale logowanie powinno być możliwe w każdym miejscu).

Moim zdaniem stworzenie takiego prostego statycznego LoggerFactory to najlepsze wyjście - ma wszystkie zalety - możliwość łatwego testowania, możliwość łatwej podmiany biblioteki, łatwa konfigurowalność, brak zaśmiecania konstruktora. Jedyny minus to ukryta zależność - podmiana loggera w testach staje się nieoczywista i trzeba się wgryzać w "magię".

Odpowiadając na pytanie - tak - wstrzykuję ILogger<Type> gdzie to ma sens, czasem ILoggerFactory a pisząc dll-kę tworzę statyczną klasę i wykorzystuję jak w Twoim przykładzie.

0

Jak chcesz tak zgrabnie, to czemu w ogóle nie pójść krok dalej? static logger

public void DoSomething()
{
  Logger.Information("test");
  Logger.Warning("test");
  Logger.Error("test");
}
public static class Logger
{
  public void Information(string s) => _logger.Information(s);
  public void Warning(string s) => _logger.Warning(s);
  public void Error(string s) => _logger.Error(s);
}
0
1a2b3c4d5e napisał(a):

Jak chcesz tak zgrabnie, to czemu w ogóle nie pójść krok dalej? static logger

To chyba zadziała dobrze tylko w aplikacjach jednowątkowych? Czy logery są bezpieczne ze względu na wątki?

UPDATE: Racja. W zasadzie jak robi się logera statycznego per klasa to może być statyczny per aplikacja. Dla wątków to samo wyjdzie

6
gosc_z_pytaniem napisał(a):

Czy wstrzykujecie ILogger<SomeType> ?

W bibliotekach tak, w API nie.

Czemu logger należy traktować jako zależność, którą należy przekazać przez konstruktor / właściwość?

Nie wiem, czy należy, po prostu można to tak rozwiązać jeśli chce się osiągnąć założone cele. Ale przez konstruktor, na pewno nie przez właściwość.

Czemu wstrzykiwać coś tak długiego, zaciemniającego konstruktor?

Aby użytkownik biblioteki mógł użyć tego samego narzędzia do logowania, którego używa w swojej aplikacji, a nie tego, które wymusza na nim twórca biblioteki. Utrzymywanie konfiguracji wielu różnych loggerów w aplikacji to zbędny wysiłek.

0
somekind napisał(a):

Aby użytkownik biblioteki mógł użyć tego samego narzędzia do logowania, którego używa w swojej aplikacji, a nie tego, które wymusza na nim twórca biblioteki. Utrzymywanie konfiguracji wielu różnych loggerów w aplikacji to zbędny wysiłek.

Może mały offtop, ale to w C# nie ma odpowiednika slf4j ? Czyli narzędzie że wszystkie dziwne loggery (o ile nie są całkiem skopane mogą być przekierowywane do jednego loggera)? Chociaż wojny loggerów w Javie się już zakończyły i teraz wszyscy używają Logback a jak ktoś nie yżywa to znaczy że ma skopany projekt :P

6
KamilAdam napisał(a):

Może mały offtop, ale to w C# nie ma odpowiednika slf4j ? Czyli narzędzie że wszystkie dziwne loggery (o ile nie są całkiem skopane mogą być przekierowywane do jednego loggera)? Chociaż wojny loggerów w Javie się już zakończyły i teraz wszyscy używają Logback a jak ktoś nie yżywa to znaczy że ma skopany projekt :P

Jest takie narzędzie: LibLog, które dostarcza wspólnej abstrakcji i wsparcia dla konkretnych loggerów za pomocą bazujących na refleksji wrapperów.
Tylko od paru lat nie ma to już sensu, bo jest wbudowana we framework abstrakcja (o której zresztą jest ten wątek). Każdy 3rd party logger dostosował się do tego stanu rzeczy i dostarcza swoją integrację: oto lista
I tak jak napisałem wcześniej - jeśli w swojej libce użyjesz abstrakcyjnego ILogger<>, to będzie się nadawała do łatwego użytku z każdą aplikacją. Jeśli przywiążesz libkę do konkretnego loggera, to prawdopodobnie spowodujesz srogi WTF u potencjalnych użytkowników.

1 użytkowników online, w tym zalogowanych: 0, gości: 1