visual tutorial telemetry studio porta insights applications application c# azure-application-insights

c# - tutorial - Ver cuerpo de solicitud POST en Application Insights



porta azure (6)

¿Es posible ver el cuerpo de la solicitud POST en Application Insights?

Puedo ver los detalles de la solicitud, pero no la carga útil que se publica en la información de la aplicación. ¿Tengo que seguir esto con algo de codificación?

Estoy construyendo un MVC core 1.1 Web Api.


Implementé un middleware para esto,

El método de invocación hace,

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

Donde, GetRequestBody es como,

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


La solución provista por @yonisha es, en mi opinión, la más limpia disponible. Sin embargo, aún necesitas ingresar tu httpcontext allí y para eso necesitas más código. También he insertado algunos comentarios que están basados ​​o tomados de los ejemplos de código anteriores. Es importante restablecer la posición de su solicitud, de lo contrario perderá sus datos.

Esta es mi solución que he probado y me da el 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); } } }

Entonces, también asegúrese de agregar esto a su Inicio -> Configurar Servicios

services.AddSingleton<ITelemetryInitializer, RequestBodyInitializer>();

EDITAR:

Si también desea obtener el cuerpo de respuesta, me ha resultado útil crear una pieza de middleware (dotnet core no está seguro acerca del marco). Al principio adopté el enfoque anterior en el que registra una respuesta y una solicitud, pero la mayoría de las veces desea que estén juntos.

public async Task Invoke(HttpContext context) { var reqBody = await this.GetRequestBodyForTelemetry(context.Request); var respBody = await this.GetResponseBodyForTelemetry(context); this.SendDataToTelemetryLog(reqBody, respBody, context); }

Esto espera una Solicitud y una Respuesta donde la solicitud es más o menos la misma que la anterior, en lugar de ser una tarea.

Para el cuerpo de respuesta, he usado el código de abajo, también excluí un 204 ya que eso lleva a un 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; } }


La solución provista por yonisha es limpia, pero no funciona para mí en .Net Core 2.0. Esto funciona si tienes un cuerpo 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(); }


Lo siento, la solución de @ yonisha no parece funcionar en .NET 4.7. La parte de Application Insights funciona bien, pero en realidad no hay una forma sencilla de obtener el cuerpo de la solicitud dentro del inicializador de telemetría en .NET 4.7. .NET 4.7 usa GetBufferlessInputStream () para obtener el flujo, y este flujo es "leído una vez". Un código potencial es así:

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

Pero el retorno de GetBufferlessInputStream () ya se ha calculado y no admite la búsqueda. Por lo tanto, el cuerpo siempre será una cadena vacía.


Nunca conseguí que la respuesta de @ yonisha funcionara, así que utilicé un 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() es un método de extensión de Microsoft.ApplicationInsights.Web .


Simplemente puede implementar su propio Inicializador de telemetría :

Por ejemplo, debajo de una implementación que extrae la carga útil y la agrega como una dimensión personalizada de la telemetría de solicitud:

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

Luego agréguelo a la configuración ya sea por el archivo de configuración o por medio de un código:

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

Luego consulta en Analytics:

requests | limit 1 | project customDimensions.body