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

.NET 7独立进程Azure Functions V4异常显示为RpcException

问题描述

在独立进程模式下运行基于.NET 7的Azure Functions(V4)时,所有异常在Azure Application Insights中均显示为RpcException。函数执行逻辑被包裹在“Invoke”逻辑中,显式抛出的AppUserException会被封装为RpcException上报,导致监控数据不清晰。

环境与代码信息

包引用

<PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.21.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.18.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.ApplicationInsights" Version="1.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Storage" Version="6.0.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Timer" Version="4.2.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.13.0" />

测试函数代码

namespace FunctionApp1
{
    public class Function1
    {
        private readonly ILogger _logger;

        public Function1(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<Function1>();
        }

        [Function("Function1")]
        public HttpResponseData Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req)
        {
            _logger.LogInformation("C# HTTP trigger function processed a request.");

            throw new AppUserException("usererror");
        }
    }
}

Program.cs代码

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureAppConfiguration(builder =>
    {
        // KeyVault
        var keyVaultEndpoint = GetKeyVaultEndpoint();
        if (string.IsNullOrWhiteSpace(keyVaultEndpoint))
        {
            throw new Exception("keyVaultEndpoint wasn't provided.");
        }
        var credentials = new DefaultAzureCredential();
        builder.AddAzureKeyVault(new Uri(keyVaultEndpoint), credentials, new KeyVaultSecretManager());

        // Azure App Config
        var azureAppConfigEndpoint = GetAzureAppConfigEndpoint();
        if (string.IsNullOrWhiteSpace(azureAppConfigEndpoint))
        {
            throw new Exception("azureAppConfigEndpoint wasn't provided.");
        }
        builder.AddAzureAppConfiguration(options =>
        {
            options.Connect(azureAppConfigEndpoint)
                .UseFeatureFlags(options =>
                {
                    options.CacheExpirationInterval = TimeSpan.FromSeconds(60);
                });
        });
        builder.AddJsonFile("appsettings.json", optional: true);
    })
    .ConfigureServices(services =>
    {
        services.AddApplicationInsightsTelemetryWorkerService();
        services.ConfigureFunctionsApplicationInsights();
        services.Configure<LoggerFilterOptions>(options =>
        {
            var toRemove = options.Rules.FirstOrDefault(rule => rule.ProviderName
                == "Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider");

            if (toRemove is not null)
            {
                options.Rules.Remove(toRemove);
            }
        });
        services.AddSolutionServices();
    })
    .ConfigureLogging((hostingContext, logging) =>
    {
        // Make sure the configuration of the appsettings.json file is picked up.
        logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
    })
    .Build();
static string? GetKeyVaultEndpoint() => Environment.GetEnvironmentVariable("ASPNETCORE_HOSTINGSTARTUP__KEYVAULT__CONFIGURATIONVAULT");
static string? GetAzureAppConfigEndpoint() => Environment.GetEnvironmentVariable("AZURE_APPCONFIG_ENDPOINT");

host.Run();

异常截图

异常显示1
异常显示2


解决方案

该问题源于独立进程模式下,Functions Runtime会将函数抛出的异常包装为RpcException,导致App Insights捕获外层异常而非自定义异常。可通过以下两种方式解决:

方法一:自定义Functions中间件捕获并处理异常

创建中间件捕获原始自定义异常,直接上报并按需处理响应:

  1. 编写异常处理中间件:
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Middleware;
using Microsoft.Extensions.Logging;

public class ExceptionHandlingMiddleware : IFunctionsWorkerMiddleware
{
    private readonly ILogger<ExceptionHandlingMiddleware> _logger;

    public ExceptionHandlingMiddleware(ILogger<ExceptionHandlingMiddleware> logger)
    {
        _logger = logger;
    }

    public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
    {
        try
        {
            await next(context);
        }
        catch (Exception ex)
        {
            var originalException = ex.GetBaseException();
            
            if (originalException is AppUserException appEx)
            {
                // 直接记录自定义异常到App Insights
                _logger.LogError(appEx, "用户自定义异常: {Message}", appEx.Message);
                
                // 针对HTTP触发器返回自定义响应(可选)
                if (context.GetHttpRequestData() is HttpRequestData req)
                {
                    var response = req.CreateResponse(System.Net.HttpStatusCode.BadRequest);
                    await response.WriteStringAsync(appEx.Message);
                    context.GetInvocationResult().Value = response;
                }
            }
            else
            {
                _logger.LogError(ex, "未处理异常");
                throw; // 非自定义异常保持原有抛出逻辑
            }
        }
    }
}
  1. 在Program.cs中注册中间件:
    修改ConfigureFunctionsWorkerDefaults配置,添加中间件:
var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults(builder =>
    {
        builder.UseMiddleware<ExceptionHandlingMiddleware>();
    })
    // 保留原有其他配置
    .Build();

方法二:自定义TelemetryInitializer修正上报异常

直接修改App Insights的Telemetry数据,将外层RpcException替换为原始自定义异常:

  1. 编写Telemetry初始化器:
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;

public class ExceptionTelemetryInitializer : ITelemetryInitializer
{
    public void Initialize(ITelemetry telemetry)
    {
        if (telemetry is ExceptionTelemetry exceptionTelemetry)
        {
            var originalException = exceptionTelemetry.Exception?.GetBaseException();
            
            if (originalException is AppUserException appEx)
            {
                // 替换异常信息为自定义异常
                exceptionTelemetry.Exception = appEx;
                exceptionTelemetry.Message = appEx.Message;
                // 添加自定义属性方便筛选
                exceptionTelemetry.Properties["ExceptionType"] = nameof(AppUserException);
            }
        }
    }
}
  1. 在Program.cs中注册初始化器:
    在ConfigureServices中添加:
services.AddApplicationInsightsTelemetryWorkerService();
services.ConfigureFunctionsApplicationInsights();
// 注册自定义Telemetry初始化器
services.AddSingleton<ITelemetryInitializer, ExceptionTelemetryInitializer>();
// 保留原有其他配置

额外注意事项

  • 确保AppUserException标记[Serializable],避免序列化问题。
  • 中间件方式需适配不同触发器类型(如Timer触发器无需HTTP响应处理)。
  • 可结合两种方式,同时实现响应处理和Telemetry修正,提升监控清晰度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 10:43:11