使用log4net包含日志记录的最佳做法是什么?

时间:2009-12-14 17:00:33

标签: .net wcf log4net

我被告知使用log4net将“日志记录”添加到我的代码中,问题是没有人可以及时访问并查看需要使用日志记录解决的实际问题。

因此,对于要记录什么以便获得合理的成本/利益折衷,是否有一系列指导?

因此:

  

应添加哪种日志记录   到一个有用的应用程序   以后?

(代码使用了很多 WCF ,一方是Winforms,另一方是通常在同一台机器上运行的“服务器”)

-

我已经将AJM的答案排除在做有用的博客文章之前,并指出了很多评论,但如果有人提出了一套很好的“rules of thumb”,我很可能会改变预期的答案。

9 个答案:

答案 0 :(得分:8)

要记住的一件事是,虽然您的配置将处理不同级别的日志记录,但您可能会在日志调用中造成沉重的开销。例如:

// some kind of loop
// do some operations
Logger.LogDebug(myObject.GetXmlRepresentation());
// end loop

如果您有一个记录器侦听DEBUG日志,这显然只会记录该对象,但是无论您的日志记录级别如何,构建XML对象的调用都会运行,并且可能导致一些相当大的速度下降。


正确的解决方案是:

// some kind of loop
// do some operations
if (Logger.IsDebug)
{
    Logger.LogDebug(myObject.GetXmlRepresentation());
}
// end loop

答案 1 :(得分:6)

我发现这篇文章非常有用:http://blog.codinghorror.com/the-problem-with-logging/

特别是我认为极简主义的方法确实是要走的路。在过去,我试图记录太多,但这会使代码膨胀

另外认为越多的日志条目越好是错误的,因为它会使日志本身膨胀。我现在将loggings的主要好处看作是提供“立足点”或概述正在发生的事情。如果特定区域需要更多细节,那么它是默认位置应该更少更好

答案 2 :(得分:5)

我最喜欢这类问题的信息来源是Release It - 一本来自务实人士的书。强烈推荐。

关于您的问题,他们的基本观点是,日志记录应该针对运营级别所需的内容。运营人员最关心的是网站可能出现故障的特殊情况(即连接池已满,与服务器的连接已关闭等)确保消息不言自明,并且非常清楚问题是什么,如果适用,修复是什么。写下供人食用的信息。

我在功能进入/退出样式日志中看到了一点点。顶级捕获异常的堆栈跟踪很有用,可以记录可能发生系统崩溃的区域(即完整连接池),以及记录系统崩溃之前的区域。

答案 3 :(得分:2)

通常,对于日志记录,我按以下顺序添加日志记录:

  1. 功能输入/退出
  2. 函数内部的主要逻辑步骤
  3. 所有中间计算的详细记录
  4. 显然我很少到达最后一个,如果你为log4net滚动你自己的包装并使用处理模式,并且可能有一点反射魔法,那么第一个是微不足道的。

    第二个通常在验收/整合和回归测试期间完成,因为主要逻辑流程和问题区域被识别。在此阶段添加日志记录也相当少,因为您通常知道在调试和测试时需要添加日志记录。

    第三种通常(对我而言)只在经历回归的代码部分中完成,或者特别重要。

    我已经为log4net实现了一个基本的包装器对象,它为我提供了直接的日志记录功能以及一个上下文对象,可以与IDisposable一起使用,将“进入/退出”逻辑包装在一个漂亮的方便包中。

答案 4 :(得分:1)

log4net的一大优点是您可以将事件记录到不同的类别。默认值为Debug,Info,Warning和Error。我喜欢这些意思

Debug - 非常详细,包含大量调试信息。例如,SQL查询 信息 - 有用的信息很有用。
警告 - 没什么致命的,但操作员应该意识到这个问题 错误 - 应用程序现在不稳定,日志包含诊断信息,例如异常消息和堆栈跟踪。

在代码中使用这些,例如

_log.Info("Updating object.");

会向任何感兴趣的听众写一个INFO级别的消息。

然后,您可以在配置中连接侦听器以使用日志消息执行操作。这是我正在使用的一个:

<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline" />
  </layout>
</appender>
<appender name="FileAppender" type="log4net.Appender.FileAppender">
  <file value="c:\temp\servicelog.txt" />
  <appendToFile value="true" />
  <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline%exception" />
  </layout>
</appender>
<root>
  <level value="ERROR" />
  <appender-ref ref="ConsoleAppender" />
</root>
<logger name="Warehouse.Core">
  <level value="INFO" />
  <appender-ref ref="FileAppender" />
</logger>
</log4net>

这表示:所有ERROR消息都发送到控制台,所有INFO消息都从记录器Warehouse.Core发送到给定文件。

由于这些类别与侦听器的连接是在配置中完成的,因此您可以在部署后更改日志记录。如果没有人正在收听,那么记录几乎没有性能损失。


关于日志记录的成本与收益,在过多的日志记录(没有人会使用的大型日志)和不够的(单行称“失败”)之间肯定会有一个最佳点。

我的策略是记录INFO可能失败的东西:外部依赖(应用程序启动,服务调用,SQL连接),以及DEBUG更复杂的内容代码(业务逻辑中的诊断消息,单个SQL调用,某些方法)调用)。

不寻常的情况,其中(例如)默认值通常不会转到WARN,异常转到ERROR或FATAL。

另外:请记住,WCF有一个最优秀的服务跟踪查看器,允许您“深入”到单个数据包以及它们如何被堆栈的两端处理。在没有代码更改的情况下,配置也可以使用它。因此,我通常只会对WCF服务调用和响应进行非常简短的日志记录。

答案 5 :(得分:1)

  

应该在以后有用的应用程序中添加哪种日志记录?

如果从自己的异常类中抛出异常,甚至更好,所有异常类都派生自基类,在(基本)异常构造函数中添加ERROR级别日志记录;节省你必须记住每次捕获/抛出。如果你有一个很大的代码库,这很有用。

对于CLR或第三方异常,请记录Exception.ToString(),而不仅仅是消息,否则您将错过完整的堆栈跟踪(假设程序员不会吞下异常或重新抛出sans内部异常)

专注于您知道或怀疑您会遇到问题的区域的DEBUG详细信息(只需询问质量保证或技术支持在哪里查看; - )

如果您遵循robustness principle,那么当您忽略或更改输入或预期行为时,您可能需要INFO或WARN日志记录。如果您的WCF服务开始接收意外(但可解析)的输入,这可能很有用。

为了确保您的应用程序运行良好,请不要在默认情况下启用DEBUG级别日志记录的情况下发送/安装它,ERROR或WARN可能是最佳选择。

我不同意接受的答案的最后部分,因为log4net具有很棒的过滤功能;在你的程序员理解日志记录成本(根据CK的答案)并且他们(以及QA和技术支持)知道过滤器的情况下,并且在DEBUG配置所有内容是一个坏主意。如果您在DEBUG级别记录一个大对象图,xml文档,db结果等等,请将其包装在一些代码中以减少开销:

if (log.IsDebugEnabled)
{
    log.DebugFormat("Loaded in {0} ms {1}", timer.ElapsedMilliseconds, dataSet.GetXml());
}

我建议您遵循recommended静态记录器每类方法,因为它必须使记录更有用,当您必须实际使用它并使用过滤器缩小问题时,例如: LoggerMatchFilter。

如果您遵循这种方法并且愿意采用(相当小的)性能命中,这里有一种方法,它使用堆栈跟踪为任何类创建ILog对象,并确保配置文件连接以监视更改:

public static class LogFactory
{
    /// <summary>
    /// Create log whose name is the calling methods class name.
    /// </summary>
    /// <remarks>
    /// <para>
    /// Configures the log repository if it hasn't been configured before.
    /// </para>
    /// <para>
    /// Creates a debug log message right after getting the logger, this follows
    /// the log4net recommendation to log first message as early as possible.
    /// </para>
    /// </remarks>
    /// <returns>Log ready for work.</returns>
    public static log4net.ILog Create()
    {
        var method = new StackTrace().GetFrame(1).GetMethod();
        var log = log4net.LogManager.GetLogger(method.DeclaringType);

        if (log4net.LogManager.GetRepository().Configured == false)
        {
            try
            {
                new FileIOPermission(FileIOPermissionAccess.Read,
                    AppDomain.CurrentDomain.SetupInformation.ConfigurationFile)
                    .Demand();

                var configFile = new FileInfo(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
                log4net.Config.XmlConfigurator.ConfigureAndWatch(configFile);
                log.DebugFormat("Log4net configured and watching {0}", configFile.FullName);
            }
            catch (System.Security.SecurityException e)
            {
                log.DebugFormat("Unable to watch config file due to security permissions. {0}", e.ToString());
            }
        }

        log.DebugFormat("Logging {0}", log.Logger.Name);

        return log;
    }
}

答案 6 :(得分:1)

记录并不是一项容易的任务,但我的经验是,所有日志都应该可以为负责任的各方搜索。错误的有用目标是直接发送电子邮件(在某些情况下是短信)。但最终所有日志记录数据都应该可以在具有可选用户界面的数据库中进行搜索。

当收到特定帐户的电子邮件时,可以直接将其处理并放入数据库。下面有som类别和处理规则:

  • 严重错误:应立即使用电子邮件/短信
  • 未来问题:每日/每周电子邮件
  • 调试信息==&gt;每日/每周电子邮件,附上自上次以来产生的调试信息量的通知。

调试的内容可以以不同的方式写入数据库,但我们需要更好的性能。在“生产模式”下不应将大量数据写入数据库。这应该在每日/每周的基础上完成。最好的方法是生成本地文件(例如XML或纯文本),并在维护时间(晚上)将此文件放入数据库。应该可以启动/停止调试会话,并且只在调试会话完成时将调试信息写入数据库。

调试组件可以实现为WCF和log2net,并可以直接访问数据库和/或定期放入数据库的本地文件存储。

有一件事是清楚的......所有错误/异常都应记录在某处。没有什么比失去的错误消息更令人恼火了:))

快乐的调试!

答案 7 :(得分:0)

将事件记录到事件查看器。

明智地使用INFO DEBUG WARNING和ERROR。

开发时显示生产中的所有内容(在服务器端使用控制台 - 见下文),仅记录错误(可配置)

我在每个类的乞讨中创建一个记录器,给它typeof(myClass)但是这是可选的......

我在DEV中作为控制台应用程序托管WCF - 所以在控制台中也很容易看到服务器日志,但是一旦它成为服务,你就必须使用事件查看器....

啊 - 如果你将它与WCF“调用时间”(从客户端调用到服务器)的速度非常快,那么它不会真正影响你的时间,除非你有像疯了一样的日志(例如nHibernate) )

答案 8 :(得分:0)

对于WCF,可能有更好的方法来做这件事;对于WinForms,您可以考虑查看PostSharp。这将允许您记录方法调用而不会使代码混乱。在幕后你仍然会使用优秀的log4net。

警告:我自己没有用过它;我在代码营看到了一个非常令人印象深刻的演示文稿。