Подтвердить что ты не робот

Как связать исключения с запросами в Application Insights on Azure?

Мы используем Owin на Azure для службы 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));
}

Это работает, за исключением исключений и запросов. Оба регистрируются, но при нажатии на неудавшийся запрос говорится: "Не найдено никаких связанных исключений". И наоборот, при открытии свойств исключения мы можем прочитать "Запросы, затронутые этим исключением: 0". Каков правильный способ сделать это?

4b9b3361

Ответ 1

Application Insights связывает исключения и запросы, сравнивая ExceptionTelemetry.Context.Operation.Id и RequestTelemetry.Id.

У меня нет образца кода для OWIN, однако ASP.NET 5 пакет в SDK Application Insights имеет похожие компоненты промежуточного программного обеспечения для отслеживания исключений и запросов. Надеюсь, вы сможете использовать эту информацию для создания решения для OWIN.

Мы создаем экземпляр RequestTelemetry и сохраняем его в среде обработки запроса перед вызовом следующего компонента промежуточного программного обеспечения, который выполняет обработку фактического запроса. В ASP.NET 5 мы регистрируем RequestTelemetry как службу с запросом. С OWIN я бы предположил, что ваш компонент промежуточного программного обеспечения создаст его и сохранит его в словаре environment.

У нас также есть ITelemetryInitializer, называемый OperationIdTelemetryInitializer, который устанавливает ITelemetry.Context.Operation.Id с RequestTelemetry.Id, извлеченным из среды. Этот инициализатор необходимо добавить к TelemetryConfiguration, используемому для создания экземпляров TelemetryClient в вашем приложении. TelemetryConfiguration.Active используется по умолчанию.

Ответ 2

Что я закончил:

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);
    }
}

Ответ 3

Для моего текущего клиента мы еще не OWIN-ed вверх.

Я зарегистрировал DelegatingHandler с помощью WebAPI, который вставляет текущий запрос в поток SynchronizationContext через CallContext.LogicalSetData и удаляет его после завершения запроса.

В моей существующей системе ведения журнала, которая должна была быть обновлена ​​с помощью материалов Application Insights, я затем беру запрос из потока через CallContext.LogicalSetData и устанавливаю для получения HttpContext, который помещается в свойства запроса по рамки, затем из HttpContext.Items, я получаю экземпляр RequestTelemetry.

В конечном счете, это все необходимо, потому что я не могу получить доступ к запросу или действию или что-либо из контейнера IoC, который обновляет службы.

В конце концов мы можем переписать часть этого, чтобы улучшить создание и поток объекта стиля OperationContext или InstrumentationContext вниз по стеку и избавиться от обработчика и CallContext смешной бизнес.