.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();
异常截图
解决方案
该问题源于独立进程模式下,Functions Runtime会将函数抛出的异常包装为RpcException,导致App Insights捕获外层异常而非自定义异常。可通过以下两种方式解决:
方法一:自定义Functions中间件捕获并处理异常
创建中间件捕获原始自定义异常,直接上报并按需处理响应:
- 编写异常处理中间件:
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; // 非自定义异常保持原有抛出逻辑 } } } }
- 在Program.cs中注册中间件:
修改ConfigureFunctionsWorkerDefaults配置,添加中间件:
var host = new HostBuilder() .ConfigureFunctionsWorkerDefaults(builder => { builder.UseMiddleware<ExceptionHandlingMiddleware>(); }) // 保留原有其他配置 .Build();
方法二:自定义TelemetryInitializer修正上报异常
直接修改App Insights的Telemetry数据,将外层RpcException替换为原始自定义异常:
- 编写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); } } } }
- 在Program.cs中注册初始化器:
在ConfigureServices中添加:
services.AddApplicationInsightsTelemetryWorkerService(); services.ConfigureFunctionsApplicationInsights(); // 注册自定义Telemetry初始化器 services.AddSingleton<ITelemetryInitializer, ExceptionTelemetryInitializer>(); // 保留原有其他配置
额外注意事项
- 确保
AppUserException标记[Serializable],避免序列化问题。 - 中间件方式需适配不同触发器类型(如Timer触发器无需HTTP响应处理)。
- 可结合两种方式,同时实现响应处理和Telemetry修正,提升监控清晰度。
内容的提问来源于stack exchange,提问作者J Flex
相关产品推荐
相关产品推荐

