86 votes

Afficher le corps de la demande POST dans Application Insights

Est-il possible d'afficher le corps de la demande POST dans Application Insights?

Je peux voir les détails de la demande, mais pas la charge utile affichée dans les informations sur les applications. Dois-je suivre cela avec un code?

Je construis un Api Web 1.1 de base MVC.

Demande POST

59voto

yonisha Points 1844

Vous pouvez simplement mettre en place votre propre Télémétrie Initialiseur:

Par exemple, en dessous d'une mise en œuvre que des extraits de la charge utile et ajoute une dimension personnalisée de la demande de télémétrie:

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

Puis l'ajouter à la configuration, soit par fichier de configuration ou via le code:

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

Une requête dans google Analytics:

requests | limit 1 | project customDimensions.body

41voto

joerivrij Points 156

La solution fournie par @yonisha est à mon avis le plus propre disponible. Cependant, vous avez encore besoin pour obtenir votre httpcontext là et que vous avez besoin de plus de code. J'ai également inséré quelques commentaires qui sont fondées ou prise à partir d'exemples de code ci-dessus. Il est important de rétablir la position de votre demande, sinon vous allez perdre les données.

C'est ma solution que j'ai testé et qui me donne la 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);
            }
        }
    }

Alors assurez-vous également d'ajouter à votre Démarrage -> ConfigureServices

services.AddSingleton<ITelemetryInitializer, RequestBodyInitializer>();

EDIT:

Si vous aussi vous voulez obtenir le responsebody j'ai trouvé utile d'en créer un morceau de middleware (dotnet de base ne savez pas sur le cadre). Au début, j'ai eu approche ci-dessus où vous vous connectez une réponse et une demande, mais la plupart du temps, vous voulez que ces ensemble.

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

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

On attend à la fois une Demande et une Réponse où la demande est à peu près la même que ci-dessus, au lieu que c'est une tâche.

Pour le corps de la réponse, j'ai utilisé le code ci-dessous, j'ai également exclu un 204 depuis qui conduit à une 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;
        }
    }

2voto

Dhanuka777 Points 822

J'ai mis en place un middleware pour cela,

La méthode Invoke fait,

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

Où, GetRequestBody est comme,

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

0voto

paulyb Points 46

La solution fournie par yonisha est propre, mais elle ne fonctionne pas pour moi dans .Net Core 2.0. Cela fonctionne si vous avez un corps 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();
}
 

0voto

Ivan Uthus Points 11

Je suis désolé, @yonisha la solution ne semble pas fonctionner dans .NET 4.7. L'Application Insights partie fonctionne bien, mais il n'y a pas de moyen simple d'obtenir le corps de la requête à l'intérieur de la télémétrie de l'initialiseur dans .NET 4.7. .NET de 4,7 utilise GetBufferlessInputStream() pour obtenir le flux, et ce flux est "lue". Un potentiel de code comme ceci:

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

Mais le retour de GetBufferlessInputStream() est déjà consommé, et ne prend pas en charge la recherche. Par conséquent, le corps sera toujours une chaîne vide.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X