如何禁止在应用程序启动时创建空日志文件?

发布于 2024-08-27 01:25:11 字数 103 浏览 6 评论 0原文

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

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

I have configured log4net in my app successfully but one thing is a little bit annoying for me.

The log file is created (empty) after my app start even if no error occurs. I would like to log file be created only after some error.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

无人问我粥可暖 2024-09-03 01:25:11

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

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" />

我将其与滚动文件附加程序一起使用。

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

I actually found a way to do this in this thread:

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

I've tested the first method and it works. Just in case that link is not longer good I'll reproduce the code here. Basically the author states that there are two ways of doing this.

First way:

Create a new locking model that only acquires a lock (and creates the file) if the appropriate threshold for that logger works.

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

            return base.AcquireLock();
      }
}

Now in the config file, set the threshold to start out as:

<threshold value="OFF" />

and make sure you set this new LockingModel as you model:

<lockingModel type="Namespace.MyLock" />

I'm using this with a rolling file appender.

The second method is listed at the link. I haven't tried this technique but it seems to be technically sound.

╰沐子 2024-09-03 01:25:11

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

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

我们通过创建以下自定义 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 方法以及对文件长度的检查将在每个日志事件写入日志文件后调用。仅当您预计错误很少时才使用它,并且业务要求是在没有错误时日志文件不应存在。
  • 日志文件被创建并在空时被删除。如果您有其他工具监视日志目录中的文件系统更改,这可能会出现问题。然而,这在我们的情况下不是问题。

I know this is an old question but I think this can be useful for someone else.

We came across a similar situation where it was required that the application shouldn't leave an empty log file if no errors occurred.

We solved it by creating the following custom LockingModel class:

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

It is derived from the FileAppender.MinimalLock class that will release the lock on the log file after writing each log message.

We added extra functionality that will delete the log file if it is still empty after the lock is released. It prevents the application from leaving empty error log files if the applications runs and exits without any errors.

Pros

  • It will still create an empty log file during the configuration phase of Log4Net, ensuring that logging is working before the rest of the app starts. However, the log file is deleted immediately.
  • It doesn't require you to turn off logging in your config file by setting threshold value to "OFF" and than, later on, turn on logging programmatically before writing your first log event.

Cons

  • This is most likely a slow method of managing your log files because the ReleaseLock method, and the check on the file length, will be called after every log event that is written to the log file. Only use it when you expect to have very few errors and it is a business requirement that the log file shouldn't exist when there are no errors.
  • The log files are created and deleted when empty. This might be a problem if you have other tools monitoring the log directory for file system changes. However, this was not a problem in our situation.
笑脸一如从前 2024-09-03 01:25:11

以下内容对我有用。配置记录器时会发生对 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()
  • PreAppendCheck() 在 FileAppender 的基础 TextWriterAppender 中被重写。
  • 如果文件尚未打开,则重写的 PreAppendCheck() 会调用虚拟的PrepareWriter。
  • FileAppender 的PrepareWriter() 会调用SafeOpenFile(),而SafeOpenFile() 又会调用OpenFile()

The following worked for me.The first call to OpenFile() occurs when the logger is configured. Subsequent calls are when actual log message is generated.

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

        base.OpenFile(fileName, append);
    }
}

And in the config file, change the appender

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

The sequence from log4Net source is as below:


  • The first call to OpenFile() is because of ActivateOptions() called from FileAppender's constructor.
  • When log message is generated, AppenderSkeleton's DoAppend() calls PreAppendCheck()
  • PreAppendCheck() is overridden in TextWriterAppender, the base of FileAppender.
  • The overridden PreAppendCheck() calls virtual PrepareWriter if the file is not yet open.
  • PrepareWriter() of FileAppender calls SafeOpenFile() which inturn calls OpenFile()
感悟人生的甜 2024-09-03 01:25:11

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

无论如何,可能有一种方法可以做到这一点,但如果没有,我怀疑这就是原因。

The problem with that approach is that then if the file exists but is read-only, or is in a directory which doesn't exist etc, you won't find out until another error is already causing problems. You really want to be confident that logging is working before the rest of the app starts.

There may be a way of doing this anyway, but if not I suspect that this is the reason.

我不在是我 2024-09-03 01:25:11

另一种非常简单的方法在 邮件列表存档的此消息

基本上,对于 log4net,日志文件是在配置记录器时创建的。将其配置为以其他方式执行有点困难。解决方案是推迟配置的执行。上面的消息建议在设置记录器时执行以下操作:

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 中添加额外的逻辑反而解决了我的问题。

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

Another method that is quite simple is described in this message of the mailing list archive

Basically, with log4net, the log file is created when the logger is configured. To configure it to do otherwise is a bit hacky. The solution is to defer the execution of the configuration. The message above suggests doing the following when setting up the logger:

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

I usually configure log4net with the assembly attribute, which configures the logger automatically (thus creating the log file), and a simple getter for the log:

[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);

But removing that and adding in the above getter with the additional logic instead solved the problem for me.

Note: in general I agree that in most cases it would be best to configure the logger and create the file (and even write to it) on application startup.

我早已燃尽 2024-09-03 01:25:11

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

AcquireLock and ReleaseLock method worked for me, but it bothered me that the file was created/deleted that many times. Here is another similar option that shuts down the logger and deletes the empty logfile when the program completed. Just call RemoveEmptyLogFile when you are done logging.

/// <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
美人骨 2024-09-03 01:25:11
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;
    }
}
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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文