如何在app start上禁用创建空日志文件?

时间:2010-03-28 15:26:44

标签: c# winforms log4net log4net-configuration

我已经在我的应用程序中成功配置了log4net,但有一件事对我来说有点烦人。

即使没有错误发生,我的应用程序启动后也会创建(空)日志文件。我想在出现一些错误后才创建日志文件。

7 个答案:

答案 0 :(得分:14)

我实际上在这个帖子中找到了一种方法:

http://www.l4ndash.com/Log4NetMailArchive/tabid/70/forumid/1/postid/18271/view/topic/Default.aspx

我测试了第一种方法,但它确实有效。为了防止链接不再好,我将在这里重现代码。基本上,作者声称有两种方法可以做到这一点。

第一种方式:

如果该记录器的适当阈值有效,则创建一个仅获取锁定(并创建文件)的新锁定模型。

public class MyLock : log4net.Appender.FileAppender.MinimalLock
{
      public override Stream AcquireLock()
      {
            if (CurrentAppender.Threshold == log4net.Core.Level.Off)
                  return null;

            return base.AcquireLock();
      }
}

现在在配置文件中,将阈值设置为:

<threshold value="OFF" />

并确保在建模时设置这个新的LockingModel:

<lockingModel type="Namespace.MyLock" />

我正在使用滚动文件追加器。

第二种方法列在链接中。我没有尝试过这种技术,但它在技术上似乎是合理的。

答案 1 :(得分:7)

我知道这是一个老问题,但我认为这对其他人有用。

我们遇到了类似的情况,即如果没有发生错误,应用程序不应该留下空的日志文件。

我们通过创建以下自定义LockingModel类来解决它:

public class MinimalLockDeleteEmpty : FileAppender.MinimalLock
{
    public override void ReleaseLock()
    {
        base.ReleaseLock();

        var logFile = new FileInfo(CurrentAppender.File);
        if (logFile.Exists && logFile.Length <= 0)
        {
            logFile.Delete();
        }
    }
}

它派生自FileAppender.MinimalLock类,它将在写入每条日志消息后释放对日志文件的锁定。

我们添加了额外的功能,如果日志文件在锁定释放后仍为空,则会删除日志文件。如果应用程序运行并退出而没有任何错误,它会阻止应用程序留下空的错误日志文件。

赞成

  • 在Log4Net的配置阶段仍会创建一个空的日志文件,确保在应用程序的其余部分启动之前记录正常。但是,会立即删除日志文件。
  • 它不需要您通过将阈值设置为“OFF”来关闭配置文件中的日志记录,然后在编写第一个日志事件之前以编程方式打开日志记录。

缺点

  • 这很可能是一种管理日志文件的慢速方法,因为ReleaseLock方法和文件长度检查将在写入日志文件的每个日志事件之后调用。只有在您希望错误很少的情况下才使用它,并且在没有错误时,日志文件不应该存在是业务要求。
  • 空白时创建和删除日志文件。如果您有其他工具监视日志目录以查找文件系统更改,则可能会出现问题。但是,在我们的情况下这不是问题。

答案 2 :(得分:4)

以下对我有用。首次调用OpenFile()会在配置记录器时发生。后续调用是在生成实际日志消息时。

class CustomFileAppender : RollingFileAppender
{
    private bool isFirstTime = true;
    protected override void OpenFile(string fileName, bool append)
    {
        if (isFirstTime)
        {
            isFirstTime = false;
            return;
        }

        base.OpenFile(fileName, append);
    }

}

在配置文件中,更改appender

<log4net>
<appender name="RollingFile" type="<your namespace>.CustomFileAppender">
...
</log4net>

来自log4Net源的序列如下:

  • 第一次调用OpenFile()是因为从FileAppender的构造函数调用了ActivateOptions()。
  • 当生成日志消息时,AppenderSkeleton的DoAppend()调用PreAppendCheck()
  • 在TextAppender的基础TextWriterAppender中重写了PreAppendCheck()。
  • 如果文件尚未打开,则重写的PreAppendCheck()会调用虚拟PrepareWriter
  • FileAppender的PrepareWriter()调用SafeOpenFile(),调用OpenFile()

答案 3 :(得分:3)

该方法的问题在于,如果文件存在但是只读,或者存在于不存在的目录中,则在另一个错误已经导致问题之前,您将不会发现。您真的希望确保在应用程序的其余部分开始之前日志记录正在运行。

无论如何可能会这样做,但如果不是,我怀疑这就是原因。

答案 4 :(得分:2)

this message of the mailing list archive

中描述了另一种非常简单的方法

基本上,使用log4net,在配置记录器时会创建日志文件。配置它做其他方面有点hacky。解决方案是推迟配置的执行。上面的消息建议在设置记录器时执行以下操作:

private static ILog _log = LogManager.GetLogger(typeof(Program));
public static ILog Log
{
    get
    {
        if(!log4net.LogManager.GetRepository().Configured)
            log4net.Config.XmlConfigurator.Configure(new FileInfo(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile));
        return _log;
    }
}

我通常使用程序集属性配置log4net,该属性自动配置记录器(从而创建日志文件),以及日志的简单getter:

[assembly: log4net.Config.XmlConfigurator(Watch = true)]
...
public static log4net.ILog Log { get { return _log; } }
private static readonly log4net.ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

但是删除它并使用附加逻辑添加上面的getter代替我解决了这个问题。

注意:总的来说,我同意在大多数情况下,最好在应用程序启动时配置记录器并创建文件(甚至写入它)。

答案 5 :(得分:0)

AcquireLock和ReleaseLock方法对我有用,但是很多次创建/删除了该文件让我感到困扰。这是另一个类似的选项,它关闭记录器并在程序完成时删除空的日志文件。完成日志记录后,只需调用RemoveEmptyLogFile即可。

/// <summary>
/// Sets the logging level for log4net.
/// </summary>
private static void RemoveEmptyLogFile()
{
  //Get the logfilename before we shut it down
  log4net.Appender.FileAppender rootAppender = (log4net.Appender.FileAppender)((log4net.Repository.Hierarchy.Hierarchy)log4net.LogManager.GetRepository()).Root.Appenders[0];
  string filename = rootAppender.File;

  //Shut down all of the repositories to release lock on logfile
  log4net.Repository.ILoggerRepository[] repositories = log4net.LogManager.GetAllRepositories();
  foreach (log4net.Repository.ILoggerRepository repository in repositories)
  {
    repository.Shutdown();
  }

  //Delete log file if it's empty
  var f = new FileInfo(filename);
  if (f.Exists && f.Length <= 0)
  {
    f.Delete();
  }
} // RemoveEmptyLogFile

答案 6 :(得分:-2)

private static ILog _log = LogManager.GetLogger(typeof(Program));
public static ILog Log
{
    get
    {
        if(!log4net.LogManager.GetRepository().Configured)
        log4net.Config.XmlConfigurator.Configure(new FileInfo(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile));
        return _log;
    }
}