Log4Net Wrapper类是什么样的?
我一直在寻找一个.net(c#)的日志框架,并决定给log4net去读一些问题/回答线程在这里stackoverflow。 我看到人们一遍又一遍地提到他们使用一个包装类的log4net,我不知道是什么样子。
我有我的代码分裂成不同的项目(数据访问/业务/ Web服务/ ..)。 log4net包装类如何看起来像? 包装类是否需要包含在所有的项目中? 我应该把它作为一个单独的项目吗?
包装应该是单身人士吗?
从本质上讲,你创build一个接口,然后是该接口的具体实现,它直接包装了Log4net的类和方法。 额外的日志logging系统可以通过创build更多具体的类来包装,这些类包含这些系统的其他类和方法。 最后,使用工厂根据configuration设置或代码行更改来创build包装的实例。 (注意:你可以使用一个像StructureMap这样的控制反转容器来获得更多的灵活性和复杂性。)
public interface ILogger { void Debug(object message); bool IsDebugEnabled { get; } // continue for all methods like Error, Fatal ... } public class Log4NetWrapper : ILogger { private readonly log4net.ILog _logger; public Log4NetWrapper(Type type) { _logger = log4net.LogManager.GetLogger(type); } public void Debug(object message) { _logger.Debug(message); } public bool IsDebugEnabled { get { return _logger.IsDebugEnabled; } } // complete ILogger interface implementation } public static class LogManager { public static ILogger GetLogger(Type type) { // if configuration file says log4net... return new Log4NetWrapper(type); // if it says Joe's Logger... // return new JoesLoggerWrapper(type); } }
以及在您的类中使用此代码的示例(声明为静态只读字段):
private static readonly ILogger _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
您可以使用以下方法获得相同的更多性能友好效果:
private static readonly ILogger _logger = LogManager.GetLogger(typeof(YourTypeName));
前面的例子被认为是更可维护的。
你不想创build一个Singleton来处理所有的日志logging,因为Log4Netlogging了调用types; 它更清洁和有用,让每个types使用自己的logging器,而不是只看到日志文件中报告所有消息的单一types。
因为您的实现应该是相当可重用的(组织中的其他项目),您可以将其组装为自己的组件,或者将其包含在自己的个人/组织框架/实用程序组合中。 不要在每个业务/数据/ UI组件中分别重新声明类,这是不可维护的。
假设你正在使用类似cfeduke的答案 ,你也可以像这样在你的LogManager
添加一个重载:
public static ILogger GetLogger() { var stack = new StackTrace(); var frame = stack.GetFrame(1); return new Log4NetWrapper(frame.GetMethod().DeclaringType); }
这样在你的代码中,你现在可以使用:
private static readonly ILogger _logger = LogManager.GetLogger();
而不是以下任何一个:
private static readonly ILogger _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILogger _logger = LogManager.GetLogger(typeof(YourTypeName));
这与第一个替代scheme(即使用MethodBase.GetCurrentMethod().DeclaringType
)的方法实际上是等效的,只是简单一点。
你计划为log4net编写一个包装程序有什么好处。 我build议先写log4net类,然后再写一个包装器。 cfeduke是在他如何编写封装的答案是正确的,但除非你需要添加实际的function,他的例子包装将只能成功地减缓日志logging过程,并增加未来维护者的复杂性。 当.Net中的重构工具使得这样的改变变得非常容易时尤其如此。
我已经成功地将log4net依赖项隔离到一个单独的项目中。 如果你打算这样做,这是我的包装类看起来像:
using System; namespace Framework.Logging { public class Logger { private readonly log4net.ILog _log; public Logger() { _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); } public Logger(string name) { _log = log4net.LogManager.GetLogger(name); } public Logger(Type type) { _log = log4net.LogManager.GetLogger(type); } public void Debug(object message, Exception ex = null) { if (_log.IsDebugEnabled) { if (ex == null) { _log.Debug(message); } else { _log.Debug(message, ex); } } } public void Info(object message, Exception ex = null) { if (_log.IsInfoEnabled) { if (ex == null) { _log.Info(message); } else { _log.Info(message, ex); } } } public void Warn(object message, Exception ex = null) { if (_log.IsWarnEnabled) { if (ex == null) { _log.Warn(message); } else { _log.Warn(message, ex); } } } public void Error(object message, Exception ex = null) { if (_log.IsErrorEnabled) { if (ex == null) { _log.Error(message); } else { _log.Error(message, ex); } } } public void Fatal(object message, Exception ex = null) { if (_log.IsFatalEnabled) { if (ex == null) { _log.Fatal(message); } else { _log.Fatal(message, ex); } } } } }
不要忘记在接口项目的AssemblyInfo.cs
中添加这个(花了好几个小时才find这个)
[assembly: log4net.Config.XmlConfigurator(Watch = true, ConfigFile = "log4net.config")]
并把你的log4netconfigurationXML在log4net.config
文件中,将其设置为Content
, Copy Always
我的理解是,log4net的包装类将是一个静态类,它负责从app.config / web.config或代码(例如与NUnit的集成)初始化日志logging对象。
log4net包装器的一个可能的用途可能是一个类,通过reflection获得调用类和方法,以了解你的日志logging发生的地方。 至less我经常使用这个。
Alconja,我喜欢使用堆栈跟踪回到调用方法的想法。 我正在考虑进一步封装调用,不只是检索logging器对象,而是执行实际执行日志logging。 我想要的是一个处理日志logging的静态类,通过从所使用的具体实现中抽象出来。 即
LoggingService.LogError("my error message");
这样我只需要改变静态类的内部,如果我以后决定用户另一个日志系统。
所以我用你的想法来使用堆栈跟踪来获取调用对象:
public static class LoggingService { private static ILog GetLogger() { var stack = new StackTrace(); var frame = stack.GetFrame(2); return log4net.LogManager.GetLogger(frame.GetMethod().DeclaringType); } public static void LogError(string message) { ILog logger = GetLogger(); if (logger.IsErrorEnabled) logger.Error(message); } ... }
有人看到这个方法有问题吗?
有像WPF的Prism Library这样的框架可以促进你select的日志框架的使用。
这是一个使用log4net的例子:
using System; using log4net; using log4net.Core; using Prism.Logging; public class Log4NetLoggerFacade : ILoggerFacade { private static readonly ILog Log4NetLog = LogManager.GetLogger(typeof (Log4NetLoggerFacade)); public void Log(string message, Category category, Priority priority) { switch (category) { case Category.Debug: Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Debug, message, null); break; case Category.Exception: Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Error, message, null); break; case Category.Info: Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Info, message, null); break; case Category.Warn: Log4NetLog.Logger.Log(typeof(Log4NetLoggerFacade), Level.Warn, message, null); break; default: throw new ArgumentOutOfRangeException(nameof(category), category, null); } } }
请注意,通过指定callerStackBoundaryDeclaringType
您仍然可以获得发出日志logging请求的调用方的类名称。 您只需在转化模式中join%C %M
:
<layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date [%thread] %-5level %C.%M - %message%newline" /> </layout>
然而,正如文件警告,产生调用者类信息是缓慢的,因此它必须明智地使用。
我知道这个答案已经很晚了,但是对未来可能会有帮助。
这听起来像你想要XQuiSoft日志logging给你一个程序化的API。 您不必指定您想要使用XQuiSoft的logging器。 这是如此简单:
Log.Write(Level.Verbose,“source”,“category”,“your message here”);
然后,通过configuration,您可以通过源,类别,级别或任何其他自定义filter将消息定向到不同的位置(文件,电子邮件等)。
请参阅这篇文章的介绍。