Azure函数App新线程日志停止输出至App Insights问题排查
解决方案与排查方向
一、新线程日志输出的调整方法
创建带作用域的Logger实例
原始注入的ILogger绑定了函数的请求上下文,主线程返回后这个上下文会被销毁,导致新线程里的Logger无法正常输出日志。可以在启动后台任务前创建一个独立的作用域Logger:// 先在类中注入IServiceProvider private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; public MyWorkingClass(ILogger log, IServiceProvider serviceProvider) { _logger = log; _serviceProvider = serviceProvider; } public string Run() { using (var scope = _serviceProvider.CreateScope()) { var scopedLogger = scope.ServiceProvider.GetRequiredService<ILogger<MyWorkingClass>>(); Task.Run(() => { scopedLogger.LogInformation("starting"); // 后续所有日志都用这个scopedLogger scopedLogger.LogInformation("正在执行步骤1"); // ... 长时间操作 }); } return "Starting task"; }作用域内的Logger不会随主线程上下文回收而失效,能保证日志正常输出到App Insights。
调整日志批量发送配置
检查host.json里的日志配置,默认的批量发送策略可能因为日志没攒够数量,就被上下文销毁导致丢失。可以调小批量大小和超时时间,让日志更快发送:{ "logging": { "batchSize": 5, "flushTimeout": "00:00:10", "applicationInsights": { "samplingSettings": { "isEnabled": true, "excludedTypes": "Request" } } } }
二、其他可能影响的因素
函数超时限制
Azure Function消费计划默认超时是5分钟,即使主线程返回了,宿主进程如果到了超时时间还是会被回收,后台线程也会被强制终止。可以在host.json里延长超时:{ "functionTimeout": "00:15:00" }注意:高级计划或专用计划可以设置更长的超时,消费计划最长10分钟。
后台任务未被宿主跟踪
Task.Run创建的后台任务不受Azure Function宿主跟踪,主线程结束后宿主可能会主动终止这些未完成的任务。换成QueueBackgroundWorkItem让宿主跟踪任务:// 注入IHostingEnvironment private readonly IHostingEnvironment _hostingEnv; public MyWorkingClass(ILogger log, IServiceProvider serviceProvider, IHostingEnvironment hostingEnv) { _logger = log; _serviceProvider = serviceProvider; _hostingEnv = hostingEnv; } public string Run() { _hostingEnv.QueueBackgroundWorkItem(async cancellationToken => { using (var scope = _serviceProvider.CreateScope()) { var scopedLogger = scope.ServiceProvider.GetRequiredService<ILogger<MyWorkingClass>>(); scopedLogger.LogInformation("starting"); // 长时间操作要监听取消信号,避免强制终止 await LongRunningTask(cancellationToken); } }); return "Starting task"; }App Insights采样过滤
App Insights的采样策略可能过滤掉了部分日志,测试阶段可以把采样率设为100%:{ "logging": { "applicationInsights": { "samplingSettings": { "isEnabled": true, "fixedRate": 1.0 } } } }日志上下文丢失
新线程可能丢失了原始请求的TraceId等上下文信息,导致日志无法关联到请求被过滤。可以手动给Logger添加作用域保留上下文:Task.Run(() => { using (_logger.BeginScope(new Dictionary<string, object> { ["TraceId"] = Activity.Current?.Id })) { _logger.LogInformation("starting"); // ... 后续日志 } });
内容的提问来源于stack exchange,提问作者CodeMonkey
相关产品推荐
相关产品推荐

