如何 link Azure 上的 Application Insights 中的请求例外?

How to link exceptions to requests in Application Insights on Azure?

我们在 Azure 上使用 Owin 进行 REST 服务,并且必须直接向 Application Insights 报告。我们想要记录异常和请求。现在我们有这个:

using AppFunc = Func<IDictionary<string, object>, Task>;
public class InsightsReportMiddleware
{

    readonly AppFunc next;
    readonly TelemetryClient telemetryClient;

    public InsightsReportMiddleware(AppFunc next, TelemetryClient telemetryClient)
    {
        if (next == null)
        {
            throw new ArgumentNullException("next");
        }

        this.telemetryClient = telemetryClient;
        this.next = next;
    }

    public async Task Invoke(IDictionary<string, object> environment)
    {
        var sw = new Stopwatch();
        sw.Start();

        await next(environment);
        sw.Stop();

        var ctx = new OwinContext(environment);
        var rt = new RequestTelemetry(
            name: ctx.Request.Path.ToString(),
            timestamp: DateTimeOffset.Now,
            duration: sw.Elapsed,
            responseCode: ctx.Response.StatusCode.ToString(),
            success: 200 == ctx.Response.StatusCode
            );

        rt.Url = ctx.Request.Uri;
        rt.HttpMethod = ctx.Request.Method;
        telemetryClient.TrackRequest(rt);
    }
}


public class InsightsExceptionLogger : ExceptionLogger
{
    readonly TelemetryClient telemetryClient;

    public InsightsExceptionLogger(TelemetryClient telemetryClient)
    {
        this.telemetryClient = telemetryClient;            
    }

    public override Task LogAsync(ExceptionLoggerContext context, System.Threading.CancellationToken cancellationToken)
    {
        telemetryClient.TrackException(context.Exception);
        return Task.FromResult<object>(null);
    }

    public override void Log(ExceptionLoggerContext context)
    {
        telemetryClient.TrackException(context.Exception);
    }
}

他们像这样注册到我们的应用程序中:

static void ConfigureInsights(IAppBuilder app, HttpConfiguration config)
{
    var rtClient = new TelemetryClient();
    app.Use<InsightsReportMiddleware>(rtClient);
    config.Services.Add(typeof (IExceptionLogger), new InsightsExceptionLogger(rtClient));
}

这有效,除了异常和请求未连接。两者都会被记录下来,但是当点击一个失败的请求时,它会显示 "No related exceptions were found"。反之,当打开异常属性时,我们可以读到"Requests affected by this exception: 0"。 这样做的正确方法是什么?

Application Insights 通过比较 ExceptionTelemetry.Context.Operation.IdRequestTelemetry.Id 来链接异常和请求。

我没有 OWIN 的代码示例,但是 Application Insights SDK 的 ASP.NET 5 package 具有类似的中间件组件,用于跟踪异常和请求。我希望您可以使用这些信息为 OWIN 构建解决方案。

我们创建一个 RequestTelemetry 实例并将其存储在请求处理环境中,然后调用下一个执行实际请求处理的中间件组件。在 ASP.NET 5 中,我们将 RequestTelemetry 注册为请求范围的服务。使用 OWIN,我想您的中间件组件会创建它并将其存储在 environment 字典中。

我们还有一个名为 OperationIdTelemetryInitializerITelemetryInitializer,它将 ITelemetry.Context.Operation.Id 设置为从环境中提取的 RequestTelemetry.Id。此初始化程序需要添加到 TelemetryConfiguration 中,用于在您的应用程序中创建 TelemetryClient 实例。默认使用 TelemetryConfiguration.Active

我最后做了什么:

using AppFunc = Func<IDictionary<string, object>, Task>;
public class InsightsReportMiddleware
{
    readonly AppFunc next;
    readonly TelemetryClient telemetryClient;

    public InsightsReportMiddleware(AppFunc next, TelemetryClient telemetryClient)
    {
        if (next == null)
        {
            throw new ArgumentNullException("next");
        }

        this.telemetryClient = telemetryClient;
        this.next = next;
    }

    public async Task Invoke(IDictionary<string, object> environment)
    {
        var ctx = new OwinContext(environment);
        var rt = new RequestTelemetry()
        {
            Url = ctx.Request.Uri,
            HttpMethod = ctx.Request.Method,
            Name = ctx.Request.Path.ToString(),
            Timestamp = DateTimeOffset.Now
        };
        environment.Add("requestTelemetry", rt);

        var sw = new Stopwatch();
        sw.Start();
        await next(environment);
        sw.Stop();

        rt.ResponseCode = ctx.Response.StatusCode.ToString();
        rt.Success = ctx.Response.StatusCode < 400;
        rt.Duration = sw.Elapsed;
        telemetryClient.TrackRequest(rt);
    }
}

public class InsightsExceptionLogger : ExceptionLogger
{
    readonly TelemetryClient telemetryClient;

    public InsightsExceptionLogger(TelemetryClient telemetryClient)
    {
        this.telemetryClient = telemetryClient;            
    }

    public override Task LogAsync(ExceptionLoggerContext context, System.Threading.CancellationToken cancellationToken)
    {
        var owinContext = context.Request.GetOwinEnvironment();
        ExceptionTelemetry exceptionTelemetry = null;
        if (owinContext != null)
        {
            object obj;
            if (owinContext.TryGetValue("requestTelemetry", out obj))
            {
                var requestTelemetry = obj as RequestTelemetry;
                exceptionTelemetry = new ExceptionTelemetry(context.Exception)
                {
                    Timestamp = DateTimeOffset.Now
                };
                exceptionTelemetry.Context.Operation.Id = requestTelemetry.Id;
            }
        }

        if (exceptionTelemetry != null)
        {
            telemetryClient.TrackException(exceptionTelemetry);
        }
        else
        {
            telemetryClient.TrackException(context.Exception);                
        }

        return Task.FromResult<object>(null);
    }

    public override void Log(ExceptionLoggerContext context)
    {
        telemetryClient.TrackException(context.Exception);
    }
}

对于我当前的客户,我们还没有 OWIN-ed。

我用 WebAPI 注册了一个 DelegatingHandler,它通过 CallContext.LogicalSetData 将当前请求粘贴到线程的 SynchronizationContext 中,并在请求完成后将其删除。

在我现有的日志系统中,它必须用 Application Insights 的东西进行改造,然后我通过 CallContext.LogicalSetData 从线程中获取请求并开始获取 HttpContext 这是由框架放入请求属性中,然后从 HttpContext.Items,我得到 RequestTelemetry 实例。

最终,这一切都是必需的,因为我无法从正在更新服务的 IoC 容器访问请求或操作或任何内容。

最终我们可能会重写其中的一些内容,以改进 OperationContextInstrumentationContext 样式对象在堆栈中的创建和流动,并摆脱处理程序和 CallContext 有趣的业务.

存在接受属性字典的 TelemetryClient.TrackException 方法的重载。它专门用于对异常进行分类和搜索。这允许生成一个错误 ID,并且 link 错误到 AppInsights。

错误处理示例:

var errorId = GenerateErrorId();

var trackProperties = new Dictionary<string, string>();
trackProperties.Add("ErrorId", errorId);

var ai = new TelemetryClient();
ai.TrackException(exception, trackProperties);

JObject resp = new JObject();
resp["message"] = exception.Message + " - " + errorId;

await context.Response.WriteAsync(resp.ToString());