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

.NET 6 WebAPI:Application Insights未记录未处理异常求助

问题背景

我正在开发一个.NET 6 Web API,已安装以下NuGet包:

<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.21.0" />

相关配置(简化版):

{
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    },
    "ApplicationInsights": {
      "LogLevel": {
        "Default": "Information",
        "Microsoft": "Error"
      },
      "ConnectionString": "InstrumentationKey"
    }
  }
}

遥测扩展代码:

using System.Reflection;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel;

namespace MyApplication;

public static class ApplicationServicesLoggingExtensions
{
    /// <summary>
    ///     Adds production loggers
    /// </summary>
    /// <param name="services"></param>
    /// <param name="configuration"></param>
    /// <returns></returns>
    public static IServiceCollection AddLogging(this IServiceCollection services, IConfiguration configuration)
    {
        return services.AddLogging(logging =>
            {
                logging.ClearProviders();
                logging.AddConsole();
                logging.AddEventLog(config => { config.SourceName = Assembly.GetExecutingAssembly().FullName; });
                logging.AddConfiguration(configuration.GetSection("Logging"));
                logging.AddApplicationInsights(
                    config =>
                    {
                        config.ConnectionString =
                            configuration.GetSection("Logging:ApplicationInsights:ConnectionString").Value;
                    },
                    options => { options.FlushOnDispose = true; });
                logging.AddDebug();
            })
            .SetupStorageFolderForTelemetryChannel(configuration);
    }

    /// <summary>
    ///     Setup up telemetry with a storage folder using <see cref="ServerTelemetryChannel" />
    ///     and installs a custom initializer that helps identify the application generating telemetry
    /// </summary>
    /// <param name="services"></param>
    /// <param name="configuration"></param>
    /// <returns></returns>
    private static IServiceCollection SetupStorageFolderForTelemetryChannel(this IServiceCollection services,
        IConfiguration configuration)
    {
        services.AddSingleton<ITelemetryInitializer, CustomTelemetryInitializer>();
        var configuredStorageFolder = configuration.GetValue<string>("ApplicationInsights:StorageFolder");
        if (string.IsNullOrEmpty(configuredStorageFolder))
            return services
                .AddApplicationInsightsTelemetry();

        var baseFolder = AppContext.BaseDirectory;
        var storageFolder = Path.Combine(baseFolder, configuredStorageFolder);

        var f = new DirectoryInfo(storageFolder);
        if (!f.Exists) f.Create();
        // This sets up ServerTelemetryChannel with StorageFolder set to a custom location.
        return services.AddSingleton(typeof(ITelemetryChannel),
                new ServerTelemetryChannel { StorageFolder = storageFolder })
            .AddApplicationInsightsTelemetry();
    }
}

自定义遥测初始化器代码:

using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.Extensibility;

namespace MyApplication;

public class CustomTelemetryInitializer : ITelemetryInitializer
{
    private readonly IConfiguration _configuration;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public CustomTelemetryInitializer(IConfiguration configuration, IHttpContextAccessor httpContextAccessor)
    {
        _configuration = configuration;
        _httpContextAccessor = httpContextAccessor;
    }

    public void Initialize(ITelemetry telemetry)
    {
        SetApplicationNameToRoleName(telemetry);
        SetEnvironmentNameToRoleInstanceAddingDate(telemetry);
        SetAMachine(telemetry);
    }

    private void SetApplicationNameToRoleName(ITelemetry telemetry)
    {
        telemetry.Context.GlobalProperties["MachineName"] = Environment.MachineName;
    }

    private void SetAMachine(ITelemetry telemetry)
    {
        var appName = _configuration.GetValue<string>("AppSettings:ApplicationName");
        if (string.IsNullOrEmpty(appName)) return;
        telemetry.Context.Cloud.RoleName = appName;
    }

    private void SetEnvironmentNameToRoleInstanceAddingDate(ITelemetry telemetry)
    {
        var environmentName = _configuration.GetValue<string>("AppSettings:Environment");
        var dc = _configuration.GetValue<string>("AppSettings:WarehouseNumber");
        if (string.IsNullOrEmpty(environmentName) || string.IsNullOrEmpty(dc)) return;
        telemetry.Context.Cloud.RoleInstance = $"{environmentName}-{dc}";
    }
}

Program.cs注册代码:

builder.Services.AddLogging(builder.Configuration);

问题:期望所有未处理异常都能被记录至Application Insights,但编排API调用其他API超时抛出的异常未被记录。调整AddControllers注册顺序后,能看到异常发生前的日志,但异常本身仍未被捕获记录。


解决方案

1. 确保全局异常中间件正确记录异常

如果项目使用了自定义全局异常处理中间件,必须在捕获异常后明确记录日志,避免吞掉异常导致Application Insights无法捕获:

public class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<ExceptionHandlingMiddleware> _logger;

    public ExceptionHandlingMiddleware(RequestDelegate next, ILogger<ExceptionHandlingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            // 强制记录异常日志,Application Insights会捕获这条日志
            _logger.LogError(ex, "未处理异常");
            // 后续响应处理逻辑...
        }
    }
}

2. 修正Application Insights注册逻辑

当前代码中存在重复调用AddApplicationInsightsTelemetry()的问题,会导致配置冲突。修改扩展方法,合并注册逻辑并明确开启异常跟踪:

public static IServiceCollection AddLogging(this IServiceCollection services, IConfiguration configuration)
{
    // 确保HttpContextAccessor已注册,自定义初始化器依赖它
    services.AddHttpContextAccessor();

    return services.AddLogging(logging =>
        {
            logging.ClearProviders();
            logging.AddConsole();
            logging.AddEventLog(config => { config.SourceName = Assembly.GetExecutingAssembly().FullName; });
            logging.AddConfiguration(configuration.GetSection("Logging"));
            logging.AddApplicationInsights(
                config =>
                {
                    config.ConnectionString = configuration.GetSection("Logging:ApplicationInsights:ConnectionString").Value;
                    config.EnableAdaptiveSampling = true;
                },
                options => 
                { 
                    options.FlushOnDispose = true;
                    options.TrackExceptions = true; // 明确开启异常跟踪
                });
            logging.AddDebug();
        })
        .SetupStorageFolderForTelemetryChannel(configuration);
}

private static IServiceCollection SetupStorageFolderForTelemetryChannel(this IServiceCollection services,
    IConfiguration configuration)
{
    services.AddSingleton<ITelemetryInitializer, CustomTelemetryInitializer>();
    var configuredStorageFolder = configuration.GetValue<string>("ApplicationInsights:StorageFolder");
    if (string.IsNullOrEmpty(configuredStorageFolder))
        return services; // 不再重复调用AddApplicationInsightsTelemetry

    var baseFolder = AppContext.BaseDirectory;
    var storageFolder = Path.Combine(baseFolder, configuredStorageFolder);

    var f = new DirectoryInfo(storageFolder);
    if (!f.Exists) f.Create();
    
    return services.AddSingleton(typeof(ITelemetryChannel),
            new ServerTelemetryChannel { StorageFolder = storageFolder });
}

3. 调整日志级别配置

确保Application Insights能捕获Error级别的日志(异常默认以Error级别记录),修改配置文件:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    },
    "ApplicationInsights": {
      "LogLevel": {
        "Default": "Information",
        "Microsoft": "Error",
        "MyApplication": "Error" // 添加项目命名空间的Error级别日志捕获
      },
      "ConnectionString": "InstrumentationKey"
    }
  }
}

4. 验证异步代码的异常处理

超时异常通常以TaskCanceledException形式抛出,确保异步方法没有使用async void(这类方法的异常无法被全局捕获),且异常没有被静默处理:

// 正确示例:返回Task的异步方法
public async Task CallExternalApi()
{
    try
    {
        await _httpClient.GetAsync("https://external-api.example.com");
    }
    catch (TaskCanceledException ex)
    {
        _logger.LogError(ex, "调用外部API超时");
        throw; // 重新抛出异常,让全局捕获机制处理
    }
}

5. 明确启用异常跟踪

在Program.cs中补充配置,确保Application Insights的异常跟踪功能开启:

builder.Services.AddApplicationInsightsTelemetry(options =>
{
    options.EnableExceptionTracking = true;
});

内容的提问来源于stack exchange,提问作者Harish Ninge Gowda

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 01:25:55