如何在Azure Service Fabric应用中捕获未捕获异常并记录至App Insights
当然可以搞定!在Azure Service Fabric里实现全局未捕获异常的捕获、记录到App Insights再重新抛出,得根据你的服务类型(是ASP.NET Core Web服务还是普通的无状态/有状态服务)来选对应的方案,我给你梳理几个常用的场景:
1. 针对ASP.NET Core类型的后端服务
如果你的后端是基于ASP.NET Core的Web服务,自定义异常处理中间件是最靠谱的方式——它能拦截所有请求链路里的未捕获异常,记录到App Insights后再重新抛出。
先写一个自定义中间件:
public class UnhandledExceptionLoggingMiddleware { private readonly RequestDelegate _next; private readonly ILogger<UnhandledExceptionLoggingMiddleware> _logger; private readonly TelemetryClient _telemetryClient; public UnhandledExceptionLoggingMiddleware(RequestDelegate next, ILogger<UnhandledExceptionLoggingMiddleware> logger, TelemetryClient telemetryClient) { _next = next; _logger = logger; _telemetryClient = telemetryClient; } public async Task InvokeAsync(HttpContext httpContext) { try { // 传递请求到下一个中间件 await _next(httpContext); } catch (Exception ex) { // 把异常记录到App Insights,顺便加些服务上下文方便排查 var exceptionTelemetry = new ExceptionTelemetry(ex); exceptionTelemetry.Properties["ServiceName"] = httpContext.Request.Host.Value; exceptionTelemetry.Properties["RequestPath"] = httpContext.Request.Path; _telemetryClient.TrackException(exceptionTelemetry); // 同时记到本地日志(可选) _logger.LogError(ex, "未捕获异常已上报至App Insights"); // 重新抛出异常,让上层框架或Service Fabric处理后续逻辑 throw; } } }
然后在Program.cs里注册这个中间件注意要放在路由、端点等核心中间件之前,确保能捕获到所有异常:
// 注册自定义异常日志中间件 app.UseMiddleware<UnhandledExceptionLoggingMiddleware>(); // 其他中间件注册(比如路由、静态文件等) app.UseRouting(); // ...
2. 针对非ASP.NET Core的Service Fabric服务(无状态/有状态)
如果是普通的无状态/有状态服务(比如只在RunAsync里跑业务逻辑),需要在服务的初始化和核心循环里做全局异常捕获,同时还要处理后台线程、Task的未观察异常。
处理RunAsync循环内的异常
protected override async Task RunAsync(CancellationToken cancellationToken) { var telemetryClient = new TelemetryClient(new TelemetryConfiguration("<你的App Insights Instrumentation Key>")); try { while (!cancellationToken.IsCancellationRequested) { // 你的核心业务逻辑代码 await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken); } } catch (Exception ex) { // 记录异常到App Insights,加上Service Fabric的上下文信息 var exceptionTelemetry = new ExceptionTelemetry(ex); exceptionTelemetry.Properties["PartitionId"] = Context.PartitionId.ToString(); exceptionTelemetry.Properties["ReplicaId"] = Context.ReplicaId.ToString(); telemetryClient.TrackException(exceptionTelemetry); // 强制推送日志,避免进程退出前日志没发出去 telemetryClient.Flush(); await Task.Delay(TimeSpan.FromSeconds(2)); // 重新抛出异常,让Service Fabric触发故障转移 throw; } }
捕获AppDomain和Task的全局未处理异常
在服务的OnInitialize方法里注册全局事件,覆盖线程池、后台Task的未捕获异常:
protected override void OnInitialize() { base.OnInitialize(); var telemetryClient = new TelemetryClient(new TelemetryConfiguration("<你的App Insights Instrumentation Key>")); // 捕获AppDomain级别的未处理异常 AppDomain.CurrentDomain.UnhandledException += (sender, args) => { if (args.ExceptionObject is Exception ex) { telemetryClient.TrackException(ex); telemetryClient.Flush(); Task.Delay(TimeSpan.FromSeconds(2)).Wait(); } }; // 捕获未被观察到的Task异常 TaskScheduler.UnobservedTaskException += (sender, args) => { telemetryClient.TrackException(args.Exception); telemetryClient.Flush(); Task.Delay(TimeSpan.FromSeconds(2)).Wait(); // 这里如果调用args.SetObserved()会阻止进程崩溃,但通常推荐让异常传播,触发Service Fabric的故障转移 // args.SetObserved(); }; }
3. 几个通用注意事项
- 配置App Insights:确保你的服务已经通过配置文件(比如
appsettings.json)或者环境变量正确设置了App Insights的Instrumentation Key,避免硬编码。 - 补充上下文信息:记录异常时尽量添加服务名称、分区ID、请求参数等信息,后续排查问题会方便很多。
- 日志推送时机:在进程可能退出的场景下,调用
Flush()后最好加一点延迟,确保日志能成功发送到App Insights。
内容的提问来源于stack exchange,提问作者Slicc
相关产品推荐
相关产品推荐

