Cela peut être quelque peu lié à Passer ILogger ou ILoggerFactory aux constructeurs dans AspNet Core? , mais il s'agit spécifiquement de la conception de bibliothèques , pas de la manière dont l'application réelle qui utilise ces bibliothèques implémente sa journalisation.
J'écris une bibliothèque .net Standard 2.0 qui sera installée via Nuget, et pour permettre aux personnes utilisant cette bibliothèque d'obtenir des informations de débogage, je dépends de Microsoft.Extensions.Logging.Abstractions pour permettre l'injection d'un enregistreur standardisé.
Cependant, je vois plusieurs interfaces et des exemples de code sur le Web utilisent ILoggerFactory
et créent parfois un enregistreur dans le ctor de la classe. Il y a aussi ILoggerProvider
qui ressemble à une version en lecture seule de l'usine, mais les implémentations peuvent implémenter ou non les deux interfaces, donc je devrais choisir. (L'usine semble plus courante que le fournisseur).
Certains codes que j'ai vu utilisent l' ILogger
interface non générique et peuvent même partager une instance du même enregistreur, et certains prennent un ILogger<T>
dans leur ctor et s'attendent à ce que le conteneur DI prenne en charge les types génériques ouverts ou l'enregistrement explicite de chaque ILogger<T>
variante de ma bibliothèque les usages.
À l'heure actuelle, je pense que ILogger<T>
c'est la bonne approche, et peut-être un facteur qui ne prend pas cet argument et passe simplement un Null Logger à la place. De cette façon, si aucune journalisation n'est nécessaire, aucune n'est utilisée. Cependant, certains conteneurs DI choisissent le plus gros facteur et échoueraient donc de toute façon.
Je suis curieux de savoir ce que je suis censé faire ici pour créer le moins de maux de tête pour les utilisateurs, tout en permettant une prise en charge appropriée de la journalisation si vous le souhaitez.
Ceux-ci sont tous valables sauf pour
ILoggerProvider
.ILogger
etILogger<T>
sont ce que vous êtes censé utiliser pour la journalisation. Pour obtenir unILogger
, vous utilisez unILoggerFactory
.ILogger<T>
est un raccourci pour obtenir un enregistreur pour une catégorie particulière (raccourci pour le type comme catégorie).Lorsque vous utilisez
ILogger
pour effectuer la journalisation, chaque inscritILoggerProvider
a la possibilité de gérer ce message de journal. Il n'est pas vraiment valable pour la consommation de code d'appelerILoggerProvider
directement.la source
ILoggerFactory
serait formidable comme moyen facile de câbler DI pour les consommateurs en n'ayant qu'une seule dépendance ( "Donnez-moi simplement une usine et je créerai mes propres enregistreurs" ), mais cela empêcherait d'utiliser un enregistreur existant ( à moins que le consommateur n'utilise un wrapper). Prendre unILogger
- générique ou non - permet au consommateur de me donner un enregistreur qu'il a spécialement préparé, mais rend la configuration DI potentiellement beaucoup plus complexe (à moins qu'un conteneur DI prenant en charge Open Generics soit utilisé). Cela vous semble-t-il correct? Dans ce cas, je pense que j'irai avec l'usine.ILogger<T>
, je prendrai unILogger<MyClass>
ou même juste à laILogger
place - de cette façon, l'utilisateur peut le câbler avec un seul enregistrement, et sans avoir besoin de génériques ouverts dans leur conteneur DI. Penchée vers le non génériqueILogger
, mais expérimentera beaucoup ce week-end.Le
ILogger<T>
était une réelle qui est faite pour DI. L'ILogger est venu pour aider à implémenter le modèle d'usine beaucoup plus facilement, au lieu d'écrire vous-même toute la logique DI et Factory, c'était l'une des décisions les plus intelligentes du noyau asp.net.Vous pouvez choisir entre:
ILogger<T>
si vous avez besoin d'utiliser des modèles d'usine et DI dans votre code ou si vous pouvez utiliser leILogger
, pour implémenter une journalisation simple sans DI nécessaire.étant donné que, The
ILoggerProvider
est juste un pont pour gérer chacun des messages du journal enregistré. Il n'est pas nécessaire de l'utiliser, car il n'effectue rien que vous devez intervenir dans le code, il écoute le ILoggerProvider enregistré et gère les messages. C'est à peu près ça.la source
this ILogger
.S'en tenir à la question, je pense que
ILogger<T>
c'est la bonne option, compte tenu des inconvénients des autres options:ILoggerFactory
force votre utilisateur à céder le contrôle de la fabrique d'enregistreurs globaux mutables à votre bibliothèque de classes. De plus, en acceptantILoggerFactory
votre classe, vous pouvez maintenant écrire dans le journal avec n'importe quel nom de catégorie arbitraire avecCreateLogger
méthode. Bien qu'ilILoggerFactory
soit généralement disponible en tant que singleton dans un conteneur DI, en tant qu'utilisateur, je doute pourquoi une bibliothèque aurait besoin de l'utiliser.ILoggerProvider.CreateLogger
ressemble, elle n'est pas destinée à l'injection. Il est utilisé avecILoggerFactory.AddProvider
pour que l'usine puisse créer des agrégésILogger
qui écrivent sur plusieursILogger
créés à partir de chaque fournisseur enregistré. Cela est clair lorsque vous inspectez la mise en œuvre deLoggerFactory.CreateLogger
ILogger
ressemble également à la voie à suivre, mais c'est impossible avec .NET Core DI. Cela ressemble en fait à la raison pour laquelle ils devaient fournirILogger<T>
en premier lieu.Donc, après tout, nous n'avons pas de meilleur choix que
ILogger<T>
de choisir parmi ces classes.Une autre approche consisterait à injecter quelque chose d'autre qui enveloppe non générique
ILogger
, qui dans ce cas devrait être non générique. L'idée est qu'en l'enveloppant avec votre propre classe, vous prenez le contrôle total de la façon dont l'utilisateur peut le configurer.la source
L'approche par défaut est censée être
ILogger<T>
. Cela signifie que dans le journal, les journaux de la classe spécifique seront clairement visibles car ils incluront le nom complet de la classe comme contexte. Par exemple, si le nom complet de votre classe est,MyLibrary.MyClass
vous l'obtiendrez dans les entrées de journal créées par cette classe. Par exemple:Vous devez utiliser le
ILoggerFactory
si vous souhaitez spécifier votre propre contexte. Par exemple, tous les journaux de votre bibliothèque ont le même contexte de journal au lieu de chaque classe. Par exemple:Et puis le journal ressemblera à ceci:
Si vous faites cela dans toutes les classes, le contexte sera juste MyLibrary pour toutes les classes. J'imagine que vous voudriez faire cela pour une bibliothèque si vous ne voulez pas exposer la structure de classe interne dans les journaux.
Concernant la journalisation optionnelle. Je pense que vous devriez toujours exiger ILogger ou ILoggerFactory dans le constructeur et laisser au consommateur de la bibliothèque le soin de l'éteindre ou de fournir un Logger qui ne fait rien dans l'injection de dépendance s'il ne veut pas de journalisation. Il est très facile de désactiver la journalisation pour un contexte spécifique dans la configuration. Par exemple:
la source
Pour la conception d'une bibliothèque, une bonne approche serait:
Ne forcez pas les consommateurs à injecter un enregistreur dans vos classes. Créez simplement un autre ctor passant NullLoggerFactory.
2. Limitez le nombre de catégories que vous utilisez lorsque vous créez des enregistreurs pour permettre aux consommateurs de configurer facilement le filtrage des journaux.
this._loggerFactory.CreateLogger(Consts.CategoryName)
la source