Je veux utiliser un nlogger dans mon application, peut-être que dans le futur je devrai changer le système de journalisation. Je veux donc utiliser une façade forestière.
Connaissez-vous des recommandations pour des exemples existants comment les écrire? Ou donnez-moi simplement un lien vers les meilleures pratiques dans ce domaine.
Réponses:
J'avais l'habitude d'utiliser des façades de journalisation telles que Common.Logging (même pour masquer ma propre bibliothèque CuttingEdge.Logging ), mais de nos jours, j'utilise le modèle d'injection de dépendance et cela me permet de cacher les enregistreurs derrière ma propre abstraction (simple) qui adhère à la fois à Dependency Principe d'inversion et principe de ségrégation d'interface(FAI) parce qu'il a un membre et parce que l'interface est définie par mon application; pas une bibliothèque externe. Minimiser les connaissances que les parties centrales de votre application ont sur l'existence de bibliothèques externes, mieux c'est; même si vous n'avez jamais l'intention de remplacer votre bibliothèque de journalisation. La forte dépendance à la bibliothèque externe rend le test de votre code plus difficile et complique votre application avec une API qui n'a jamais été conçue spécifiquement pour votre application.
Voici à quoi ressemble souvent l'abstraction dans mes applications:
En option, cette abstraction peut être étendue avec quelques méthodes d'extension simples (permettant à l'interface de rester étroite et de continuer à adhérer au FAI). Cela rend le code pour les consommateurs de cette interface beaucoup plus simple:
Étant donné que l'interface ne contient qu'une seule méthode, vous pouvez facilement créer une
ILogger
implémentation qui effectue un proxy vers log4net , Serilog , Microsoft.Extensions.Logging , NLog ou toute autre bibliothèque de journalisation et configurer votre conteneur DI pour l'injecter dans les classes qui ont unILogger
dans leur constructeur.Notez qu'avoir des méthodes d'extension statiques au-dessus d'une interface avec une seule méthode est assez différent d'avoir une interface avec de nombreux membres. Les méthodes d'extension ne sont que des méthodes d'assistance qui créent un
LogEntry
message et le transmettent à la seule méthode de l'ILogger
interface. Les méthodes d'extension font partie du code du consommateur; ne fait pas partie de l'abstraction. Non seulement cela permet aux méthodes d'extension d'évoluer sans qu'il soit nécessaire de changer l'abstraction, les méthodes d'extension et leLogEntry
constructeur sont toujours exécutés lorsque l'abstraction du journal est utilisée, même lorsque ce logger est stubbed / mocked. Cela donne plus de certitude quant à l'exactitude des appels à l'enregistreur lors de l'exécution dans une suite de tests. L'interface à un membre facilite également les tests; Avoir une abstraction avec de nombreux membres rend difficile la création d'implémentations (telles que des simulacres, des adaptateurs et des décorateurs).Lorsque vous faites cela, il n'y a presque jamais besoin d'une abstraction statique que les façades de journalisation (ou toute autre bibliothèque) pourraient offrir.
la source
J'ai utilisé le petit wrapper d'interface + adaptateur de https://github.com/uhaciogullari/NLog.Interface qui est également disponible via NuGet :
la source
Pour le moment, le meilleur pari est d'utiliser le package Microsoft.Extensions.Logging ( comme l'a souligné Julian ). La plupart des frameworks de journalisation peuvent être utilisés avec cela.
Définir votre propre interface, comme expliqué dans la réponse de Steven, est OK pour les cas simples, mais il manque quelques éléments que je considère importants:
IsEnabled(LogLevel)
vous le souhaitez, pour des raisons de performances encore une foisVous pouvez probablement implémenter tout cela dans votre propre abstraction, mais à ce stade, vous réinventerez la roue.
la source
En général, je préfère créer une interface comme
et dans le runtime, j'injecte une classe concrète qui est implémentée depuis cette interface.
la source
LogWarning
etLogCritical
méthodes et leurs surcharges. En faisant cela, vous violerez le principe de séparation des interfaces . Préférez définir l'ILogger
interface avec une seuleLog
méthode.LogEntry
, et donc une dépendance àLoggingEventType
. L'ILogger
implémentation doitLoggingEventTypes
probablement les gérercase/switch
, ce qui est une odeur de code . Pourquoi cacher laLoggingEventTypes
dépendance? La mise en œuvre doit gérer les niveaux de journalisation de toute façon , il serait préférable d' explicite sur ce que devrait faire une mise en œuvre, plutôt que de se cacher derrière une seule méthode avec un argument général.ICommand
qui a unHandle
qui prend unobject
. Les implémentations doiventcase/switch
sur les types possibles afin de remplir le contrat de l'interface. Ce n'est pas idéal. N'ayez pas d'abstraction qui cache une dépendance qui doit de toute façon être gérée. Au lieu de cela, ayez une interface qui énonce clairement ce qui est attendu: "Je m'attends à ce que tous les enregistreurs gèrent les avertissements, erreurs, fatals, etc.". Ceci est préférable à "Je m'attends à ce que tous les enregistreurs gèrent les messages qui incluent des avertissements, des erreurs, des fatals, etc."LoggingEventType
doit être appeléLoggingEventLevel
car les types sont des classes et doivent être codés comme tels dans la POO. Pour moi, il n'y a aucune différence entre ne pas utiliser de méthode d'interface et ne pas utiliser laenum
valeur correspondante . Utilisez plutôtErrorLoggger : ILogger
,InformationLogger : ILogger
où chaque enregistreur définit son propre niveau. Ensuite, la DI doit injecter les enregistreurs nécessaires, probablement via une clé (enum), mais cette clé ne fait plus partie de l'interface. (Vous êtes maintenant SOLIDE).Une excellente solution à ce problème est apparue sous la forme du projet LibLog .
LibLog est une abstraction de journalisation avec prise en charge intégrée des principaux enregistreurs, notamment Serilog, NLog, Log4net et Enterprise. Il est installé via le gestionnaire de packages NuGet dans une bibliothèque cible en tant que fichier source (.cs) au lieu d'une référence .dll. Cette approche permet d'inclure l'abstraction de journalisation sans forcer la bibliothèque à prendre une dépendance externe. Il permet également à un auteur de bibliothèque d'inclure la journalisation sans forcer l'application consommatrice à fournir explicitement un enregistreur à la bibliothèque. LibLog utilise la réflexion pour déterminer quel enregistreur concret est utilisé et s'y connecter sans aucun code de câblage explicite dans le (s) projet (s) de bibliothèque.
Ainsi, LibLog est une excellente solution pour la journalisation dans les projets de bibliothèque. Il suffit de référencer et de configurer un enregistreur concret (Serilog pour la victoire) dans votre application ou service principal et d'ajouter LibLog à vos bibliothèques!
la source
Au lieu d'écrire votre propre façade, vous pouvez utiliser les services Castle Logging ou Simple Logging Façade .
Les deux incluent des adaptateurs pour NLog et Log4net.
la source
Depuis 2015, vous pouvez également utiliser la journalisation .NET Core si vous créez des applications .NET Core.
Le package auquel NLog doit se connecter est:
la source