You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何限制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");
}

已尝试的疑问

  1. 曾看到有方案提到配置queueLimit="5000" overflowAction="Discard",但反编译后发现自己用的FileTarget继承链(FileTarget → TargetWithLayoutHeaderAndFooter → TargetWithLayout → Target)中没有这两个属性,它们属于AsyncTaskTarget,这是为什么?
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 11:50:55