我们拥有具有多个协调器和活动的持久功能。我们希望使用中间件作为全局异常处理程序,而不是在每个协调器/活动中处理它。我们可以在中间件中捕获错误,但在耐用功能监视器中实例为绿色。我们希望将其变为红色并将错误文本发送到监视器。有什么办法可以达到这个目的吗?
编辑:添加代码
internal sealed class ErrorHandlerMiddleware(ILogger<ErrorHandlerMiddleware> logger) : IFunctionsWorkerMiddleware {
private readonly ILogger<ErrorHandlerMiddleware> _logger = logger ?? throw new ArgumentNullException(nameof(logger));
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
try
{
await next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, ex.Message);
// How to call DurableOrchestrationContextBase.SetCustomStatus from here?
}
}
}
我使用下面的代码使用中间件作为全局异常处理程序来处理异常。
GlobalExceptionHandlerMiddleware.cs:
public class GlobalExceptionHandlerMiddleware : IFunctionsWorkerMiddleware
{
private readonly ILogger _logger;
public GlobalExceptionHandlerMiddleware(ILogger<GlobalExceptionHandlerMiddleware> logger)
{
_logger = logger;
}
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
try
{
// Executes the orchestration function
await next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "An unhandled exception occurred.");
// you can re-throw the exception to handle it elsewhere(or)mark it as failed.
throw;
}
}
}
程序.cs:
var builder = FunctionsApplication.CreateBuilder(args);
builder.ConfigureFunctionsWebApplication();
// Register exception handler middleware.
builder.UseMiddleware<GlobalExceptionHandlerMiddleware>();
builder.Build().Run();
函数.cs:
public static class Function1
{
[Function(nameof(Function1))]
public static async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
var logger = context.CreateReplaySafeLogger(nameof(Function1));
var outputs = new List<string>();
try
{
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Tokyo"));
context.SetCustomStatus("Tokyo");
throw new Exception("Something went wrong in the orchestration.");
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "Seattle"));
context.SetCustomStatus("Seattle");
outputs.Add(await context.CallActivityAsync<string>(nameof(SayHello), "London"));
context.SetCustomStatus("London");
return outputs;
}
catch (Exception ex)
{
logger.LogError($"Orchestrator failed: {ex.Message}");
context.SetCustomStatus($"Error: {ex.Message}");
// Rethrow the orchestration to mark it as failed
throw new Exception("Orchestrator execution failed.", ex);
}
}
[Function(nameof(SayHello))]
public static string SayHello([ActivityTrigger] string name, FunctionContext executionContext)
{
var logger = executionContext.GetLogger("SayHello");
logger.LogInformation("Saying hello to {name}.", name);
return $"Hello {name}!";
}
[Function("Function1_HttpStart")]
public static async Task<HttpResponseData> HttpStart(
[HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
FunctionContext executionContext)
{
var logger = executionContext.GetLogger("Function1_HttpStart");
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(nameof(Function1));
logger.LogInformation("Started orchestration with ID = '{instanceId}'.", instanceId);
return await client.CreateCheckStatusResponseAsync(req, instanceId);
}
输出:
Functions:
Function1_HttpStart: [GET,POST] http://localhost:7182/api/Function1_HttpStart
Function1: orchestrationTrigger
SayHello: activityTrigger
For detailed output, run func with --verbose flag.
[2024-12-02T09:52:41.939Z] Host lock lease acquired by instance ID '000000000000000000000000F72731CC'.
[2024-12-02T09:52:43.675Z] Executing 'Functions.Function1_HttpStart' (Reason='This function was programmatically called via the host APIs.', Id=e618a276-60b9-4feb-84ce-3db17390ca3c)
[2024-12-02T09:52:59.923Z] Scheduling new Function1 orchestration with instance ID '0958069fc24c4678b7486bf01fc6b995' and 0 bytes of input data.
[2024-12-02T09:53:00.275Z] Started orchestration with ID = '0958069fc24c4678b7486bf01fc6b995'.
[2024-12-02T09:53:00.398Z] Executed 'Functions.Function1_HttpStart' (Succeeded, Id=e618a276-60b9-4feb-84ce-3db17390ca3c, Duration=16761ms)
[2024-12-02T09:53:00.438Z] Executing 'Functions.Function1' (Reason='(null)', Id=34b6427d-ccfa-4c8b-8b0e-49dd33e86521)
[2024-12-02T09:53:06.458Z] Executed 'Functions.Function1' (Succeeded, Id=34b6427d-ccfa-4c8b-8b0e-49dd33e86521, Duration=6070ms)
[2024-12-02T09:53:06.551Z] Executing 'Functions.SayHello' (Reason='(null)', Id=2f65ef8f-bba4-4eca-9c9c-f5c3884c462a)
[2024-12-02T09:53:08.907Z] Saying hello to Tokyo.
[2024-12-02T09:53:08.913Z] Executed 'Functions.SayHello' (Succeeded, Id=2f65ef8f-bba4-4eca-9c9c-f5c3884c462a, Duration=2369ms)
[2024-12-02T09:53:08.985Z] Executing 'Functions.Function1' (Reason='(null)', Id=4a739a12-8f60-4b48-b24d-1699a98254b4)
[2024-12-02T09:53:10.430Z] Orchestrator failed: Something went wrong in the orchestration.
[2024-12-02T09:53:10.704Z] An unhandled exception occurred.
[2024-12-02T09:53:10.708Z] Result: An unhandled exception occurred.
Exception: System.Exception: Orchestrator execution failed.
[2024-12-02T09:53:10.712Z] ---> System.Exception: Something went wrong in the orchestration.
[2024-12-02T09:53:10.715Z] at FunctionApp11.Function1.RunOrchestrator(TaskOrchestrationContext context) in C:\Users\uname\Source\Repos\FunctionApp11\Function1.cs:line 25
[2024-12-02T09:53:10.719Z] --- End of inner exception stack trace ---
[2024-12-02T09:53:10.723Z] at FunctionApp11.Function1.RunOrchestrator(TaskOrchestrationContext context) in C:\Users\uname\Source\Repos\FunctionApp11\Function1.cs:line 42
//Removed few logs
[2024-12-02T09:53:10.939Z] at Microsoft.Azure.WebJobs.Host.Executors.FunctionExecutor.ExecuteWithLoggingAsync(IFunctionInstanceEx instance, FunctionStartedMessage message, FunctionInstanceLogEntry instanceLogEntry, ParameterHelper parameterHelper, ILogger logger, CancellationToken cancellationToken) in D:\a\_work\1\s\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionExecutor.cs:line 306. IsReplay: False. State: Failed. RuntimeStatus: Failed. HubName: TestHubName. AppName: . SlotName: . ExtensionVersion: 2.13.7. SequenceNum er: 8. TaskEventId: -1
功能应用程序的应用洞察: