Il existe un grand nombre de bibliothèques de journalisation parmi lesquelles choisir, chacune avec ses propres particularités et avantages. (Exemples .Net: log4net, System.Diagnostics.TraceSource, nLog, etc.)
L’inclination naturelle est d’abstraire ces bizarreries et d’utiliser une façade en bois. (exemples: Castle.Services.Logging , Common.Logging , Façade de journalisation simple ) Ainsi, si un cadre de journalisation que vous utilisez devient périmé ou si un autre vient en vogue, vous pouvez simplement échanger l’implémentation et laisser le code intact.
Mais il existe plusieurs façades de journalisation parmi lesquelles choisir. Étant donné que l'abstraction était la réponse à de nombreuses implémentations de journalisation disparates, pourquoi ne pas utiliser une façade en journalisation? Si cela semble ridicule, qu'est-ce qui le rend plus ridicule que la façade d'origine de l'exploitation forestière? Qu'est-ce qui fait qu'une couche supplémentaire d'abstraction au-dessus du cadre de journalisation est le nombre magique?
Je parlerai principalement du point de vue de l'utilisation de l'abstraction pour isoler le code d'application d'un cadre de journalisation particulier. D'autres facteurs peuvent influer sur le choix du cadre de journalisation ou sur le choix (et les exigences) d'une abstraction.
J'ai récemment passé beaucoup de temps à évaluer divers cadres de journalisation, ainsi que des abstractions de journalisation tierces.
Certaines personnes pensent qu'il est utile d'isoler leur code d'application d'un framework de journalisation spécifique. Vous trouverez de nombreux articles ici sur SO comme ceci et ceci et ceci (et il y en a d’autres) où l’abattage est discuté et que beaucoup de personnes considèrent comme un problème. Bien entendu, le cadre de journalisation doit être encapsulé/résumé.
Évidemment, cela vous permet de ne pas être lié à un cadre spécifique. Est-ce important? Allez-vous vraiment changer votre framework de journalisation? Eh bien, il y a aussi beaucoup de gens qui ne mentionnent pas l'emballage ou ceux qui le déconseillent. Si vous regardez quelques exemples de code d'habillage de structure de journalisation qui ont été publiés ici, vous pouvez également voir de nombreux exemples montrant pourquoi certaines personnes au moins ne devraient pas envelopper leur structure de journalisation!
Si vous aviez démarré un projet récemment, vous avez peut-être examiné les cadres de journalisation et, peut-être, vous vous êtes limité à deux finalistes: log4net et NLog. Chacun a des arguments en sa faveur. log4net est clairement un favori, probablement LE favori de ceux qui ont exprimé une opinion. NLog fournit des fonctionnalités très similaires. A en juger par la popularité, log4net pourrait être le choix évident. Basé sur les capacités, ils semblent très similaires. Sur la base des "activités récentes" (comme indiqué par les archivages de leurs répertoires de code source par activité de blog ou par manque d’activités), NLog est le choix évident. Si vous deviez choisir il y a un an, vous pourriez utiliser log4net car ce serait le choix "sûr". On ne savait pas quand NLog allait sortir. Au cours de l'année écoulée, NLog a connu un cycle de développement assez important et a publié une version bêta il y a quelques jours à peine.
Lequel choisir il y a un an? Lequel choisir maintenant? Était-ce un meilleur choix alors? Est-ce le meilleur choix maintenant?
Une abstraction vous donne la possibilité de différer la décision de votre choix (vous n'avez même pas nécessairement à choisir à jamais, bien que vous souhaitiez probablement le faire si vous envisagez de fournir le cadre de journalisation avec votre produit). Vous pouvez tester l'un après l'autre, puis l'autre, et avoir une idée de la façon dont ils travaillent avec votre application, avec votre équipe et dans votre environnement. Utiliser quelque chose comme Common.Logging ou SLF vous permet de commencer à écrire du code maintenant, de coder vers une interface/API de journalisation et d'obtenir votre code de journalisation en place. Si vous pensez que l'interface/API fournie par l'abstraction est suffisante pour votre travail (et pourquoi ne le seriez-vous pas, étant donné qu'elle est essentiellement identique à l'interface/API fournie par log4net et NLog), il n'y a pas grand chose danger d'utiliser l'abstraction. Au cours du cycle de développement, vous constaterez peut-être qu’un cadre ou l’autre correspond mieux à vos besoins. Après avoir codé pour l'abstraction, vous êtes libre de faire ce choix à tout moment, jusqu'au moment où votre produit sortira.
Vous pensez peut-être même, au fond de votre esprit, que vous pourriez écrire une bibliothèque de journalisation à partir de rien. Encore une fois, si vous pensez que l'interface/API de log4net et/ou NLog est suffisante, vous pouvez implémenter votre bibliothèque de journalisation avec une API similaire. Si vous croyez cela, cela pourrait être une autre raison d'utiliser une abstraction. Encore une fois, vous pouvez commencer à écrire du code (pour votre produit, pas votre bibliothèque de journalisation) dès aujourd'hui, en vous connectant avec un autre framework de journalisation jusqu'à ce que votre bibliothèque de journalisation "à partir de zéro" soit prête. Peut-être voudrez-vous vraiment utiliser System.Diagnostics.TraceSource et Ukadc.Diagnostics (pour obtenir des capacités de formatage de sortie similaires à log4net ou NLog) afin d’obtenir une "meilleure" intégration à la journalisation mise en œuvre par Microsoft dans certains leurs plates-formes utilisant TraceSources. Il pourrait être assez facile d'écrire un "enregistreur" en termes de TraceSources, puis d'écrire l'abstraction afin de pouvoir le brancher sur Common.Logging ou SLF. (Si l'interface/API est suffisante, vous pouvez simplement écrire votre "consignateur" en termes d'interface de la bibliothèque d'abstraction et ne pas avoir à écrire une couche d'abstraction supplémentaire).
Avec des arguments aussi convaincants, pourquoi n’utiliserait-il JAMAIS une abstraction? Haha je plaisante!
Si une abstraction est bonne, devriez-vous écrire la vôtre ou en utiliser une existante? Si vous en écrivez un vous-même, vous devez évidemment l'écrire. Comment est-que quelqu'un peut faire ça? Eh bien, vous pouvez simplement définir une interface et envelopper un framework (soyez prudent et enveloppez-le correctement!). Plus tard, si vous décidez que vous souhaitez basculer, enveloppez ce cadre. Si vous êtes prudent, vous ne devez modifier aucun code d'application, à l'exception peut-être de l'emplacement où vous créez réellement les objets du cadre sous-jacent. Peut-être que c'est bon Vous avez évité une dépendance vis-à-vis de l'abstraction d'un tiers pour le "petit" prix d'implémentation d'un seul wrapper sur un seul framework. Cependant, il y a un coût. Tant que vous n’avez pas écrit votre abstraction, vous ne pouvez pas vraiment écrire beaucoup de code d’application qui en soit connecté, à moins d’avoir une bonne stratégie pour le changer en votre abstraction. Il devient également plus difficile de tester deux ou plusieurs frameworks pour déterminer celui qui vous convient le mieux. Chaque framework que vous souhaitez "essayer" nécessite un autre travail d'intégration. Si vous souhaitez passer facilement d’un framework à l’autre (au moins pendant le cycle de développement), vous avez du travail à faire pour le rendre facile. Les frameworks tiers fournissent cette solution prête à l'emploi.
Hou la la! Maintenant je suis vendu! Donnez-moi l'abstraction de journalisation, ou donnez-moi la mort!
Les abstractions de journalisation sont-elles toutes de la sauce? Y a-t-il un inconvénient? Ils ne peuvent pas que grand, peuvent-ils?
Eh bien, comme toujours, lorsque vous "achetez" quelque chose ou que vous obtenez quelque chose de gratuit, vous obtenez ce qui est disponible. Les abstractions de journalisation ne sont pas différentes. Ni Common.Logging ni SLF n'exposent au moins un ensemble très important de fonctionnalités de log4net/NLog - les capacités de journalisation (GDC, MDC, NDC). Celles-ci peuvent être essentielles pour obtenir des informations adéquates consignées et formatées afin de vous permettre de tirer le meilleur parti de vos ressources. SLF ne fournit pas d'abstraction de TraceSource. Il ne fournit pas non plus de fonctions IsXXXEnabled. Common.Logging fournit une abstraction de TraceSource. Castle.Logging EXPOSE GDC/MDC/NDC pour log4net et NLog. Il fournit également une abstraction de TraceSource. L'abstraction TraceSource de Castle améliore également la journalisation TraceSource en fournissant une fonction de nommage "hiérarchique", similaire à celle fournie par log4net et NLog. Ça a l'air plutôt cool!
En outre, ces projets sont tous opensource d'une forme ou d'une autre. Ainsi, en fonction de l'abstraction, les développeurs pourraient avoir plus ou moins intérêt à le maintenir à jour et à ajouter de nouvelles fonctionnalités. Common.Logging est passé par quelques versions et est utilisé, autant que je sache, dans Spring.Net. Semble raisonnablement actif, au moins historiquement. Castle.Logging est utilisé dans le cadre du château. Ainsi, ils ont apparemment des clients "réels" et obtiennent une utilisation "réelle", ce qui, espérons-le, conduira à une implémentation accrue des fonctionnalités. Autant que je sache, SLF n'est pas utilisé dans le cadre d'une "véritable" plate-forme de développement. Il est donc difficile de dire combien il est exercé.
La feuille de route de ces plateformes n’est pas claire. Common.Logging a certaines fonctionnalités à venir répertoriées sur leur site Web, mais aucune indication précise quant à leur disponibilité. Le site dit "juin", mais de quelle année? À quelle fréquence la liste de diffusion est-elle contrôlée? Pour le format SLF, quelle est la fréquence de surveillance de leur codeplex? Où se situe la priorité de ces projets "gratuits" par rapport aux emplois payants des développeurs? Pouvez-vous vous permettre de faire appel à une tierce partie pour implémenter une fonctionnalité dont vous avez besoin? Seront-ils réceptifs si vous implémentez quelque chose et que vous le soumettez ensuite pour qu'il soit pris en compte dans le produit?
Sur le plan positif, toutes les sources de toutes ces abstractions sont disponibles, vous pouvez donc en assumer la responsabilité et apporter des corrections ou des améliorations, sans avoir à passer par le temps et l'énergie nécessaires à la création d'une abstraction rayure. Aimez-vous Common.Logging mais vous voulez vraiment log4net/NLog GDC/MDC/NDC? Obtenez l'implémentation de Castle et ajoutez-la à Common.Logging. Voila! Une abstraction de journalisation contenant près de 100% de l'API de journalisation log4net/NLog. Préférez-vous le format SLF, mais souhaiteriez-vous qu’il utilise IsXXXEnabled? Pas beaucoup de travail pour mettre cela en œuvre. Allez-y et pointez sur le GDC/MDC/NDC pendant que vous y êtes. Aimez-vous le château? (Je ne le connais pas très bien, je ne sais pas si c'est facile à utiliser en dehors de Castle, si ça compte) Soyez prudent, je ne l'ai pas utilisé, mais en regardant la source sur git, cela ressemble à l'enregistreur NLog abstraction peut-être pas conserver les informations du site de l'appel.
Est-il éthique de prendre part à plusieurs projets Open Source et de les combiner pour créer un "super" projet (pour votre propre utilisation ou celle de votre entreprise)? Est-il mauvais de prendre Common.Logging et de l’ajouter à la mise en œuvre du Castle GDC/MDC/NDC? Je ne sais pas. Je laisserai quelqu'un d'autre répondre à cela.
J'ai presque fini ...
Certaines abstractions de journalisation tierces offrent d'autres fonctionnalités. Vous pouvez utiliser une bibliothèque implémentée en termes de, disons log4net. Vous pourriez ne pas vouloir utiliser log4net, ou du moins ne pas vouloir y être lié. Common.Logging (et peut-être SLF) vous permet relativement facilement de capturer les messages de journalisation log4net et de les rediriger via l'abstraction afin qu'ils soient capturés dans le flux de journalisation de l'infrastructure de journalisation sous-jacente de l'abstraction. SLF pourrait fournir quelque chose de similaire. Bien sûr, vous pourrez peut-être faire quelque chose de similaire avec les infrastructures de journalisation existantes, directement ou en écrivant un log4net Appender, un NLog Target ou un System.Diagnostics TraceListener personnalisé. Ces caractéristiques ne sont pas encore très présentes dans mon évaluation de l'opportunité d'utiliser une abstraction de journalisation tierce sur mon projet, parce que je m'intéresse principalement au seul aspect de l'abstraction.
Alors, où suis-je? Je pense qu'il est utile de garder votre code d'application isolé d'un framework de journalisation spécifique. Pour moi, Common.Logging ressemble à un choix d'abstraction solide, bien que certaines fonctionnalités importantes manquent (GDC/MDC/NDC) et qu'il ne soit pas compatible Silverlight. Ce serait formidable que ces fonctionnalités soient bientôt disponibles. Je suis à l'aise avec la mise en œuvre de GDC/MDC/NDC si je dois le faire. Le rendre compatible avec Silverlight demanderait probablement plus d’efforts, principalement parce que je n’ai pas une expérience particulière de C #/.NET/Silverlight. Tant que ces problèmes ne seront pas résolus, nous pourrons écrire beaucoup de code d’application avec Common.Logging in place. Nous pouvons passer notre temps à développer notre application plutôt qu'à développer une autre bibliothèque de journalisation ou une bibliothèque d'abstraction. Si nous devions ajouter nous-mêmes ces fonctionnalités manquantes, eh bien, nous aurions dû en faire beaucoup si nous avions implémenté nous-mêmes une bibliothèque de journalisation ou une bibliothèque d'abstraction.
Je pense que ce qui fait de One (niveau d’abstraction) le chiffre magique ici est que Zero est trop peu et Deux est trop.
Le fait d'échanger un enregistreur derrière une façade d'enregistreur (nombre de niveaux: 1) peut éventuellement présenter des avantages pour l'utilisateur, tel que le nouvel enregistreur peut faire quelque chose que l'ancien enregistreur ne peut pas. Je peux imaginer que cela pourrait être la performance, le soutien de certains types d’appenders, etc.
Il est beaucoup plus difficile d'imaginer les avantages pour l'utilisateur de changer de façade d'enregistreur (nombre de niveaux: 2).
(Et si le nombre de niveaux est 0, alors c'est probablement juste une mauvaise conception orientée objet: vous aurez des milliers d'endroits dans votre code où le consignateur est référencé et que se passe-t-il s'il y a un changement radical dans la prochaine version du consignateur )
Le problème avec les façades d'enregistreur semble être que vous devez choisir l'une des options tierces ou créer votre propre option et vous préparer à vous y tenir longtemps.
Jusqu'à ce que NLog et log4net fournissent une interface qui puisse être utilisée à la place des classes concrètes, je les ai toujours abstraites derrière ma propre interface et classe d'emballage.
Pourquoi?
Pour obtenir la meilleure couverture possible de tous les besoins des utilisateurs en matière de tests, en particulier lorsque ces besoins couvrent la journalisation.
Lorsque vous enregistrez toute votre journalisation via une interface plutôt que sur une classe concrète, il devient très facile de fournir un objet fictif avec l'interface de journalisation (choisissez votre manière de procéder, injection de dépendance, etc.) pour tout enregistrer. les appels de journalisation effectués pendant un scénario de test particulier.
Ceux-ci peuvent ensuite être affirmés et si un changement de code interrompt la journalisation, vos tests unitaires le couvriront.
Si NLog ou log4Net fournissaient une interface à leurs enregistreurs, je n'aurais pas besoin de fournir une interface et une classe wrapper, car je pourrais simplement me moquer de leur interface pour les tests.
Dans mon projet, j'utilise System.Diagnostics.Trace.TraceError(...)
, System.Diagnostics.Debug.Print(...)
comme façade pour la journalisation. Pour organiser (écrire) les journaux, j'utilise NLog, c'est-à-dire que dans app.config, j'ai la configuration pour NLog et la redirection de la trace de .net vers NLog.
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="file" xsi:type="File"
layout="${longdate:universalTime=true}Z [${threadid}] ${pad:padding=5:inner=${level:uppercase=true}} ${logger} ${message}"
fileName="${basedir}/App_Data/logfile.txt"...
</targets>
</nlog>
<system.diagnostics>
<trace>
<listeners>
<add name="nlog" type="NLog.NLogTraceListener, NLog" />
</listeners>
</trace>
</system.diagnostics>
Cela ne me fournit pas à un enregistreur. Lorsque j'envoie mes composants à des clients, ils peuvent utiliser n'importe quel enregistreur de leur choix. L’utilisation d’un enregistreur particulier dans l’application pourrait poser des problèmes, c’est-à-dire que vous pouvez utiliser nlog mais vos clients utilisent log4net.
Une utilisation importante d'une façade de journalisation est lorsque vous êtes en train d'écrire une bibliothèque. L'incorporation de dépendances dans une bibliothèque est toujours quelque chose qui nécessite un peu de soin, et la journalisation encore plus.
En bref, vous ne voulez pas forcer votre implémentation de journalisation sur les utilisateurs de votre bibliothèque. En utilisant une façade bien choisie, ils seront en mesure de gérer les journaux de votre bibliothèque avec leur propre cadre de choix et n'auront pas à passer par des exclusions et des échappatoires étranges en matière de dépendance pour faire coexister votre environnement de journalisation et le leur.
Ce n'est pas le chiffre magique, cela dépend de votre souplesse. Si vous envisagez de changer un jour la façade en rondins, vous devriez écrire une façade. Si vous envisagez de ne modifier que le journal vous avez besoin d’une façade. Si vous ne pensez à rien, n’utilisez pas de façade.
Comme vous l'avez dit, l'inconvénient réside dans les capacités spéciales. Si vous les utilisez, écrivez votre propre façade uniquement.
Il est utilisable en protocole pour système plugin. Au lieu de dire use Some3rdParty.dll and MyPlugingApi.dll
, je ne documenterais que MyPlugingApi.dll
. L'interface de façade de journal suggérera et documentera certaines utilisations qui mèneront probablement à des journaux lisibles et à des performances de journalisation suffisantes. Et bien sûr, les modifications apportées n'entraîneront aucune modification de l'API du plug-in.
Pourquoi faut-il changer la mise en œuvre? Cela peut arriver si le courant semble ralentir pour démarrer en dehors de la configuration ou pour écrire des entrées, si vous souhaitez développer une version réduite de .NET qui n'a pas besoin de tierce partie, en s'intégrant à un autre code base utilisant d'autres enregistreurs de journaux.
J'ai aussi écrit encore une autre façade qui est enfant de pensées dans cette réponse.
Je ne suis certes pas un expert moi-même, mais dans notre groupe, l'intérêt de la façade ne consiste pas à nous donner la possibilité de modifier le cadre de journalisation. C’est vrai, c’est quelque chose que nous obtenons, mais nous appartenons à la catégorie qui a très peu de chances de changer notre cadre.
Dans notre cas, nous utilisons une façade pour adapter la journalisation interface aux besoins de notre application. Nous avons constaté que, dans tous les cadres sémantiques que nous avons examinés, ils étaient encore trop concentrés sur ce que nous appelons le modèle de journalisation "forensics": une personne fouille dans les journaux à la recherche d'une ligne de sortie pour tenter d'analyser un événement.
Bien que ce soit un cas d'utilisation pour nous aussi, ce n'est pas notre cas d'utilisation principal. Nous voulons davantage d'un cadre instrumentation que d'un cadre de journalisation, ce qui nous permettra de faire rapport sur des sujets d'intérêt, même ceux auxquels nous n'avions pas pensé au moment de la mise en œuvre.
Par exemple, notre application n'a pas besoin d'un "message" pour accompagner un événement; notre "consignateur" acceptera plutôt les énumérations définissant le type d'événement et les objets d'état représentant des spécificités (par exemple, des horodatages ou d'autres valeurs) et les sérialisera pour faciliter la création de rapports, l'analyse de la performance, les mesures de la valeur commerciale, etc. médecine légale. (Nous reconnaissons que nous rendons en fait la criminalistique traditionnelle un peu plus difficile au profit d'une interface de journalisation simple à utiliser et compréhensible qui augmente la probabilité que nous l'utilisions réellement et plus souvent).
Donc, pour vous donner une réponse sommaire succincte, voici un résumé des avantages que nous estimons tirer de l’utilisation d’une façade en bois.
Et finalement, 0 façade perdrait ces avantages, et 2 façades n’ajouterait rien à ces avantages. C’est pourquoi 1 façade est le bon chiffre pour nous.
Bonne question, @Brian! :)
Dans cet exemple, vous n'avez besoin que d'un niveau d'abstraction pour pouvoir échanger votre enregistreur.
Quel avantage supplémentaire serait de pouvoir échanger votre façade d’exploitation forestière?