Comment append un préfixe de catégorie au message log4net?

J’aime append un préfixe de catégorie à tous les messages figurant dans les messages de journalisation existants. Cependant, il est fastidieux d’append ce préfixe à tous les messages de journalisation existants un à un. Existe-t-il un moyen d’append un atsortingbut au niveau de la classe pour que tous les messages de cette classe soient consignés dans une catégorie donnée?

Au lieu du chemin en ce moment comme ci-dessous,

Log.Info("[Ref] Level 1 Starts ..."); 

Je veux vraiment quelque chose comme ceci ou une autre façon de définir le log4net.ILog.

 [LoggingCategory("Ref")] public class MyClass { public void MyMethod() { Log.Info("Level 1 Starts ..."); } } 

Problème intéressant, tentative rude …

Log4NetLogger – adaptateur de journalisation

 public class Log4NetLogger { private readonly ILog _logger; private readonly ssortingng _category; public Log4NetLogger(Type type) { _logger = LogManager.GetLogger(type); _category = GetCategory(); } private ssortingng GetCategory() { var atsortingbutes = new StackFrame(2).GetMethod().DeclaringType.GetCustomAtsortingbutes(typeof(LoggingCategoryAtsortingbute), false); if (atsortingbutes.Length == 1) { var attr = (LoggingCategoryAtsortingbute)atsortingbutes[0]; return attr.Category; } return ssortingng.Empty; } public void Debug(ssortingng message) { if(_logger.IsDebugEnabled) _logger.Debug(ssortingng.Format("[{0}] {1}", _category, message)); } } 

LoggingCategoryAtsortingbute – applicable aux classes

 [AtsortingbuteUsage(AtsortingbuteTargets.Class)] public class LoggingCategoryAtsortingbute : Atsortingbute { private readonly ssortingng _category; public LoggingCategoryAtsortingbute(ssortingng category) { _category = category; } public ssortingng Category { get { return _category; } } } 

LogTester – une implémentation de test

 [LoggingCategory("LT")] public class LogTester { private static readonly Log4NetLogger Logger = new Log4NetLogger(typeof(LogTester)); public void Test() { Logger.Debug("This log message should have a prepended category"); } } 

Vous demandez comment faire cela via un atsortingbut. La suggestion de @ Jonathan semble bien fonctionner, mais vous pourrez peut-être obtenir un résultat assez bon en utilisant les capacités intégrées de log4net.

Si vous souhaitez regrouper des classes en “catégories”, vous pouvez extraire le consignateur en fonction du nom de la catégorie plutôt que du nom de la classe. Lorsque vous configurez votre format de sortie, vous pouvez utiliser le jeton de formatage du journal pour indiquer à log4net d’écrire le nom du journal dans la sortie.

En règle générale, on récupère le consignateur basé sur le nom de la classe comme ceci:

 public class Typical { private static readonly ILog logger = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); public void F() { logger.Info("this message will be tagged with the classname"); } } 

Il est parfaitement acceptable de récupérer des enregistreurs basés sur un nom arbitraire comme ceci:

 public class A { private static readonly ILog logger = LogManager.GetLogger("REF"); public void F() { logger.Info("this message will be tagged with REF"); } } public class B { private static readonly ILog logger = LogManager.GetLogger("REF"); public void F() { logger.Info("this message will be tagged with REF"); } } public class C { private static readonly ILog logger = LogManager.GetLogger("UMP"); public void F() { logger.Info("this message will be tagged with UMP"); } } 

Dans l’exemple précédent, les classes A et B sont considérées comme appartenant à la même “catégorie”. Elles ont donc récupéré le journal avec le même nom. La classe C est dans une catégorie différente, elle a donc récupéré l’enregistreur avec un nom différent.

Vous pouvez configurer vos enregistreurs (dans le fichier de configuration) avec votre propre hiérarchie “catégorie”:

 App App.DataAccess App.DataAccess.Create App.DataAccess.Read App.DataAccess.Update App.DataAccess.Delete App.UI App.UI.Login App.UI.Query App.UI.Options 

Vous pouvez également configurer le format de sortie de l’enregistreur pour n’enregistrer qu’une partie du nom de l’enregistreur qualifié complet. Quelque chose comme ça:

 %logger:2 

Pour obtenir les 2 dernières parties du nom complet. Par exemple, si le nom complet de votre classe est:

 NameSpaceA.NameSpaceB.NameSpaceC.Class 

Ensuite, le format ci-dessus affichera ceci sous le nom du logger:

 NameSpaceC.Class 

Je ne suis pas sûr à 100% de la syntaxe car je ne l’ai pas utilisée et je ne trouve pas un bon exemple pour le moment.

Un inconvénient de cette approche est que vous devez définir et mémoriser vos catégories et décider de la catégorie appropriée pour chaque classe (vous avez également ce problème si vous souhaitez décorer chaque classe avec un atsortingbut contenant sa catégorie). De même, si vous avez plusieurs classes dans la même catégorie, vous ne pouvez pas activer ou désactiver la journalisation ni changer le niveau de journalisation pour un sous-ensemble de ces classes.

Peut-être serait-il utile de consigner un seul espace de noms à partir de la hiérarchie des espaces de noms:

Peut-être que vous pouvez “classer” vos classes en fonction de leur espace de noms. Donc, vous voudrez peut-être enregistrer l’espace de noms parent immédiat d’une classe en tant que catégorie.

Par conséquent, pour le nom de classe complet ci-dessus, vous souhaiterez peut-être consigner “NameSpaceC” en tant que “catégorie” ou nom de journal.

Je ne sais pas si vous pouvez le faire immédiatement avec log4net, mais vous pourriez facilement écrire un PatternLayoutConverter pour obtenir le nom du logger et effacer le nom de la classe et les espaces de noms “de niveau supérieur”.

Voici un lien vers un exemple de PatternLayoutConverter personnalisé. Prend un paramètre que, dans mon cas, je voulais utiliser pour rechercher une valeur dans un dictionnaire. Dans ce cas, le paramètre peut représenter le décalage par rapport à la FIN du nom de logger pleinement qualifié (même interprétation que le paramètre associé à l’object de mise en page du nom du logger intégré de log4net), mais un code supplémentaire pourrait être ajouté pour consigner UNIQUEMENT l’espace de nom unique à cet emplacement. indice.

Propriété log4net personnalisée PatternLayoutConverter (avec index)

Encore une fois, étant donné ce nom de classe pleinement qualifié:

 NameSpaceA.NameSpaceB.NameSpaceC.Class 

Vous pouvez considérer que l’espace de nom du parent immédiat est la “catégorie”. Si vous avez défini une category PatternLayoutConverter personnalisée et qu’elle a pris un paramètre, votre configuration peut ressembler à ceci:

 %category 

Par défaut, il retournerait la sous-chaîne entre le dernier et l’avant-dernier '.' personnages. Étant donné un paramètre, il pourrait renvoyer n’importe quel espace de nom discret dans la chaîne.

PatternLayoutConverter peut ressembler à quelque chose comme ceci (non testé):

  class CategoryLookupPatternConverter : PatternLayoutConverter { protected override void Convert(System.IO.TextWriter writer, LoggingEvent loggingEvent) { //Assumes logger name is fully qualified classname. Need smarter code to handle //arbitrary logger names. ssortingng [] names = loggingEvent.LoggerName.Split('.'); ssortingng cat = names[names.Length - 1]; writer.Write(setting); } } 

Ou, en utilisant la propriété Option pour obtenir le nom de l’espace de nom Nth (par rapport à la fin):

  class CategoryLookupPatternConverter : PatternLayoutConverter { protected override void Convert(System.IO.TextWriter writer, LoggingEvent loggingEvent) { //Assumes logger name is fully qualified classname. Need smarter code to handle //arbitrary logger names. ssortingng [] names = loggingEvent.LoggerName.Split('.'); ssortingng cat; if (Option > 0 && Option < names.Length) { cat = names[names.Length - Option]; } else { string cat = names[names.Length - 1]; } writer.Write(setting); } } 

L'idée de @Jonathan est plutôt cool, mais cela ajoute un peu de code supplémentaire pour définir et maintenir un nouveau wrapper d'enregistreur (mais beaucoup de gens le font et le trouvent peu contraignant). Bien entendu, mes idées personnalisées PatternLayoutConverter nécessitent également un code personnalisé de votre part.

Un autre inconvénient est que GetCategory semble coûter assez cher pour chaque appel de journalisation.