设置一个简单的 log4net 日志管理器类,处理多个附加器和自动 zip 功能





5.00/5 (6投票s)
如何设置 log4net 日志工具,创建不同的对象,配置它们将各自的日志存储在自己的日志目录中,并压缩旧日志
引言
在自动化行业中,管理/监控应用程序通常同时连接到不同的设备(PLC、标签打印机、条形码扫描仪等...)并在文件系统上存储大量的日志数据。
已经编写了大量关于 log4net 及其配置的教程,所以如果你不够熟练,你可以参考官方支持页面 https://logging.apache.ac.cn/log4net/ 并阅读 Tim Corey 在 https://codeproject.org.cn/Articles/140911/log-net-Tutorial 上发表的一篇非常好的文章“log 4 net tutorial”。
背景
log4net 允许您在不同的路径中拥有不同的日志文件,这些路径可能与主应用程序的日志文件不同。
我们希望从 log4net 配置中找到所有这些文件,通过 .log 扩展名和上次写入日期早于 XXX 天来过滤,压缩它们并删除原始文件。
使用代码
附加的 C# 解决方案(使用 VS 2015 开发,目标框架:4.5.2)是一个简单的控制台应用程序,其中
- 日志管理器具有 log4net.ILog 类型的变量,并将主应用程序的相关数据记录在 log4net.config 文件中配置的目录中
- 运行时实例化了 4 个 "Device" 对象,并且每个对象
- 将其自己的日志写入在 log4net.config 文件中配置的目录中
- 压缩较旧的 .log 文件,然后删除它们。
请注意,本文档的目的不是解释如何创建运行时长时间运行的任务和管理它们的有限状态机,而是仅以非常简单的方式满足上述要求。
log4net.config 文件
- MAIN 应用程序引用 Main RollingFileAppender 将日志存储在 Log/Main/ 文件夹中
- DEVICE1 引用 Device1 RollingFileAppender 并记录到 Log/Device1/
- DEVICE2 引用 Device2 RollingFileAppender 并记录到 Log/Device2/
- DEVICE3 引用 Device3 RollingFileAppender 并记录到 Log/Device3/
- DEVICE4 引用 Device4 RollingFileAppender 并记录到 Log/Device4/
对于每个 Appender,每个日志最多 5MB,日期模式为 yyyyMMdd.log
<!-- MAIN SECTION -->
<appender name="Main" type="log4net.Appender.RollingFileAppender">
<file value="Log/Main/" />
<datePattern value="yyyyMMdd'.log'" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<maximumFileSize value="5MB" />
<param name="StaticLogFileName" value="false"/>
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%date [%level] %message %exception %newline"/>
</layout>
</appender>
<logger name="MAIN" additivity="false">
<level value="ALL" />
<appender-ref ref="Main" />
<appender-ref ref="ConsoleAppender" />
</logger>
<!-- DEVICE 1 -->
<appender name="Device1" type="log4net.Appender.RollingFileAppender">
<file value="Log/Device1/" />
<datePattern value="yyyyMMdd'.log'" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<maxSizeRollBackups value="10" />
<maximumfilesize value="5MB" />
<param name="StaticLogFileName" value="false"/>
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%date [%level] %message %exception %newline"/>
</layout>
</appender>
<logger name="DEVICE1" additivity="false">
<level value="ALL" />
<appender-ref ref="Device1" />
<appender-ref ref="ConsoleAppender" />
</logger>
您可以为不同的对象分配不同的日志文件,这些文件位于不同的路径中,这些路径可能与主应用程序的日志文件不同。
这可以在 InitLogger() 中完成
_Logger = new Logger(string.Empty);
稍后在 InitDevices() 中:
device.Logger = new Logger(String.Format("DEVICE1"));
device.Logger = new Logger(String.Format("DEVICE2"));
device.Logger = new Logger(String.Format("DEVICE3"));
device.Logger = new Logger(String.Format("DEVICE4"));
Logger 类具有
- 一个 log4net.ILog 变量
- 用于记录错误、消息、调试信息等的方法。
- 用于压缩旧日志文件(基于 DAYS_TO_KEEP 约束)并删除未压缩的原始文件的 zip 方法。
Device 类具有
- 一个名称
- 一个识别索引
- 对主 Logger 对象的引用
- 一个名为 Work 的方法来完成其工作
主程序
- 初始化 Logger
- 创建 4 个 Device 对象并告诉他们
- 压缩他们的日志
- 完成他们的工作
- 释放对象
program.cs 的更详细视图
#region Vars
private static Logger _Logger = null;
private static Devices _Devices = null;
#endregion
static void Main(string[] args)
{
try
{
Console.WriteLine("Press any key to start...");
Console.ReadKey();
InitLogger();
InitDevices();
Work();
Console.WriteLine("Press any key to quit...");
Console.ReadKey();
}
catch (Exception)
{
throw;
}
}
private static void InitLogger()
{
try
{
_Logger = new Logger(string.Empty);
}
catch (Exception)
{
throw;
}
}
private static void InitDevices()
{
try
{
_Logger.Debug("Init devices...");
_Devices = new Devices();
for (byte i = 0; i < MAX_DEVICES; i++)
{
Device device = null;
try
{
_Logger.Debug(String.Format("Creating device {1} named DEVICE{0}", i, i + 1));
device = new Device();
device.Index = i;
device.Name = String.Format("DEVICE{0}", i + 1);
device.Logger = new Logger(String.Format("DEVICE{0}", i + 1));
_Devices.Add(device);
}
catch (Exception)
{
throw;
}
finally
{
device = null;
}
}
_Logger.Debug("Init devices done.");
}
catch (Exception)
{
throw;
}
}
protected static void Work()
{
try
{
_Logger.Debug("Starting work...");
if (_Devices == null) return;
foreach (Device device in _Devices)
{
device?.Logger?.Zip();
device?.Work();
}
_Logger.Debug("Work done.");
_Devices.Dispose();
}
catch (Exception)
{
throw;
}
}
Logger 管理器查找每个配置的文件 Appender 并检索文件夹路径
private void GetLogDirectoryPath()
{
log4net.Appender.AppenderCollection appenderCollection = null;
try
{
appenderCollection = ((log4net.Repository.Hierarchy.Logger)_Log.Logger).Appenders;
if (appenderCollection == null) return;
_LogPathList = new List<string>();
for (int i = 0; i < appenderCollection.Count; i++)
{
try
{
if (appenderCollection[i] == null) continue;
if (appenderCollection[i].GetType().BaseType != typeof(log4net.Appender.FileAppender)) continue;
_LogPathList.Add(new FileInfo(((log4net.Appender.FileAppender)appenderCollection[i]).File).DirectoryName);
}
catch (Exception e)
{
this.Error(e);
}
}
}
catch (Exception e)
{
this.Error(e);
}
finally
{
appenderCollection = null;
}
}
GetFileList() 则获取先前检索的文件夹中的所有文件,并应用扩展名和日期过滤器
private List<FileInfo> GetFileList(string path)
{
List<FileInfo> fileInfolist = null;
DirectoryInfo directoryInfo = null;
try
{
directoryInfo = new DirectoryInfo(path);
var list = from F in directoryInfo.GetFiles().ToList()
where F.Extension == LOG_FILE_EXTENSION
&& F.LastWriteTime.Date <= DateTime.Today.AddDays(-DAYS_TO_KEEP)
select F;
fileInfolist = list?.ToList();
}
catch (Exception)
{
throw;
}
finally
{
directoryInfo = null;
}
return fileInfolist;
}
现在我们可以压缩并删除每个 .log 文件
foreach (FileInfo fileInfo in fileInfolist)
{
FileStream fileStream = null;
FileInfo zippedFileInfo = null;
try
{
if (fileInfo == null) continue;
fileStream = new FileStream(fileInfo.FullName.Replace(fileInfo.Extension, ZIPPED_ARCHIVE_EXTENSION), FileMode.Create);
using (ZipArchive zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Create))
{
Stopwatch stopwatch = null;
try
{
stopwatch = new Stopwatch();
stopwatch.Start();
zipArchive.CreateEntryFromFile(fileInfo.FullName, fileInfo.Name);
stopwatch.Stop();
zippedFileInfo = new FileInfo(fileInfo.FullName.Replace(LOG_FILE_EXTENSION, ZIPPED_ARCHIVE_EXTENSION));
this.Info(String.Format("Zip done for file {0} in {1} millisec. Original size {2} bytes, compressed size {3} bytes", fileInfo.Name, stopwatch.ElapsedMilliseconds, fileInfo.Length, zippedFileInfo.Length));
}
catch (Exception e)
{
this.Error(e);
}
finally
{
stopwatch = null;
}
}
fileInfo.Delete();
}
catch (Exception e)
{
this.Error(e);
}
finally
{
fileStream = null;
zippedFileInfo = null;
}
}
输出示例
1) 主日志
2016-10-05 15:42:53,478 [DEBUG] Init devices...
2016-10-05 15:42:53,486 [DEBUG] Creating device 1 named DEVICE0
2016-10-05 15:42:53,487 [DEBUG] Creating device 2 named DEVICE1
2016-10-05 15:42:53,487 [DEBUG] Creating device 3 named DEVICE2
2016-10-05 15:42:53,487 [DEBUG] Creating device 4 named DEVICE3
2016-10-05 15:42:53,487 [DEBUG] Init devices done.
2016-10-05 15:42:53,488 [DEBUG] Starting work...
2016-10-05 15:43:00,848 [DEBUG] Work done.
2) 设备 1 日志
2016-10-05 15:42:53,492 [INFO] Zip DEVICE1 log files...
2016-10-05 15:42:53,521 [INFO] Zip done for file 20160525.log in 27 millisec. Original size 1349149 bytes, compressed size 105487 bytes
2016-10-05 15:42:53,553 [INFO] Zip done for file 20160526.log in 30 millisec. Original size 1587855 bytes, compressed size 134007 bytes
2016-10-05 15:42:53,599 [INFO] Zip done for file 20160527.log in 44 millisec. Original size 2312769 bytes, compressed size 191589 bytes
2016-10-05 15:42:55,516 [INFO] Zip done.
2016-10-05 15:42:55,517 [DEBUG] Device DEVICE1 starts working...
2016-10-05 15:42:55,517 [DEBUG] Device DEVICE1 work complete.
2016-10-05 15:43:00,848 [DEBUG] Device DEVICE1 disposed.