Log4Net Wrapper类是什么样的?

时间:2020-03-06 15:04:14  来源:igfitidea点击:

我一直在寻找.net(c#)的日志记录框架,并在阅读了stackoverflow上的一些问题/答案线程后决定尝试使用log4net。我看到人们一遍又一遍地提到他们使用log4net的包装器类,我不知道那会是什么样。

我将代码分为不同的项目(数据访问/业务/网络服务/ ..)。
log4net包装器类的外观如何?包装器类是否需要包含在所有项目中?我应该一起将其构建为一个单独的项目吗?

包装器应该是单例类吗?

解决方案

我的理解是log4net的包装器类将是一个静态类,它负责通过app.config / web.config或者通过代码(例如与NUnit集成)初始化日志记录对象。

log4net包装器的可能用途是通过反射获取调用类和方法的类,以了解日志记录条目发生的位置。至少我经常使用这个。

本质上,我们先创建一个接口,然后创建该接口的具体实现,该实现直接包装Log4net的类和方法。可以通过创建更具体的类来包装其他日志系统,这些类将包装那些系统的其他类和方法。最后,使用工厂根据配置设置或者代码更改行创建包装器的实例。 (注意:使用Control Inversion容器(如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));

前一个示例被认为更易于维护。

我们不想创建一个Singleton来处理所有日志记录,因为Log4Net记录了调用类型。使每种类型使用其自己的记录器,而不是仅在日志文件中看到报告所有消息的单个类型,将使它更加清洁和有用。

因为实现应该是相当可重用的(组织中的其他项目),所以我们可以使其实现为自己的程序集,或者理想情况下可以将其包含在我们自己的个人/组织的框架/实用程序程序集中。不要在每个业务/数据/ UI程序集中分别声明这些类,因为这是无法维护的。

我们计划摆脱编写log4net的包装程序有什么好处。我建议先对log4net类感到满意,然后再为它们编写包装。 cfeduke在有关如何编写所述包装程序的答案中是正确的,但是除非我们需要在他的示例中添加实际功能,否则包装程序只会成功减慢日志记录过程并增加将来维护人员的复杂性。当.Net中可用的重构工具使此类更改超级容易时,尤其如此。

假设我们要使用类似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));

这实际上等效于第一种选择(即使用MethodBase.GetCurrentMethod()。DeclaringType的选择),只是简单一点。

Alconja,我喜欢我们使用stacktrace跳回到调用方法的想法。我正在考虑进一步封装调用,不仅要检索记录器对象,还要执行实际执行的日志记录。我想要的是一个静态类,该类通过从所使用的特定实现中进行抽象来处理日志记录。 IE。

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 Logging为我们提供的程序化API。我们不必使用XQuiSoft指定所需的记录器。就这么简单:

Log.Write(Level.Verbose,"源","类别","消息在这里");

然后,通过配置,我们可以按来源,类别,级别或者任何其他自定义过滤器将消息定向到不同的位置(文件,电子邮件等)。

有关介绍,请参见本文。