【问题标题】:Implementation and usage of logger wrapper for log4netlog4net logger wrapper的实现和使用
【发布时间】:2015-11-27 07:43:09
【问题描述】:

此问题与Steven 的答案-here 有关。他提出了一个非常好的记录器包装器。我将他的代码粘贴在下面:

public interface ILogger
{
    void Log(LogEntry entry);
}

public static class LoggerExtensions
{
    public static void Log(this ILogger logger, string message)
    {
        logger.Log(new LogEntry(LoggingEventType.Information,
            message, null));
    }

    public static void Log(this ILogger logger, Exception exception)
    {
        logger.Log(new LogEntry(LoggingEventType.Error, 
            exception.Message, exception));
    }

    // More methods here.
}

那么,我的问题是创建代理 log4net 的实现的正确方法是什么?我应该只添加另一个带有类型参数的日志扩展方法,然后在里面创建一个开关吗?在LoggingEventType的情况下使用不同的log4net方法?

第二个问题,在后面的代码中使用它的最佳方式是什么?

因为他写道:

(...) 您可以轻松创建 ILogger 实现 (...) 并配置 您的 DI 容器将其注入到具有 ILogger 的类中 构造函数。

这是否意味着每个将记录某事的类(基本上是每个类)都应该在其构造函数中包含ILogger

【问题讨论】:

  • “这是否意味着每个类都会记录某事(所以基本上每个)”。如果每个班级都使用记录器,那么您是认真的logging way too much
  • @Steven 与该答案的赞成票和 cmets 相反,我完全不同意:你永远不能记录太多。特别是对于 Web 服务和 Windows 服务(即后端),面对 UI 的用户几乎没有错误报告,日志记录对于解决问题非常宝贵。当然,您可以说“此代码是 SOLID”,但如果该代码不能通过简单地阅读日志来分析应用程序在崩溃时所做的事情来进行“事后调试”,那么 SOLID 会给您带来什么?当然,有单元测试可以防止出现问题,但代码永远不会完美无缺。
  • @CodeCaster:我在回答中提出的方法实际上是 AOP 的一种形式。如果您在系统中定义了正确的抽象(这就是我的答案),您会发现应用一些为您进行日志记录的装饰器很容易。将此与 Clean Code 混合使用并使用异常快速失败,您会发现像 logger.Log("now we're in this if-branch")logger.Log("customer is null") 这样的调用实际上变得非常罕见。
  • 这是一个设计的东西。一组受控的抽象和降低的圈复杂度。所有运行时数据要么是输入参数,要么是从存储(数据库、内存等)中提取的某种形式的有状态数据,所有这些都可以使用方面/装饰器进行记录。当数据在对象图中移动时,拥有数据的详细信息和方法调用的顺序通常足以弄清楚发生了什么,而不会一遍又一遍地用相同的重复代码行污染整个代码@987654329 @。就像我说的,这是一种设计。
  • @TimLaax:使该记录器成为单例/环境上下文,并不会改变它是依赖项的事实;但它确实隐藏了依赖关系。这使得测试、模拟、替换、装饰和拦截变得困难,向任何消费者隐藏这种依赖存在的事实,并且使工具(例如您的 DI 库)无法为您分析对象图。在我的书中,将记录器设为单例绝对不是更好也不是更清洁。使其成为单身人士是对症状的治疗。您在太多类中注入记录器:停止这样做。

标签: c# .net logging log4net nlog


【解决方案1】:

那么,我的问题是创建代理到 log4net 的实现的正确方法是什么?

你应该创建类似的东西:

public class Log4netAdapter : ILogger
{
    private readonly log4net.ILog m_Adaptee;

    public Log4netAdapter(log4net.ILog adaptee)
    {
        m_Adaptee = adaptee;
    }

    public void Log(LogEntry entry)
    {
        //Here invoke m_Adaptee
        if(entry.Severity == LoggingEventType.Debug)
            m_Adaptee.Debug(entry.Message, entry.Exception);
        else if(entry.Severity == LoggingEventType.Information)
            m_Adaptee.Info(entry.Message, entry.Exception);
        else if(entry.Severity == LoggingEventType.Warning)
            m_Adaptee.Warn(entry.Message, entry.Exception);
        else if(entry.Severity == LoggingEventType.Error)
            m_Adaptee.Error(entry.Message, entry.Exception);
        else
            m_Adaptee.Fatal(entry.Message, entry.Exception);
    }
}

这是否意味着每个将记录某事的类(基本上是每个类)都应该在其构造函数中包含 ILogger?

据我了解,史蒂文斯的回答是:是的,你应该这样做。

稍后在代码中使用它的最佳方式是什么?

如果您使用的是 DI 容器,则只需使用 DI 容器将 ILogger 映射到 Log4netAdapter。您还需要注册log4net.ILog,或者只是将 log4net 记录器的实例提供给 DI 容器,以将其注入 Log4netAdapter 构造函数。

如果你不使用 DI 容器,即使用Pure DI,那么你可以这样做:

ILog log = log4net.LogManager.GetLogger("MyClass");

ILogger logging_adapter = new Log4netAdapter(log);

var myobject = new MyClass(other_dependencies_here, logging_adapter);

【讨论】:

  • 我没有什么要补充的(+1 对我来说),除非就像我在评论中所说的那样,如果您将ILogger 注入系统中的几乎所有组件,您要么是 @987654322 @.
  • @TimLaax:这很难说。我可以将您指向我的文章(hereherehere)关于应用允许您轻松应用横切关注点(例如日志记录)的设计,您可以查看此 example project获得一个小的工作示例,但我不知道专门显示日志记录的现成示例项目。
  • @Steven,为什么适配器会在其构造函数中接受ILog,而不是直接实例化 Log4Net 对象?是否允许测试适配器?
  • 没关系,我找到了@Steven 编写的示例,可以在这里找到:stackoverflow.com/a/25113659/3311255
  • @Krptodr 请在 SO 上发布一个新问题,并附上完整的示例和详细信息,并用simple-injector 标记它。我会在宿醉消失后看看:)
猜你喜欢
  • 2017-01-22
  • 1970-01-01
  • 1970-01-01
  • 2011-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多