hmm. semble qu'il y ait deux problèmes ici - l'un est d'avoir un objet journal réel par classe et l'autre est d'avoir le nom du journal identique à la classe.
Peter Recore
Réponses:
59
Avec log4net, l'utilisation d'un enregistreur par classe facilite la capture de la source du message du journal (c'est-à-dire la classe qui écrit dans le journal). Si vous n'avez pas un enregistreur par classe, mais que vous en avez un pour l'ensemble de l'application, vous devez recourir à d'autres astuces de réflexion pour savoir d'où proviennent les messages du journal.
Comparez ce qui suit:
Journal par classe
using System.Reflection;
privatestaticreadonly ILog _logger =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
publicvoidSomeMethod()
{
_logger.DebugFormat("File not found: {0}", _filename);
}
Un enregistreur par application (ou similaire)
Logger.DebugFormat("File not found: {0}", _filename); // Logger determines caller
-- or --
Logger.DebugFormat(this, "File not found: {0}", _filename); // Pass in the caller
En utilisant le deuxième exemple, le Logger aurait besoin de créer une trace de pile pour voir qui l'appelait ou votre code devrait toujours passer l'appelant. Avec le style enregistreur par classe, vous faites toujours cela, mais vous pouvez le faire une fois par classe au lieu d'une fois par appel et éliminer un grave problème de performances.
Merci, cela aide à clarifier les choses. Nous étions juste en train de mettre manuellement le nom de la classe et la méthode dans le message (c'est-à-dire "ImageCreator.CreateThumbnail () appelé"), mais c'est mieux si le logger peut le gérer.
Daniel T.
1
Juste pour info, il est devenu "meilleure" pratique d'avoir un enregistreur par instance, plutôt que par classe (c'est-à-dire statique) car cela facilite la capture d'informations telles que les informations de thread. Évidemment, c'est une question de goût, pas de "règle absolue", mais je voulais simplement jeter ça.
Will Hartung
7
@will, pouvez-vous expliquer cela un peu plus? Lors de la journalisation à l'aide d'un enregistreur par classe, j'enregistre toujours l'ID de fil afin que l'enregistreur puisse obtenir les informations de fil en cours. Toute autre information de fil serait également disponible pour l'enregistreur.
Jeremy Wiebe
@Jeremy Wiebe: Est-ce la seule raison? Fonctionnellement, il n'y a pas de problèmes si j'utilise une seule variable globale de type logger pour toute l'application?
giorgim
1
@Giorgi Non, je ne pense pas. Vous pouvez obtenir beaucoup de ces informations ces jours-ci avec les attributs CallerInformation, ce qui rend un enregistreur par classe un peu moins pertinent - msdn.microsoft.com/en-us/library/hh534540.aspx
Jeremy Wiebe
15
Avantage d'utiliser "logger per file" dans NLog: vous avez la possibilité de gérer / filtrer les logs par namespace et par nom de classe. Exemple:
<logger name="A.NameSpace.MyClass" minlevel="Debug" writeTo="ImportantLogs" />
<logger name="A.NameSpace.MyOtherClass" minlevel="Trace" writeTo="ImportantLogs" />
<logger name="StupidLibrary.*" minlevel="Error" writeTo="StupidLibraryLogs" />
<!-- Hide other messages from StupidLibrary -->
<logger name="StupidLibrary.*" final="true" />
<!-- Log all but hidden messages -->
<logger name="*" writeTo="AllLogs" />
NLogger a un extrait de code très utile pour ce faire. L' nloggerextrait créera le code suivant:
Donc, seulement quelques touches et vous avez un enregistreur par classe. Il utilisera l'espace de noms et le nom de classe comme nom de l'enregistreur. Pour définir un nom différent pour votre enregistreur de classe, vous pouvez utiliser ceci:
Et, comme @JeremyWiebe l'a dit, vous n'avez pas besoin d'utiliser des astuces pour obtenir le nom de la classe qui essaie de consigner un message: le nom de l'enregistreur (qui est généralement le nom de la classe) peut être facilement consigné dans un fichier (ou autre cible) en utilisant ${logger}dans la mise en page.
Vous saurez toujours d'où provient une instruction de journal particulière, si vous incluez le nom de l'enregistreur dans votre format de sortie de journal.
Vous pouvez contrôler les instructions de journal que vous voyez à un niveau précis en activant ou désactivant certains enregistreurs ou en définissant leur niveau.
Dans la plupart des cas, le nom de la classe fournit un bon nom pour l'enregistreur. Lors de l'analyse des fichiers journaux, vous pouvez voir le message du journal et l'associer directement à une ligne de code.
Les journaux SQL d'Hibernate sont un bon exemple où ce n'est pas la meilleure approche. Il existe un enregistreur partagé nommé "Hibernate.SQL" ou quelque chose comme ça, où un certain nombre de classes différentes écrivent du SQL brut dans une seule catégorie de journaliseur.
Du point de vue du développement, c'est plus simple si vous n'avez pas à créer un objet de journalisation à chaque fois. D'un autre côté, si vous ne le faites pas, mais que vous le créez de manière dynamique en utilisant la réflexion, cela ralentira les performances. Pour résoudre ce problème, vous pouvez utiliser le code suivant qui crée le journal de manière dynamique de manière asynchrone:
using NLog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespaceWinForms
{
classlog
{
publicstaticasyncvoidLog(int severity, string message)
{
await Task.Run(() => LogIt(severity, message));
}
privatestaticvoidLogIt(int severity, string message)
{
StackTrace st = new StackTrace();
StackFrame x = st.GetFrame(2); //the third one goes back to the original caller
Type t = x.GetMethod().DeclaringType;
Logger theLogger = LogManager.GetLogger(t.FullName);
//https://github.com/NLog/NLog/wiki/Log-levelsstring[] levels = { "Off", "Trace", "Debug", "Info", "Warn", "Error", "Fatal" };
int level = Math.Min(levels.Length, severity);
theLogger.Log(LogLevel.FromOrdinal(level), message);
}
}
}
Deux raisons me viennent immédiatement à l'esprit:
Le fait d'avoir un journal séparé pour chaque classe facilite le regroupement de tous les messages / erreurs du journal relatifs à une classe donnée.
Avoir un journal dans une classe vous permet de consigner des détails internes qui peuvent ne pas être accessibles en dehors de la classe (par exemple, l'état privé, les informations concernant l'implémentation d'une classe, etc.).
Vous avez un enregistreur dans la classe, qu'il soit défini au niveau de la classe ou globalement. Les enregistreurs globaux ne sont pas «en dehors» de la classe du point de vue de la visibilité. Vous faites toujours référence à l'enregistreur global à partir de la classe en question afin d'avoir une visibilité complète.
Robert
0
Probablement parce que vous voulez pouvoir enregistrer des méthodes qui ne sont visibles que par la classe sans interrompre l'encapsulation, cela facilite également l'utilisation de la classe dans une autre application sans interrompre la fonctionnalité de journalisation.
Cela rend difficile l'utilisation de cette classe dans une autre application. Vous devez référencer la bibliothèque de journalisation que vous le vouliez ou non.
Piotr Perak
0
Facilite la configuration des appenders par espace de noms ou classe.
Si vous utilisez NLOG, vous pouvez spécifier le site d'appel dans la configuration, cela enregistrera le nom de la classe et la méthode où se trouvait l'instruction de journalisation.
<property name="CallSite"value="${callsite}" />
Vous pouvez ensuite utiliser une constante pour le nom de votre enregistreur ou le nom de l'assemblage.
Clause de non-responsabilité: je ne sais pas comment NLOG recueille ces informations, je suppose que cela serait une réflexion, vous devrez peut-être considérer les performances. Il y a quelques problèmes avec les méthodes Async si vous n'utilisez pas NLOG v4.4 ou version ultérieure.
Réponses:
Avec log4net, l'utilisation d'un enregistreur par classe facilite la capture de la source du message du journal (c'est-à-dire la classe qui écrit dans le journal). Si vous n'avez pas un enregistreur par classe, mais que vous en avez un pour l'ensemble de l'application, vous devez recourir à d'autres astuces de réflexion pour savoir d'où proviennent les messages du journal.
Comparez ce qui suit:
Journal par classe
using System.Reflection; private static readonly ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); public void SomeMethod() { _logger.DebugFormat("File not found: {0}", _filename); }
Un enregistreur par application (ou similaire)
Logger.DebugFormat("File not found: {0}", _filename); // Logger determines caller -- or -- Logger.DebugFormat(this, "File not found: {0}", _filename); // Pass in the caller
En utilisant le deuxième exemple, le Logger aurait besoin de créer une trace de pile pour voir qui l'appelait ou votre code devrait toujours passer l'appelant. Avec le style enregistreur par classe, vous faites toujours cela, mais vous pouvez le faire une fois par classe au lieu d'une fois par appel et éliminer un grave problème de performances.
la source
Avantage d'utiliser "logger per file" dans NLog: vous avez la possibilité de gérer / filtrer les logs par namespace et par nom de classe. Exemple:
<logger name="A.NameSpace.MyClass" minlevel="Debug" writeTo="ImportantLogs" /> <logger name="A.NameSpace.MyOtherClass" minlevel="Trace" writeTo="ImportantLogs" /> <logger name="StupidLibrary.*" minlevel="Error" writeTo="StupidLibraryLogs" /> <!-- Hide other messages from StupidLibrary --> <logger name="StupidLibrary.*" final="true" /> <!-- Log all but hidden messages --> <logger name="*" writeTo="AllLogs" />
NLogger a un extrait de code très utile pour ce faire. L'
nlogger
extrait créera le code suivant:private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
Donc, seulement quelques touches et vous avez un enregistreur par classe. Il utilisera l'espace de noms et le nom de classe comme nom de l'enregistreur. Pour définir un nom différent pour votre enregistreur de classe, vous pouvez utiliser ceci:
private static NLog.Logger logger = NLog.LogManager.GetLogger("MyLib.MyName");
Et, comme @JeremyWiebe l'a dit, vous n'avez pas besoin d'utiliser des astuces pour obtenir le nom de la classe qui essaie de consigner un message: le nom de l'enregistreur (qui est généralement le nom de la classe) peut être facilement consigné dans un fichier (ou autre cible) en utilisant
${logger}
dans la mise en page.la source
Je vois quelques raisons à ce choix.
la source
Il existe également un avantage en termes de performances dans le cas de NLog. La plupart des utilisateurs utiliseront
La recherche de la classe actuelle à partir de la trace de pile nécessite des performances (mais pas beaucoup).
la source
Dans la plupart des cas, le nom de la classe fournit un bon nom pour l'enregistreur. Lors de l'analyse des fichiers journaux, vous pouvez voir le message du journal et l'associer directement à une ligne de code.
Les journaux SQL d'Hibernate sont un bon exemple où ce n'est pas la meilleure approche. Il existe un enregistreur partagé nommé "Hibernate.SQL" ou quelque chose comme ça, où un certain nombre de classes différentes écrivent du SQL brut dans une seule catégorie de journaliseur.
la source
Du point de vue du développement, c'est plus simple si vous n'avez pas à créer un objet de journalisation à chaque fois. D'un autre côté, si vous ne le faites pas, mais que vous le créez de manière dynamique en utilisant la réflexion, cela ralentira les performances. Pour résoudre ce problème, vous pouvez utiliser le code suivant qui crée le journal de manière dynamique de manière asynchrone:
using NLog; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WinForms { class log { public static async void Log(int severity, string message) { await Task.Run(() => LogIt(severity, message)); } private static void LogIt(int severity, string message) { StackTrace st = new StackTrace(); StackFrame x = st.GetFrame(2); //the third one goes back to the original caller Type t = x.GetMethod().DeclaringType; Logger theLogger = LogManager.GetLogger(t.FullName); //https://github.com/NLog/NLog/wiki/Log-levels string[] levels = { "Off", "Trace", "Debug", "Info", "Warn", "Error", "Fatal" }; int level = Math.Min(levels.Length, severity); theLogger.Log(LogLevel.FromOrdinal(level), message); } } }
la source
Deux raisons me viennent immédiatement à l'esprit:
la source
Probablement parce que vous voulez pouvoir enregistrer des méthodes qui ne sont visibles que par la classe sans interrompre l'encapsulation, cela facilite également l'utilisation de la classe dans une autre application sans interrompre la fonctionnalité de journalisation.
la source
Facilite la configuration des appenders par espace de noms ou classe.
la source
Si vous utilisez NLOG, vous pouvez spécifier le site d'appel dans la configuration, cela enregistrera le nom de la classe et la méthode où se trouvait l'instruction de journalisation.
<property name="CallSite" value="${callsite}" />
Vous pouvez ensuite utiliser une constante pour le nom de votre enregistreur ou le nom de l'assemblage.
Clause de non-responsabilité: je ne sais pas comment NLOG recueille ces informations, je suppose que cela serait une réflexion, vous devrez peut-être considérer les performances. Il y a quelques problèmes avec les méthodes Async si vous n'utilisez pas NLOG v4.4 ou version ultérieure.
la source