基于大小限制滚动文件附加程序并使用时间戳进行备份
Rolling file appender based on size limit and backing up with a timestamp
有没有一种方法可以设置 log4net 配置,如果日志文件的大小超过某个限制,现有的日志文件应该用时间戳重命名?
要求:
例如当大小超过 1MB 时,我的日志应该看起来像
Log.txt
Log.12-07-2018.txt
Log.11-07-2018.txt
等,其中 Log.txt 是 current/latest 日志。
注:
- 当滚动在同一天发生两次时,log4net 应该将其附加到已经具有当天时间戳的文件中。例如如果滚动发生在 12-07-2018 并且文件大小限制为 1MB 则可以备份到 Log.12-07-2018.txt 文件导致 2MB 文件大小。
我尝试了什么:
这是我正在使用的 log4net 配置:
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender" >
<file value="${LOCALAPPDATA}\foobar\Log.txt" />
<encoding value="utf-8" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maximumFileSize value="1MB" />
<maxSizeRollBackups value="1000" />
<preserveLogFileNameExtension value="true" />
<datePattern value=".dd-MM-yyyy" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %level [%thread] %type.%method - %message%n" />
</layout>
</appender>
<root>
<level value="All" />
<!-- If the following line is not included the log file will not be created even if log4net is configured with this file. -->
<appender-ref ref="RollingFileAppender" />
</root>
</log4net>
我得到的:
Log.txt
Log.1.txt
Log.2.txt
我希望重命名部分基于时间戳,备份逻辑基于大小。当我尝试时:
<rollingStyle value="Date" />
<datePattern value=".dd-MM-yyyy" />
备份日志文件的命名有效,但滚动是基于日期而不是大小。
也许我不应该 post 这作为答案(唯一的区别是 staticlogfilename 属性和日志扩展的 file-datePattern 值)但我发现 post 代码很难看在评论中...
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="path\Log"/>
<datePattern value="'.'yyyy-MM-dd'.log'" />
<appendToFile value="true"/>
<staticLogFileName value="false" />
<maximumFileSize value="1MB"/>
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
</appender>
.config 文件的这个配置对我有用
可以实现,但不能仅通过配置实现。
必须创建自定义 RollingFileAppender
并在 Log4net
配置中引用,如下所示,其中 PFX.RollingFileAppender
从程序集 PFX.Lib
.
加载
设置staticLogFileName
确保最新的文件总是命名为Log.txt
(如配置)。
我们(重新)使用 datePattern
选项来配置日志文件名中的日期部分。
(出于性能原因,countDirection
设置为值 0 以减少翻转次数;最多 recent/last 备份文件将是编号最高的文件。)
<log4net>
<appender name="RollingFileAppender" type="PFX.CustomRollingFileAppender, PFX.Lib" >
<datePattern value="yyyy-MM-dd" />
<staticLogFileName value="true" />
<countDirection value="0" />
<file value="${LOCALAPPDATA}\foobar\Log.txt" />
<encoding value="utf-8" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maximumFileSize value="1MB" />
<maxSizeRollBackups value="1000" />
<preserveLogFileNameExtension value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %level [%thread] %type.%method - %message%n" />
</layout>
</appender>
<root>
<level value="All" />
<appender-ref ref="RollingFileAppender" />
</root>
</log4net>
要求中最具挑战性的部分是排除文件名中的index/counter后缀;例如。 Log-2018-07-16.1.txt
因为 Log4net 在内部跟踪并使用此计数器编号来处理例如。备份文件删除。
因此,我们必须自己管理清理工作。
Log4net 的实现 RollingFileAppender
不是很开放;很少有方法可以覆盖
并且无法访问当前柜台号码跟踪。
如果您不能或不想在您自己的项目中包含和修改原始 RollingFileAppender
的完整源代码,
您可以从原始的继承,以便尽可能少地进行更改,如下所示。
如果发生基于文件大小的汇总,我们会检查今天的日志文件(匹配今天的日期;参见要求)是否已经存在。
如果是这样,则不会发生汇总;只有 Log.txt
中的内容被移动到今天的日志文件中。
如果今天的日志文件不存在,则会发生开箱即用的更新。这将创建一个名称中带有 index/counter 后缀的文件;紧接着这个文件被重命名以匹配配置的日期模式。
因为创建了一个新文件,所以删除了所有已删除的日志文件(超过配置的阈值)。
(为简洁起见,下面的代码不包含异常处理。)
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using log4net.Appender;
using log4net.Util;
namespace PFX
{
public class CustomRollingFileAppender : RollingFileAppender
{
private String _baseFileExtension;
private String _baseFileNameWithoutExtension;
private String _fileDeletePattern;
private String _folder;
private String _backupSearchPattern;
public CustomRollingFileAppender()
{}
public override void ActivateOptions()
{
base.ActivateOptions();
this._baseFileNameWithoutExtension = Path.GetFileNameWithoutExtension(this.File);
this._baseFileExtension = Path.GetExtension(this.File);
this._folder = Path.GetDirectoryName(this.File);
this._fileDeletePattern = $"{this._baseFileNameWithoutExtension}*{this._baseFileExtension}";
this._backupSearchPattern = $"{this._baseFileNameWithoutExtension}.*{this._baseFileExtension}";
}
protected override void AdjustFileBeforeAppend()
{
if ((RollingMode.Size == this.RollingStyle)
&& (this.File != null)
&& (((CountingQuietTextWriter)base.QuietWriter).Count >= this.MaxFileSize)
)
{
DateTime now = DateTime.Now;
String todayFileSuffix = now.ToString(this.DatePattern, CultureInfo.InvariantCulture);
String todayFileName = $"{this._baseFileNameWithoutExtension}{todayFileSuffix}{this._baseFileExtension}";
String todayFile = Path.Combine(this._folder, todayFileName);
if (base.FileExists(todayFile))
{
/* Todays logfile already exist; append content to todays file. */
base.CloseFile(); // Close current file in order to allow reading todays file.
this.moveContentToTodaysFile(todayFile);
// Delete and open todays file for a fresh start.
base.DeleteFile(this.File);
base.OpenFile(this.File, false);
}
else
{
/* Do a roll-over. */
base.RollOverSize();
using (base.SecurityContext.Impersonate(this))
{
this.deleteDepricatedBackupFiles();
this.renameBackupFiles(todayFile);
}
}
}
else
{
base.AdjustFileBeforeAppend();
}
}
// Moves the content from the current log file to todays file.
private void moveContentToTodaysFile(String todayFile)
{
using (FileStream logFile = new FileStream(this.File, FileMode.Open, FileAccess.Read, FileShare.Read))
using (StreamReader reader = new StreamReader(logFile))
using (FileStream backupFile = new FileStream(todayFile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
using (StreamWriter writer = new StreamWriter(backupFile))
{
const Int32 BUFFER_SIZE = 1024;
Char[] buffer = new Char[BUFFER_SIZE];
while (true)
{
Int32 nrOfCharsRead = reader.Read(buffer, 0, BUFFER_SIZE);
if (nrOfCharsRead <= 0) { break; }
writer.Write(buffer, 0, nrOfCharsRead);
}
}
}
// Rename backup files according to the configured date pattern, removing the counter/index suffix.
private void renameBackupFiles(String todayFile)
{
IEnumerable<String> backupFiles = Directory.EnumerateFiles(this._folder, this._backupSearchPattern, SearchOption.TopDirectoryOnly);
foreach (String backupFile in backupFiles)
{
base.RollFile(backupFile, todayFile);
}
}
// Keep the number of allowed backup files and delete all others.
private void deleteDepricatedBackupFiles()
{
DirectoryInfo folder = new DirectoryInfo(this._folder);
IEnumerable<FileInfo> filesToDelete =
folder
.EnumerateFiles(this._fileDeletePattern, SearchOption.TopDirectoryOnly)
.OrderByDescending(o => o.LastWriteTime)
.Skip(this.MaxSizeRollBackups + 1)
;
foreach (FileSystemInfo fileToDelete in filesToDelete)
{
base.DeleteFile(fileToDelete.FullName);
}
}
}
}
有没有一种方法可以设置 log4net 配置,如果日志文件的大小超过某个限制,现有的日志文件应该用时间戳重命名?
要求:
例如当大小超过 1MB 时,我的日志应该看起来像
Log.txt
Log.12-07-2018.txt
Log.11-07-2018.txt
等,其中 Log.txt 是 current/latest 日志。
注:
- 当滚动在同一天发生两次时,log4net 应该将其附加到已经具有当天时间戳的文件中。例如如果滚动发生在 12-07-2018 并且文件大小限制为 1MB 则可以备份到 Log.12-07-2018.txt 文件导致 2MB 文件大小。
我尝试了什么:
这是我正在使用的 log4net 配置:
<log4net>
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender" >
<file value="${LOCALAPPDATA}\foobar\Log.txt" />
<encoding value="utf-8" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maximumFileSize value="1MB" />
<maxSizeRollBackups value="1000" />
<preserveLogFileNameExtension value="true" />
<datePattern value=".dd-MM-yyyy" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %level [%thread] %type.%method - %message%n" />
</layout>
</appender>
<root>
<level value="All" />
<!-- If the following line is not included the log file will not be created even if log4net is configured with this file. -->
<appender-ref ref="RollingFileAppender" />
</root>
</log4net>
我得到的:
Log.txt
Log.1.txt
Log.2.txt
我希望重命名部分基于时间戳,备份逻辑基于大小。当我尝试时:
<rollingStyle value="Date" />
<datePattern value=".dd-MM-yyyy" />
备份日志文件的命名有效,但滚动是基于日期而不是大小。
也许我不应该 post 这作为答案(唯一的区别是 staticlogfilename 属性和日志扩展的 file-datePattern 值)但我发现 post 代码很难看在评论中...
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="path\Log"/>
<datePattern value="'.'yyyy-MM-dd'.log'" />
<appendToFile value="true"/>
<staticLogFileName value="false" />
<maximumFileSize value="1MB"/>
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
</appender>
.config 文件的这个配置对我有用
可以实现,但不能仅通过配置实现。
必须创建自定义 RollingFileAppender
并在 Log4net
配置中引用,如下所示,其中 PFX.RollingFileAppender
从程序集 PFX.Lib
.
设置staticLogFileName
确保最新的文件总是命名为Log.txt
(如配置)。
我们(重新)使用 datePattern
选项来配置日志文件名中的日期部分。
(出于性能原因,countDirection
设置为值 0 以减少翻转次数;最多 recent/last 备份文件将是编号最高的文件。)
<log4net>
<appender name="RollingFileAppender" type="PFX.CustomRollingFileAppender, PFX.Lib" >
<datePattern value="yyyy-MM-dd" />
<staticLogFileName value="true" />
<countDirection value="0" />
<file value="${LOCALAPPDATA}\foobar\Log.txt" />
<encoding value="utf-8" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maximumFileSize value="1MB" />
<maxSizeRollBackups value="1000" />
<preserveLogFileNameExtension value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %level [%thread] %type.%method - %message%n" />
</layout>
</appender>
<root>
<level value="All" />
<appender-ref ref="RollingFileAppender" />
</root>
</log4net>
要求中最具挑战性的部分是排除文件名中的index/counter后缀;例如。 Log-2018-07-16.1.txt 因为 Log4net 在内部跟踪并使用此计数器编号来处理例如。备份文件删除。 因此,我们必须自己管理清理工作。
Log4net 的实现 RollingFileAppender
不是很开放;很少有方法可以覆盖
并且无法访问当前柜台号码跟踪。
如果您不能或不想在您自己的项目中包含和修改原始 RollingFileAppender
的完整源代码,
您可以从原始的继承,以便尽可能少地进行更改,如下所示。
如果发生基于文件大小的汇总,我们会检查今天的日志文件(匹配今天的日期;参见要求)是否已经存在。
如果是这样,则不会发生汇总;只有 Log.txt
中的内容被移动到今天的日志文件中。
如果今天的日志文件不存在,则会发生开箱即用的更新。这将创建一个名称中带有 index/counter 后缀的文件;紧接着这个文件被重命名以匹配配置的日期模式。
因为创建了一个新文件,所以删除了所有已删除的日志文件(超过配置的阈值)。
(为简洁起见,下面的代码不包含异常处理。)
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using log4net.Appender;
using log4net.Util;
namespace PFX
{
public class CustomRollingFileAppender : RollingFileAppender
{
private String _baseFileExtension;
private String _baseFileNameWithoutExtension;
private String _fileDeletePattern;
private String _folder;
private String _backupSearchPattern;
public CustomRollingFileAppender()
{}
public override void ActivateOptions()
{
base.ActivateOptions();
this._baseFileNameWithoutExtension = Path.GetFileNameWithoutExtension(this.File);
this._baseFileExtension = Path.GetExtension(this.File);
this._folder = Path.GetDirectoryName(this.File);
this._fileDeletePattern = $"{this._baseFileNameWithoutExtension}*{this._baseFileExtension}";
this._backupSearchPattern = $"{this._baseFileNameWithoutExtension}.*{this._baseFileExtension}";
}
protected override void AdjustFileBeforeAppend()
{
if ((RollingMode.Size == this.RollingStyle)
&& (this.File != null)
&& (((CountingQuietTextWriter)base.QuietWriter).Count >= this.MaxFileSize)
)
{
DateTime now = DateTime.Now;
String todayFileSuffix = now.ToString(this.DatePattern, CultureInfo.InvariantCulture);
String todayFileName = $"{this._baseFileNameWithoutExtension}{todayFileSuffix}{this._baseFileExtension}";
String todayFile = Path.Combine(this._folder, todayFileName);
if (base.FileExists(todayFile))
{
/* Todays logfile already exist; append content to todays file. */
base.CloseFile(); // Close current file in order to allow reading todays file.
this.moveContentToTodaysFile(todayFile);
// Delete and open todays file for a fresh start.
base.DeleteFile(this.File);
base.OpenFile(this.File, false);
}
else
{
/* Do a roll-over. */
base.RollOverSize();
using (base.SecurityContext.Impersonate(this))
{
this.deleteDepricatedBackupFiles();
this.renameBackupFiles(todayFile);
}
}
}
else
{
base.AdjustFileBeforeAppend();
}
}
// Moves the content from the current log file to todays file.
private void moveContentToTodaysFile(String todayFile)
{
using (FileStream logFile = new FileStream(this.File, FileMode.Open, FileAccess.Read, FileShare.Read))
using (StreamReader reader = new StreamReader(logFile))
using (FileStream backupFile = new FileStream(todayFile, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
using (StreamWriter writer = new StreamWriter(backupFile))
{
const Int32 BUFFER_SIZE = 1024;
Char[] buffer = new Char[BUFFER_SIZE];
while (true)
{
Int32 nrOfCharsRead = reader.Read(buffer, 0, BUFFER_SIZE);
if (nrOfCharsRead <= 0) { break; }
writer.Write(buffer, 0, nrOfCharsRead);
}
}
}
// Rename backup files according to the configured date pattern, removing the counter/index suffix.
private void renameBackupFiles(String todayFile)
{
IEnumerable<String> backupFiles = Directory.EnumerateFiles(this._folder, this._backupSearchPattern, SearchOption.TopDirectoryOnly);
foreach (String backupFile in backupFiles)
{
base.RollFile(backupFile, todayFile);
}
}
// Keep the number of allowed backup files and delete all others.
private void deleteDepricatedBackupFiles()
{
DirectoryInfo folder = new DirectoryInfo(this._folder);
IEnumerable<FileInfo> filesToDelete =
folder
.EnumerateFiles(this._fileDeletePattern, SearchOption.TopDirectoryOnly)
.OrderByDescending(o => o.LastWriteTime)
.Skip(this.MaxSizeRollBackups + 1)
;
foreach (FileSystemInfo fileToDelete in filesToDelete)
{
base.DeleteFile(fileToDelete.FullName);
}
}
}
}