C# · 12月 27, 2021

c# – 使用log4net为.NET项目记录ClassName和MethodName

我一直在寻找一种方法来记录类名和方法名称作为我的日志记录基础设施的一部分.显然,我希望在运行时使用起来很简单.我已经做了很多关于记录类名和方法名的阅读,但我已经运行了2个主题.

> log4net使用内部抛出异常来生成一个堆栈框架,如果您通常使用所有日志记录,那么它将变得昂贵.
>混乱.那里有很多文学.我已经尝试了一大堆,没有得到一些有用的东西.

如果你幽默我一秒钟,我想重置.

我在我的项目中创建了一个这样的类

public static class Log { private static Dictionary<Type,ILog> _loggers = new Dictionary<Type,ILog>(); private static bool _logInitialized = false; private static object _lock = new object(); public static string SerializeException(Exception e) { return SerializeException(e,string.Empty); } private static string SerializeException(Exception e,string exceptionMessage) { if (e == null) return string.Empty; exceptionMessage = string.Format( “{0}{1}{2}\n{3}”,exceptionMessage,(exceptionMessage == string.Empty) ? string.Empty : “\n\n”,e.Message,e.StackTrace); if (e.InnerException != null) exceptionMessage = SerializeException(e.InnerException,exceptionMessage); return exceptionMessage; } private static ILog getLogger(Type source) { lock (_lock) { if (_loggers.ContainsKey(source)) { return _loggers[source]; } ILog logger = log4net.LogManager.GetLogger(source); _loggers.Add(source,logger); return logger; } } public static void Debug(object source,object message) { Debug(source.GetType(),message); } public static void Debug(Type source,object message) { getLogger(source).Debug(message); } public static void Info(object source,object message) { Info(source.GetType(),message); } public static void Info(Type source,object message) { getLogger(source).Info(message); }

private static void initialize() { XmlConfigurator.Configure(); } public static void EnsureInitialized() { if (!_logInitialized) { initialize(); _logInitialized = true; } }}

(如果这段代码看起来很熟悉,那是因为它是从示例中借来的!)

无论如何,在我的项目中,我使用这样的行记录:

Log.Info(typeof(Program).Name,”System Start”);

那么这样的作品.最重要的是,我得到类名,但没有方法名.更重要的是,我用这个“typeof”垃圾污染了我的代码.如果我在文件之间复制和粘贴一段代码等,日志框架就会撒谎!

我尝试使用PatternLayout(%C {1}.{M}),但没有工作(所做的一切都是将“Log.Info”写入日志 – 因为一切都是通过Log.X静态方法进行路由!).此外,这应该是缓慢的.

那么最好的方法是什么,给我的设置和我想要简单快捷的愿望?

提前了解任何帮助.

解决方法 log4net(和NLog)都公开了一种记录方法,可以“包装”他们的记录器,并且仍然获得呼叫站点信息的正确性.本质上,log4net(或NLog)记录器需要被告知形成记录代码和应用程​​序代码之间的“边界”的类型.我认为他们称之为“记录器类型”或类似的东西.当库获取呼叫站点信息时,他们向上浏览调用堆栈,直到MethodBase.DeclaringType等于(或者可以AssignableFrom)“记录器类型”.下一个堆栈帧将是应用程序调用代码.

以下是一个例子,说明如何通过NLog从包装器中记录(log4net将是类似的 – 在ILogger(而不是ILog)界面的log4net文档中查看:

LogEventInfo logEvent = new LogEventInfo(level,_logger.Name,null,”{0}”,new object[] { message },exception); _logger.Log(declaringType,logEvent);

其中declaringType是一个成员变量,设置如下:

private readonly static Type declaringType = typeof(AbstractLogger);

“AbstractLogger”是您的记录器包装器的类型.在你的情况下,它可能看起来像这样:

private readonly static Type declaringType = typeof(Log);

如果NLog需要获取调用站点信息(由于布局中的调用站点操作符),它将向上导航到堆栈,直到当前帧的MethodBase.DeclaringType等于(或AssignableFrom)declaringType为止.堆栈中的下一个帧将是实际的调用站点.

这是一些可以使用“包装”log4net记录器进行日志记录的代码.它使用log4net ILogger接口,并传递“包装”记录器的类型以保留呼叫站点信息.您不必使用此方法填写事件类/结构:

_logger.Log(declaringType,level,message,exception);

再次,“declaringType”是包装器的类型. _logger是log4net记录器,Level是log4net.LogLevel值,message是消息,异常是异常(如果有的话,否则为null).

就像用Typeof(无论如何)污染你的呼叫站点一样,如果你想要使用一个静态的“Log”对象,我认为你会坚持的.或者,在“日志”对象的日志记录方法的内部,您可以在本文中获得类似接受的答案的调用方法

How can I find the method that called the current method?

该链接显示如何获取紧接在前的呼叫者.如果您需要获取称为日志记录功能的方法,但是您的工作正在进行更深层次的层次化,那么您将需要将堆栈数量增加一些,而不仅仅是一个帧.

将所有这一切都合并在一起,您将会编写您的Debug方法(再次,这是Nlog,因为这是我在我面前):

public static void Debug(object message){ MethodBase mb = GetCallingMethod(); Type t = mb.DeclaringType; LogEventInfo logEvent = new LogEventInfo(LogLevel.Debug,t.Name,new object [] message,null); ILogger logger = getLogger(t) As ILogger; logger.Log(declaringType,logEvent)}

请注意,您可能不会在StackOverflow上找到很多人,建议您编写一个这样的日志包装函数(显式地获取日志调用的调用方法).我不能说我会推荐它,但它或多或少地回答你问的问题.如果要使用静态“Log”对象,则必须在每个日志调用站点显式传递Type(以获取正确的类记录器),否则必须在日志记录调用之内添加代码才能导航堆叠并为自己找出信息.我不认为这两个选项之一是特别有吸引力的.

现在,所有这一切,您可能会考虑直接使用log4net或NLog,而不是添加这种复杂的(而不是可靠的)代码来获取呼叫站点信息.正如马修指出的那样,NLog提供了一种简单的方式来获取当前课程的记录器.要使用log4net获取当前类的记录器,您可以在每个类中执行此操作:

private static readonly log4net.ILog log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

与NLog的这种方式:

private static readonly NLog.logger log = NLog.LogManager.GetCurrentClassLogger();

这是一个很常见的用法.

如果您不想依赖于特定的日志记录实现,可以使用可用的日志抽象之一,例如Common.Logging (NET)或Simple Logging Facade (SLF).

即使您不使用这些抽象之一,请下载Common.Logging的源代码,并查看log4net的抽象.它将显示如何包装log4net记录器,以便保留呼叫站点信息(并且可用于布局运算符).