![]() |
大方的钥匙扣 · 【单细胞测序】RNA ...· 6 月前 · |
![]() |
打篮球的手术刀 · 专业磁力种子下载工具 ...· 9 月前 · |
![]() |
从容的大脸猫 · oracle修改字段为null-掘金· 10 月前 · |
![]() |
打酱油的木瓜 · jquery怎么根据id获取元素值-CSDN博客· 1 年前 · |
![]() |
强健的领带 · QT:QLineEdit ...· 1 年前 · |
我一直在寻找一个用于.net (c#)的日志框架,在阅读了stackoverflow上的一些问答线程后,我决定尝试一下log4net。我看到人们一次又一次地提到他们使用log4net的包装器类,我想知道那会是什么样子。
我将代码拆分到不同的项目中(data access/business/webservice/..)。log4net包装类应该是什么样子的?包装器类是否需要包含在所有项目中?我应该把它作为一个单独的项目一起构建吗?
包装器应该是单例类吗?
我的理解是,log4net的包装类应该是一个静态类,它负责从app.config/web.config或通过代码(例如与NUnit集成)初始化日志记录对象。
log4net包装器的一个可能用途是一个类,它通过反射获取调用类和方法,以了解日志记录发生在哪里。至少我经常用到这个。
本质上,您创建了一个接口,然后创建了该接口的一个具体实现,该接口直接包装了Log4net的类和方法。可以通过创建更具体的类来包装其他日志记录系统,这些类包装了这些系统的其他类和方法。最后,使用工厂根据配置设置或代码更改行创建包装器的实例。(注意:使用 StructureMap 这样的 Inversion of Control 容器可以变得更加灵活和复杂。)
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));
前面的例子被认为是更易维护的。
您不会希望创建一个单例来处理所有日志记录,因为Log4Net会为调用类型记录日志;让每种类型使用自己的记录器而不是只在日志文件中看到一个报告所有消息的类型要干净得多,也很有用。
因为您的实现应该具有相当高的可重用性(组织中的其他项目),所以您可以使其成为自己的程序集,或者最好将其包含在您自己的个人/组织的框架/实用程序集中。不要在每个业务/数据/UI程序集中单独地重新声明类,这是不可维护的。
您计划从为log4net编写包装器中获得什么好处?我建议先熟悉一下log4net类,然后再围绕它们编写包装器。cfeduke在如何编写上述包装器方面的回答是正确的,但除非您需要在他的示例中添加实际功能,否则包装器只会成功地减慢日志记录过程,并为未来的维护者增加复杂性。当.Net中可用的重构工具使这样的更改变得非常容易时,这一点尤其正确。
假设你使用的是像
cfeduke's answer above
这样的东西,你也可以像这样给你的
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));
它实际上等同于第一个替代方案(即使用
MethodBase.GetCurrentMethod().DeclaringType
的方案),只是稍微简单一点。
Alconja,我喜欢你使用stacktrace跳回到调用方法的想法。我正在考虑进一步封装调用,不仅是为了检索记录器对象,而且是为了执行实际的日志记录。我想要的是一个处理日志记录的静态类,通过从所使用的特定实现中抽象出来。也就是说。
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);
}
有没有人看到这种方法有问题?
我知道这个答案有点晚了,但它可能会在未来对某些人有所帮助。
听起来您需要XQuiSoft日志为您提供的编程应用编程接口。你不必为XQuiSoft指定你想要的记录器。它就像这样简单:
Log.Write(Level.Verbose,“来源”,“类别”,“您的消息在这里”);
然后,通过配置,您可以按来源、类别、级别或任何其他自定义过滤器将消息定向到不同的位置(文件、电子邮件等)。
有关介绍,请参阅 this article 。
我已经成功地将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);
_log.Debug(message, ex);
public void Info(object message, Exception ex = null)
if (_log.IsInfoEnabled)
if (ex == null)
_log.Info(message);
_log.Info(message, ex);
public void Warn(object message, Exception ex = null)
if (_log.IsWarnEnabled)
if (ex == null)
_log.Warn(message);
_log.Warn(message, ex);
public void Error(object message, Exception ex = null)
if (_log.IsErrorEnabled)
if (ex == null)
_log.Error(message);
_log.Error(message, ex);
public void Fatal(object message, Exception ex = null)
if (_log.IsFatalEnabled)
if (ex == null)
_log.Fatal(message);
_log.Fatal(message, ex);
}
别忘了把它添加到接口项目的
AssemblyInfo.cs
中(我花了好几个小时才找到它)
[assembly: log4net.Config.XmlConfigurator(Watch = true, ConfigFile = "log4net.config")]
并将您的log4net配置xml放在
log4net.config
文件中,将其设置为
Content
、
Copy Always
有像 Prism Library for WPF 这样的框架可以促进您选择的日志记录框架使用 facade 。
这是一个使用 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: