停止由 CancellationToken 生成的异常,使其不再被 ApplicationInsights 报告

Stop Exception generated by CancellationToken from getting Reported by ApplicationInsights

认为 在我的控制器中使用 CancellationToken 是个好主意,如下所示:

[HttpGet("things", Name = "GetSomething")]
public async Task<ActionResult> GetSomethingAsync(CancellationToken ct) {
    var result = await someSvc.LoadSomethingAsync(ct);
    return Ok(result);
}

问题是现在 Azure Application Insights 显示了一堆 System.Threading.Tasks.TaskCancelledException: A Task was canceled. 类型的异常以及偶尔的 Npgsql.PostgresException: 57014: canceling statement due to user request。这是我不需要的噪音。

Application Insights 已使用标准方法注册为服务 - services.AddApplicationInsightsTelemetry(Configuration);

尝试的解决方案

我想我可以插入应用程序管道并捕获上述异常,将它们转换为正常响应。我把下面的代码放在不同的地方。它捕获任何异常,如果 IsCancellationRequested,它 returns 一个虚拟响应。否则它会重新抛出捕获的异常。

app.Use(async (ctx, next) =>
{
    try { await next(); }
    catch (Exception ex)
    {
        if (ctx.RequestAborted.IsCancellationRequested)
        {       
            ctx.Response.StatusCode = StatusCodes.Status418ImATeapot;
        }
        else { throw; }
    }
});

这段代码的作用在于它改变了响应。但是,异常仍会发送到 Application Insights。

要求

结论

我希望我了解报告异常的 Application Insights 机制。在试验之后,我觉得尝试在应用程序管道中捕获错误并不是正确的方法。只是不知道是什么。

您尝试过创建 ExceptionFilter 吗?

public class MyExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        if (context?.Exception.Message == "Did it catch this?")
        {
            context.Result = new BadRequestObjectResult("You have no access.");
        }
    }
}

ConfigureServices 方法内部:

services.AddMvc(config =>
        {
            config.Filters.Add(typeof(MyExceptionFilter));
        })

过滤器中的逻辑只是给您一个示例,说明您希望如何过滤某些内容并传播结果。在您的情况下,我猜您需要以某种方式吞下与上下文相关的异常,以便之后不会发现异常。

也许简单地将它赋值给 null 就可以了?

我用 TelemetryProcessor 解决了这个问题。

为了在处理器中访问 RequestAborted.IsCancellationRequested,需要通过调用使 HttpContext 服务可用services.AddHttpContextAccessor()Startup.ConfigureServices.

在处理器中,如果 IsCancellationRequested 为真,我会阻止处理任何异常遥测。下面是我最终解决方案的一个简化示例,因为最终我意识到我需要对超出我原始请求范围的依赖项和请求做一些更细微的工作。

internal class AbortedRequestTelemetryProcessor : ITelemetryProcessor
{
    private readonly IHttpContextAccessor httpContextAccessor;
    private readonly ITelemetryProcessor next;

    public AbortedRequestTelemetryProcessor(
        ITelemetryProcessor next, IHttpContextAccessor httpContextAccessor)
    {
        this.httpContextAccessor = httpContextAccessor;
        this.next = next;
    }

    public void Process(ITelemetry item)
    {
        if (httpContextAccessor.HttpContext?.RequestAborted.IsCancellationRequested == true 
            && item is ExceptionTelemetry)
        {
            // skip exception telemetry if cancellation was requested
            return; 
        }
        // Send everything else:
        next.Process(item);
    }
}