在 Azure 函数应用程序中最顶层的协调器中获取原始错误消息和堆栈跟踪(隔离模式)

问题描述 投票:0回答:1

我有一个Azure函数应用程序(NET8.0,隔离模式,核心工具版本4.0.5801,函数运行时版本:4.34.1.22669)。在撰写此问题时,所有软件包都是最新的。 有许多编排和活动功能,以及一个启动调用链的“主”编排器。该协调器还应该能够捕获异常并提供原始错误消息和堆栈跟踪。演示代码:

[Function(nameof(MainOrchestrator))]
public static async Task MainOrchestrator(
    [OrchestrationTrigger] TaskOrchestrationContext context, FunctionContext executionContext)
{
    string[] cities = ["Seattle", "London"];
    try
    {
        foreach (var city in cities)
        {
            await context.CallSubOrchestratorAsync(nameof(CityOrchestrator), city);
        }
    }
    catch (TaskFailedException ex)
    {
        // inspect the exception here
    }

}

[Function(nameof(CityOrchestrator))]
public static async Task CityOrchestrator(
    [OrchestrationTrigger] TaskOrchestrationContext context, string city, FunctionContext executionContext)
{
    string[] postfixes = ["a", "b"];

    foreach (var postfix in postfixes)
    {
        await context.CallActivityAsync(nameof(SayHello), input: $"{city}-{postfix}");
    }
}

[Function(nameof(SayHello))]
public static async Task SayHello([ActivityTrigger] string cityName, FunctionContext executionContext)
{
    await Task.Delay(100);
    if (cityName == "London-b") throw new Exception("Fire in London!");
}

[Function("Orchestration_HttpStart")]
public static async Task<HttpResponseData> HttpStart(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
    [DurableClient] DurableTaskClient client,
    FunctionContext executionContext)
{
    string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
            nameof(MainOrchestrator));
    return await client.CreateCheckStatusResponseAsync(req, instanceId);
}

错误发生在“SayHello”活动中。如果我在“CityOrchestrator”中捕获它,那么我可以从“FailureDetails”属性中获取我需要的内容。但如果我在主协调器中这样做,那么失败详细信息会向我显示消息“任务‘SayHello’(#1)因未处理的异常而失败:伦敦着火了!” (好吧,它接近原始)以及与执行框架相关的堆栈跟踪:

在 Microsoft.DurableTask.Worker.Shims.TaskOrchestrationContextWrapper.CallActivityAsync[T](TaskName 名称、对象输入、TaskOptions 选项) 在C中的AzureBackground.Orchestration.CityOrchestrator(TaskOrchestrationContext上下文,字符串城市,FunctionContextexecutionContext):b_projekt \ DociaBackground \ AzureBackgroundTasks \ AzureBackground \ Orchestration.cs:第42行 在 C: b_projekt\DociaBackground\AzureBackgroundTasks\AzureBackground\obj\Debug 中的 AzureBackground.DirectFunctionExecutor.ExecuteAsync(FunctionContext context) et8.0\Microsoft.Azure.Functions.Worker.Sdk.Generators\Microsoft.Azure.Functions.Worker.Sdk.Generators.FunctionExecutorGenerator\GenerateFunctionExecutor.g.cs:第 70 行 在 D: _work \s\src\DotNetWorker.Core\OutputBindings\OutputBindingsMiddleware.cs 中的 Microsoft.Azure.Functions.Worker.OutputBindings.OutputBindingsMiddleware.Invoke(FunctionContext context,FunctionExecutionDelegate next):第 13 行 在 /mnt/vss/work/1/s/extensions/Worker.Extensions.Http.AspNetCore/src/ 中的 Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.FunctionsHttpProxyingMiddleware.Invoke(FunctionContext context,FunctionExecutionDelegate next) FunctionsMiddleware/FunctionsHttpProxyingMiddleware.cs:第 38 行 在 //src/Worker.Extensions.DurableTask/FunctionsOrchestrator.cs 中的 Microsoft.Azure.Functions.Worker.Extensions.DurableTask.FunctionsOrchestrator.EnsureSynchronousExecution(FunctionContext functionContext、FunctionExecutionDelegate next、FunctionsOrchestrationContext OrchestrationContext):第 81 行 在 /_/src/Worker.Extensions.DurableTask/FunctionsOrchestrator.cs 中的 Microsoft.Azure.Functions.Worker.Extensions.DurableTask.FunctionsOrchestrator.RunAsync(TaskOrchestrationContext 上下文,对象输入):第 51 行 在 Microsoft.DurableTask.Worker.Shims.TaskOrchestrationShim.Execute(OrchestrationContext innerContext,String rawInput)

原始堆栈跟踪对于业务逻辑非常重要,我应该能够以某种方式在代码中获取它。我怎样才能做到这一点? 预先感谢您!

c# azure azure-functions .net-8.0 azure-functions-isolated
1个回答
0
投票

由于无法以优雅的方式解决问题,我开发了一个基于中间件类的解决方案。不是美丽,而是解决我的问题。我分享给遇到同样问题的人。 首先我在Program.cs中添加一个用于活动函数的中间件:

var host = new HostBuilder()
.ConfigureFunctionsWebApplication(app =>
{
    app.UseWhen<ErrorMiddleware>(context =>
    {
        var triggerType = context.FunctionDefinition.InputBindings.Values.First(a => a.Type.EndsWith("Trigger")).Type;
        return triggerType == "activityTrigger";
    });

})
.ConfigureServices(services =>
{
    services.AddSingleton<IErrorStorage>(s =>
    {
        return new ErrorStorageTable(Environment.GetEnvironmentVariable("AzureWebJobsStorage"), "MyErrorRegistry");
    });
});

中间件捕获异常并将其详细信息存储到配置的存储(在我的例子中为 Azure 表存储)中,并使用唯一的相关 ID。然后根据此 Id 构建一个标记,并使用该标记作为消息抛出另一个错误。

internal class ErrorMiddleware : IFunctionsWorkerMiddleware
{
    private readonly ILogger<ErrorMiddleware> _logger;
    private readonly IErrorStorage _errorStorage;

    public ErrorMiddleware(ILogger<ErrorMiddleware> logger, IErrorStorage errorStorage)
    {
        _logger = logger;
        _errorStorage = errorStorage;
    }


    public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
    {
        try
        {
            await next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Exception in {n}", context.FunctionDefinition.Name);

            // Registering exception
            ErrorDetails details = ErrorDetails.FromException(ex);
            ErrorCorrelationId id = ErrorCorrelationId.NewId();
            await _errorStorage.RegisterErrorAsync(id, details);
            throw new Exception(ExceptionParser.CreateMarker(id), ex);
        }

    }
}

最后,主编排中有一个 try-catch 块。如果出现异常,其消息可能包含带有 Id 的相关标记。在这种情况下,我可以从存储中读取错误详细信息。

try
{
    job.ReturnValue = 
        await context.CallSubOrchestratorAsync<string?>(taskName, input: job);
}
catch (Exception ex)
{
    if(ExceptionParser.TryGetCorrelationId(ex, out var correlationId))
    {
        job.ErrorCorrelationId = correlationId;
    }
    else
    {
        job.ErrorDetails = ErrorDetails.FromException(ex);
    }
}

我确信我的解决方案并不完美,但这总比没有好。希望它能为其他人节省一些时间。

© www.soinside.com 2019 - 2024. All rights reserved.