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

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

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

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

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(); } } 

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

  

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

  

我正在使用滚动文件appender。

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

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

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

我们通过创建以下自定义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类,它将在写入每条日志消息后释放对日志文件的锁定。

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

优点

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

缺点

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

这种方法的问题是,如果文件存在但是只读,或者存在于不存在的目录中,则在另一个错误已经导致问题之前,您将无法找到。 您确实希望在应用程序的其余部分启动之前确保日志记录正常工作。

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

以下对我有用。在配置记录器时,首次调用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源的顺序如下:


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

邮件列表存档的此消息中描述了另一种非常简单的方法

基本上,使用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; } } 

我通常使用assembly属性配置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代替我解决了问题。

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

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

 ///  /// Sets the logging level for log4net. ///  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 
 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; } }