Просмотр тела запроса POST в Application Insights

можно ли просмотреть тело запроса POST в Application Insights?

Я вижу детали запроса, но не полезную нагрузку, публикуемую в application insights. Должен ли я отслеживать это с помощью кодировки?

Я создаю веб-Api MVC core 1.1.

POST request

6 ответов


вы можете просто реализовать ваши собственные Телеметрии Инициализатора:

например, ниже реализации, которая извлекает полезную нагрузку и добавляет ее в качестве пользовательского измерения телеметрии запроса:

public class RequestBodyInitializer : ITelemetryInitializer
{
    public void Initialize(ITelemetry telemetry)
    {
        var requestTelemetry = telemetry as RequestTelemetry;
        if (requestTelemetry != null && (requestTelemetry.HttpMethod == HttpMethod.Post.ToString() || requestTelemetry.HttpMethod == HttpMethod.Put.ToString()))
        {
            using (var reader = new StreamReader(HttpContext.Current.Request.InputStream))
            {
                string requestBody = reader.ReadToEnd();
                requestTelemetry.Properties.Add("body", requestBody);
            }
        }
    }
}

затем добавьте его в конфигурацию либо конфигурационный файл или через код:

TelemetryConfiguration.Active.TelemetryInitializers.Add(new RequestBodyInitializer());

затем запросите его в Analytics:

requests | limit 1 | project customDimensions.body

решение, предоставленное @yonisha, на мой взгляд, является самым чистым из доступных. Однако вам все равно нужно получить свой httpcontext там, и для этого вам нужно больше кода. Я также вставил некоторые комментарии, которые основаны или взяты из приведенных выше примеров кода. Важно сбросить позицию вашего запроса, иначе вы потеряете его данные.

Это мое решение, которое я тестировал и дает мне jsonbody:

public class RequestBodyInitializer : ITelemetryInitializer
{
    readonly IHttpContextAccessor httpContextAccessor;

    public RequestBodyInitializer(IHttpContextAccessor httpContextAccessor)
    {
        this.httpContextAccessor = httpContextAccessor;
    }

    public void Initialize(ITelemetry telemetry)
    {
        if (telemetry is RequestTelemetry requestTelemetry)
        {
            if ((httpContextAccessor.HttpContext.Request.Method == HttpMethods.Post ||
                 httpContextAccessor.HttpContext.Request.Method == HttpMethods.Put) &&
                httpContextAccessor.HttpContext.Request.Body.CanRead)
            {
                const string jsonBody = "JsonBody";

                if (requestTelemetry.Properties.ContainsKey(jsonBody))
                {
                    return;
                }

                //Allows re-usage of the stream
                httpContextAccessor.HttpContext.Request.EnableRewind();

                var stream = new StreamReader(httpContextAccessor.HttpContext.Request.Body);
                var body = stream.ReadToEnd();

                //Reset the stream so data is not lost
                httpContextAccessor.HttpContext.Request.Body.Position = 0;
                requestTelemetry.Properties.Add(jsonBody, body);
            }
        }
    }

тогда также не забудьте добавить это в ваш запуск - > ConfigureServices

services.AddSingleton<ITelemetryInitializer, RequestBodyInitializer>();

EDIT:

если вы также хотите получить responsebody, я счел полезным создать один кусок промежуточного ПО (dotnet core не уверен в framework). Сначала я взял выше подход, где вы регистрируете ответ и запрос, но большую часть времени вы хотите их вместе.

    public async Task Invoke(HttpContext context)
    {
        var reqBody = await this.GetRequestBodyForTelemetry(context.Request);

        var respBody = await this.GetResponseBodyForTelemetry(context);
        this.SendDataToTelemetryLog(reqBody, respBody, context);
    }

это ждет как запрос, так и ответ, где запрос в значительной степени такой же, как и выше, вместо этого это задача.

для тело ответа я использовал код ниже, я также исключил 204, так как это приводит к nullref:

public async Task<string> GetResponseBodyForTelemetry(HttpContext context)
{
    Stream originalBody = context.Response.Body;

        try
        {
            using (var memStream = new MemoryStream())
            {
                context.Response.Body = memStream;

                //await the responsebody
                await next(context);
                if (context.Response.StatusCode == 204)
                {
                    return null;
                }

                memStream.Position = 0;
                var responseBody = new StreamReader(memStream).ReadToEnd();

                //make sure to reset the position so the actual body is still available for the client
                memStream.Position = 0;
                await memStream.CopyToAsync(originalBody);

                return responseBody;
            }
        }
        finally
        {
            context.Response.Body = originalBody;
        }
    }

я реализовал промежуточное ПО для этого,

Invoke метод делает,

 if (context.Request.Method == "POST" || context.Request.Method == "PUT")
        {
            var bodyStr = GetRequestBody(context);
            var telemetryClient = new TelemetryClient();
            var traceTelemetry = new TraceTelemetry
            {
                Message = bodyStr,
                SeverityLevel = SeverityLevel.Verbose
            };
            //Send a trace message for display in Diagnostic Search. 
            telemetryClient.TrackTrace(traceTelemetry);
        }

где, GetRequestBody похоже,

private static string GetRequestBody(HttpContext context)
    {
        var bodyStr = "";
        var req = context.Request;

        //Allows using several time the stream in ASP.Net Core.
        req.EnableRewind();

        //Important: keep stream opened to read when handling the request.
        using (var reader = new StreamReader(req.Body, Encoding.UTF8, true, 1024, true))
        {
            bodyStr = reader.ReadToEnd();
        }

        // Rewind, so the core is not lost when it looks the body for the request.
        req.Body.Position = 0;
        return bodyStr;
    }

решение, предоставленное yonisha, является чистым, но оно не работает для меня в .Net Core 2.0. Это работает, если у вас есть тело JSON:

public IActionResult MyAction ([FromBody] PayloadObject payloadObject)
{
    //create a dictionary to store the json string
    var customDataDict = new Dictionary<string, string>();

    //convert the object to a json string
    string activationRequestJson = JsonConvert.SerializeObject(
    new
    {
        payloadObject = payloadObject
    });

    customDataDict.Add("body", activationRequestJson);

    //Track this event, with the json string, in Application Insights
    telemetryClient.TrackEvent("MyAction", customDataDict);

    return Ok();
}

извините, решение @yonisha, похоже, не работает в .NET 4.7. Часть Application Insights работает нормально, но на самом деле нет простого способа получить тело запроса внутри инициализатора телеметрии в .NET 4.7. .NET 4.7 использует GetBufferlessInputStream () для получения потока, и этот поток "читается один раз". Один потенциальный код выглядит так:

private static void LogRequestBody(ISupportProperties requestTelemetry)
{
    var requestStream = HttpContext.Current?.Request?.GetBufferlessInputStream();

    if (requestStream?.Length > 0)
        using (var reader = new StreamReader(requestStream))
        {
            string body = reader.ReadToEnd();
            requestTelemetry.Properties["body"] = body.Substring(0, Math.Min(body.Length, 8192));
        }
}

но возврат из GetBufferlessInputStream () уже cosumed и не поддерживает поиск. Поэтому тело будет всегда будьте пустой строкой.


Я никогда не получал ответа @yonisha, поэтому я использовал DelegatingHandler вместо:

public class MessageTracingHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // Trace the request
        await TraceRequest(request);

        // Execute the request
        var response = await base.SendAsync(request, cancellationToken);

        // Trace the response
        await TraceResponse(response);

        return response;
    }

    private async Task TraceRequest(HttpRequestMessage request)
    {
        try
        {
            var requestTelemetry = HttpContext.Current?.GetRequestTelemetry();

            var requestTraceInfo = request.Content != null ? await request.Content.ReadAsByteArrayAsync() : null;

            var body = requestTraceInfo.ToString();

            if (!string.IsNullOrWhiteSpace(body) && requestTelemetry != null)
            {
                requestTelemetry.Properties.Add("Request Body", body);
            }
        }
        catch (Exception exception)
        {
            // Log exception
        }
    }

    private async Task TraceResponse(HttpResponseMessage response)
    {
        try
        {
            var requestTelemetry = HttpContext.Current?.GetRequestTelemetry();

            var responseTraceInfo = response.Content != null ? await response.Content.ReadAsByteArrayAsync() : null;

            var body = responseTraceInfo.ToString();

            if (!string.IsNullOrWhiteSpace(body) && requestTelemetry != null)
            {
                requestTelemetry.Properties.Add("Response Body", body); 
            }
        }
        catch (Exception exception)
        {
            // Log exception
        }
    }
}

.GetRequestTelemetry() метод расширения от Microsoft.Функция applicationinsights.Web.