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

Serilog.Sinks.Async生成数千线程问题排查与优化咨询

Serilog异步Sink导致服务器性能下降问题排查

我们同时使用Serilog的File Sink和Elasticsearch Sink,且均通过Serilog异步Sink将日志处理放在后台线程执行。近期服务器出现全域性能下降,系统工程师抓取完整用户Dump后,定位到问题与Serilog.Sinks.Async.BackgroundWorkerSink相关。

线程堆栈信息

近3000个线程中,某一线程的CLR堆栈如下:

OS Thread Id: 0x30b7c
        Child SP               IP Call Site
00000025AAA8F048 00007ffd91720bb4 [HelperMethodFrame_1OBJ: 00000025aaa8f048] System.Threading.Monitor.ObjWait(Int32, System.Object)
00000025AAA8F170 00007ffce4454638 System.Threading.SemaphoreSlim.WaitUntilCountOrTimeout(Int32, UInt32, System.Threading.CancellationToken) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs @ 462]
00000025AAA8F1C0 00007ffce43f1b4a System.Threading.SemaphoreSlim.Wait(Int32, System.Threading.CancellationToken) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs @ 365]
00000025AAA8F270 00007ffce44b1e27 System.Collections.Concurrent.BlockingCollection`1[[System.__Canon, System.Private.CoreLib]].TryTakeWithNoTimeValidation(System.__Canon ByRef, Int32, System.Threading.CancellationToken, System.Threading.CancellationTokenSource)
00000025AAA8F2F0 00007ffce44b1ce4 System.Collections.Concurrent.BlockingCollection`1+d__68[[System.__Canon, System.Private.CoreLib]].MoveNext()
00000025AAA8F340 00007ffce2ede22d Serilog.Sinks.Async.BackgroundWorkerSink.Pump()
00000025AAA8F390 00007ffce43d6617 System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/ExecutionContext.cs @ 183]
00000025AAA8F400 00007ffce44124fe System.Threading.Tasks.Task.ExecuteWithThreadLocal(System.Threading.Tasks.Task ByRef, System.Threading.Thread) [/_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @ 2333]
00000025AAA8F700 00007ffd421eaed3 [DebuggerU2MCatchHandlerFrame: 00000025aaa8f700]

大量线程处于SemaphoreSlim.Wait状态,等待可用线程。我们怀疑存在配置错误或优化空间,且问题大概率与File Sink相关。

疑问

  • File Sink设置shared:true是否合理?
  • Elasticsearch Sink与异步Sink结合使用是否合理?
  • Serilog应使用线程池,为何会生成约3000个线程?

当前配置代码

var logCfg =
    new LoggerConfiguration()
    .Enrich.WithProperty("machine", System.Environment.MachineName)
    .WriteTo.Map(keyPropertyName: "$filename", defaultKey: "fallback",
        configure: (fileName, wt) =>
             wt.Async(c =>
                 c.File(formatter: formatter
                 , path: logOptions.AuditPath
                 , shared: true
                 , fileSizeLimitBytes: fileSizeLimitBytes ?? 41943040
                 , rollingInterval: RollingInterval.Day
                 , rollOnFileSizeLimit: true
                 )
        )
    )
    .WriteTo.Async(c =>
               c.Elasticsearch(new Serilog.Sinks.Elasticsearch.ElasticsearchSinkOptions(new Uri(elasticUrl))
               {
                   IndexFormat = "my-audit-" + DateTime.Now.Year,
                   ModifyConnectionSettings = x => x.MyAuthentication(elasticCreds[0], elasticCreds[1])
               }
           )

    );

相关NuGet版本

  • Serilog.Sinks.Async 1.5.0.0
  • Serilog.Sinks.Elasticsearch 8.4.1
  • Serilog.Sinks.File 5.0.0

问题解答与优化建议

1. File Sink设置shared:true是否合理?

shared:true仅适用于多进程共享写入同一个日志文件的场景,单进程写入时完全没必要开启——它会引入额外的文件锁逻辑,导致写入线程频繁等待锁,进而阻塞异步Sink的队列处理,这很可能是当前线程等待的直接原因。

如果是多进程场景,也不建议共享同一日志文件,更合理的做法是按进程ID或业务标识拆分日志文件,从根源避免锁竞争。

2. Elasticsearch Sink与异步Sink结合使用是否合理?

不合理。Serilog.Sinks.Elasticsearch从7.x版本开始,默认已经实现了异步批量发送日志的能力,再套一层Serilog.Sinks.Async属于重复异步封装,会额外增加队列开销和线程调度成本,不仅无法提升性能,反而可能拖慢日志处理速度。直接使用Elasticsearch Sink的内置异步能力即可。

3. 为何会生成约3000个线程?

问题出在WriteTo.Map的配置上:Map会根据$filename属性的不同取值,创建独立的File Sink实例,而每个File Sink又被包裹在Async中——每个Async Sink默认会启动一个独立的后台工作线程。如果系统中$filename的取值非常多(比如每个请求生成不同的文件名),就会瞬间创建大量Async Sink实例,每个对应一个线程,最终导致线程数暴涨。这完全违背了线程池复用的设计,属于典型的配置错误。


优化方案

  1. 移除File Sink的shared:true配置(单进程场景),多进程场景改为按进程/业务拆分日志文件。
  2. 移除Elasticsearch Sink外层的Async封装,直接使用其内置的异步批量发送能力。
  3. 调整WriteTo.Map的使用方式:
    • 如果$filename取值过多,建议放弃使用Map,改为通过日志模板或过滤器区分内容,统一写入少数文件;
    • 必须使用Map时,添加limit参数限制最大Sink实例数(超过限制后会复用旧实例),示例:
      .WriteTo.Map(keyPropertyName: "$filename", defaultKey: "fallback", limit: 10,
          configure: (fileName, wt) => wt.File(...)
      )
      
  4. 升级Serilog.Sinks.Async到最新版本(当前最新为1.5.1),修复潜在的线程调度问题。

内容的提问来源于stack exchange,提问作者AardVark71

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 02:12:09