Może to być nieco związane z przekazywaniem ILogger lub ILoggerFactory do konstruktorów w AspNet Core? jednak dotyczy to w szczególności projektowania bibliotek , a nie sposobu, w jaki aplikacja korzystająca z tych bibliotek implementuje swoje rejestrowanie.
Piszę bibliotekę .net Standard 2.0, która zostanie zainstalowana przez Nuget i aby umożliwić osobom używającym tej biblioteki uzyskanie pewnych informacji debugowania, polegam na Microsoft.Extensions.Logging.Abstractions, aby umożliwić wstrzyknięcie standardowego Loggera.
Jednak widzę wiele interfejsów, a przykładowy kod w Internecie czasami używa ILoggerFactory
i tworzy rejestrator w ctor klasy. Jest też ILoggerProvider
wersja Factory tylko do odczytu, ale implementacje mogą, ale nie muszą, implementować oba interfejsy, więc musiałbym wybrać. (Fabryka wydaje się bardziej powszechna niż Dostawca).
Część kodu, który widziałem, używa nieogólnego ILogger
interfejsu i może nawet współużytkować jedno wystąpienie tego samego rejestratora, a niektóre biorą ILogger<T>
swój ctor i oczekują, że kontener DI będzie obsługiwał otwarte typy ogólne lub jawną rejestrację każdej ILogger<T>
odmiany mojej biblioteki używa.
W tej chwili uważam, że ILogger<T>
jest to właściwe podejście i być może ktor, który nie przyjmuje tego argumentu i zamiast tego przekazuje Null Logger. W ten sposób, jeśli nie jest potrzebne żadne logowanie, żadne nie jest używane. Jednak niektóre kontenery DI wybierają największego ctora i tak i tak by się nie udało.
Ciekaw jestem, co ja powinienem robić, żeby stworzyć najmniejszą ilość utrapieniem dla użytkowników, a jednocześnie zapewni odpowiednie wsparcie rejestrowania w razie potrzeby.
Wszystkie są ważne z wyjątkiem
ILoggerProvider
.ILogger
iILogger<T>
są tym, czego powinieneś używać do logowania. Aby uzyskaćILogger
, używaszILoggerFactory
.ILogger<T>
jest skrótem do pobrania loggera dla określonej kategorii (skrót dla typu jako kategorii).Gdy używasz
ILogger
do rejestrowania, każdy zarejestrowanyILoggerProvider
ma szansę obsłużyć ten komunikat dziennika. Nie jest to naprawdę poprawne do używania kodu doILoggerProvider
bezpośredniego wywoływania .źródło
ILoggerFactory
byłby świetny jako łatwy sposób na okablowanie DI dla konsumentów, mając tylko jedną zależność ( "Po prostu daj mi fabrykę, a utworzę własne loggery" ), ale uniemożliwiłby użycie istniejącego rejestratora ( chyba że konsument użyje jakiegoś opakowania). WykorzystanieILogger
- generycznego lub nie - pozwala konsumentowi dać mi specjalnie przygotowany rejestrator, ale sprawia, że konfiguracja DI jest potencjalnie dużo bardziej złożona (chyba że używany jest kontener DI obsługujący Open Generics). Czy to brzmi poprawnie? W takim razie myślę, że pójdę z fabryką.ILogger<T>
, wezmęILogger<MyClass>
lub po prostuILogger
zamiast tego - w ten sposób użytkownik może połączyć to tylko z jedną rejestracją i bez konieczności otwierania typów generycznych w swoim kontenerze DI. Skłaniając się ku nieogólnemuILogger
, ale będę dużo eksperymentować przez weekend.To
ILogger<T>
był rzeczywisty, który jest stworzony dla DI. ILogger przyszedł, aby ułatwić implementację wzorca fabryki, zamiast pisać samodzielnie całą logikę DI i Factory, która była jedną z najmądrzejszych decyzji w asp.net core.Masz do wyboru:
ILogger<T>
jeśli potrzebujesz użyć wzorców fabryki i DI w swoim kodzie lub możesz użyćILogger
, aby zaimplementować proste rejestrowanie bez konieczności stosowania DI.biorąc pod uwagę to, The
ILoggerProvider
jest tylko pomostem do obsługi każdej z zarejestrowanych wiadomości dziennika. Nie ma potrzeby go używać, ponieważ nie powoduje niczego, co powinieneś ingerować w kod, nasłuchuje zarejestrowanego ILoggerProvider i obsługuje komunikaty. O to chodzi.źródło
this ILogger
.Trzymając się pytania, uważam, że
ILogger<T>
jest to właściwa opcja, biorąc pod uwagę wady innych opcji:ILoggerFactory
zmusza użytkownika do przekazania kontroli nad modyfikowalną fabryką globalnych rejestratorów do biblioteki klas. Co więcej, akceptującILoggerFactory
swoją klasę, możesz teraz zapisywać do logowania z dowolną nazwą kategorii za pomocąCreateLogger
metody. ChociażILoggerFactory
jest zwykle dostępny jako singleton w kontenerze DI, ja jako użytkownik wątpię, dlaczego jakakolwiek biblioteka miałaby go używać.ILoggerProvider.CreateLogger
wygląda na to, nie jest przeznaczona do wstrzyknięć. Jest używany z,ILoggerFactory.AddProvider
aby fabryka mogła tworzyć zagregowane,ILogger
które zapisują do wieluILogger
utworzonych od każdego zarejestrowanego dostawcy. Jest to jasne, gdy sprawdzasz implementacjęLoggerFactory.CreateLogger
ILogger
również wygląda na właściwą drogę, ale jest niemożliwe w przypadku .NET Core DI. To faktycznie brzmi jak powód, dla którego musieli to zapewnićILogger<T>
w pierwszej kolejności.Nie mamy więc lepszego wyboru niż
ILogger<T>
gdybyśmy mieli wybierać z tych zajęć.Innym podejściem byłoby wstrzyknięcie czegoś innego, co opakowuje nieogólne
ILogger
, co w tym przypadku powinno być nieogólne. Chodzi o to, że opakowując go własną klasą, przejmujesz pełną kontrolę nad tym, jak użytkownik może ją skonfigurować.źródło
Domyślnym podejściem ma być
ILogger<T>
. Oznacza to, że w dzienniku logi z określonej klasy będą wyraźnie widoczne, ponieważ będą zawierać pełną nazwę klasy jako kontekst. Na przykład, jeśli pełna nazwa twojej klasy toMyLibrary.MyClass
, zobaczysz ją we wpisach dziennika utworzonych przez tę klasę. Na przykład:Powinieneś użyć,
ILoggerFactory
jeśli chcesz określić własny kontekst. Na przykład, że wszystkie dzienniki z biblioteki mają ten sam kontekst dziennika zamiast każdej klasy. Na przykład:A wtedy dziennik będzie wyglądał następująco:
Jeśli zrobisz to we wszystkich klasach, kontekst będzie po prostu MyLibrary dla wszystkich klas. Wyobrażam sobie, że chciałbyś to zrobić dla biblioteki, jeśli nie chcesz ujawniać wewnętrznej struktury klas w dziennikach.
Odnośnie opcjonalnego logowania. Myślę, że zawsze powinieneś wymagać ILogger lub ILoggerFactory w konstruktorze i pozostawić to konsumentowi biblioteki, aby go wyłączyć lub zapewnić Logger, który nie robi nic w iniekcji zależności, jeśli nie chcą rejestrować. Bardzo łatwo jest wyłączyć rejestrowanie dla określonego kontekstu w konfiguracji. Na przykład:
źródło
W przypadku projektowania bibliotek dobrym podejściem byłoby:
Nie zmuszaj konsumentów do wprowadzania loggera do twoich klas. Po prostu stwórz kolejnego ctora przekazującego NullLoggerFactory.
2. Ogranicz liczbę kategorii używanych podczas tworzenia programów rejestrujących, aby umożliwić użytkownikom łatwe konfigurowanie filtrowania dzienników.
this._loggerFactory.CreateLogger(Consts.CategoryName)
źródło