如何限制C#无限循环中NLog重复日志的输出次数?
NLog重复日志限制方案问题
问题背景
我有一个循环运行的C#程序:
while (true) { // ... 业务逻辑 }
在记录失败场景日志时,相同的错误消息会被输出数百万次,直接撑爆日志文件。我希望实现限制相同日志的输出次数,比如只显示一次初始错误,后续统计重复次数,最终日志效果如下:
...
SendMove: from here=[home] to loc=[dest]
some_object is null
this error has occurred 12938 times since
...
当前使用NLog 4.6.0版本,Logger的典型用法:
using NLog; // ... private readonly Logger _logger = LogManager.GetCurrentClassLogger(); // ... if (some_object != null) { _logger.Debug($"SendMove: from here=[{this.Name}] to loc=[{loc.Name}]"); } else { _logger.Debug("some_object is null"); }
已尝试的疑问
- 曾看到有方案提到配置
queueLimit="5000" overflowAction="Discard",但反编译后发现自己用的FileTarget继承链(FileTarget → TargetWithLayoutHeaderAndFooter → TargetWithLayout → Target)中没有这两个属性,它们属于AsyncTaskTarget,这是为什么? - NLog是否支持获取最后一条日志消息?我想自行判断是否重复输出,比如:
if (!(_logger.last_log_message().StartsWith("some_object is null")) { _logger.Debug("some_object is null (possible multiple times)"); }
可行解决方案
方案1:使用NLog内置的重复消息过滤器
NLog 4.6.0支持WhenRepeated过滤器,可以直接在配置中实现重复消息合并:
<nlog> <targets> <target name="file" xsi:type="File" fileName="app.log" /> </targets> <rules> <logger name="*" minlevel="Debug" writeTo="file"> <filters> <!-- 相同消息5秒内仅输出一次,超时后会重新输出;同时记录重复次数 --> <whenRepeated layout="${message} (repeated ${repeatedCount} times)" timeoutSeconds="5" action="Ignore" /> </filters> </logger> </rules> </nlog>
如果希望仅输出一次初始消息,后续只统计总次数,可以自定义布局或结合自定义过滤器实现。
方案2:自定义日志包装类
如果不想修改配置,可以自行封装Logger,手动记录重复次数:
public class ThrottledLogger { private readonly Logger _innerLogger; private string _lastMessage; private int _repeatCount; private readonly object _lock = new object(); public ThrottledLogger(Logger innerLogger) { _innerLogger = innerLogger; } public void Debug(string message) { lock (_lock) { if (message == _lastMessage) { _repeatCount++; } else { // 输出上一条消息的重复统计 if (_repeatCount > 0) { _innerLogger.Debug($"this error has occurred {_repeatCount} times since"); _repeatCount = 0; } _innerLogger.Debug(message); _lastMessage = message; } } } // 按需封装Info/Warn/Error等其他日志级别方法 }
使用时替换原Logger:
private readonly ThrottledLogger _logger = new ThrottledLogger(LogManager.GetCurrentClassLogger());
关于queueLimit和overflowAction的说明
这两个属性是**异步日志目标(AsyncTaskTarget)**的配置项,作用是控制异步日志队列的容量和溢出处理逻辑,和限制重复日志无关。你使用的FileTarget是同步目标,不在AsyncTaskTarget的继承链中,因此没有这些属性。
关于获取最后一条日志消息
NLog本身没有提供直接获取最后一条日志的API,因为Logger设计为无状态,且多线程场景下直接访问"最后一条消息"存在线程安全问题,推荐使用上述自定义包装类实现需求。
内容的提问来源于stack exchange,提问作者Dominique
相关产品推荐
相关产品推荐

