TL; DR Question: Y at-il un moyen d'obtenir le corps de la requête dans une instance ExceptionTelemetry existante, dans ASP.NET de base, sans copier tous les corps de demande?Comment enrichir App Insights ExceptionTelemetry avec le corps de la demande dans ASP.NET base
Je voudrais être en mesure d'inclure le Corps de la demande dans la télémétrie d'exception pour avoir un aperçu de l'application. C'est à dire. Je veux seulement la demande quand une exception s'est produite. En recherchant la documentation sur ASP.NET Core et Application Insights, il semble que la "bonne" façon d'enrichir la télémétrie soit d'utiliser TelemetryProcessors ou TelemetryInitializers, j'ai donc essayé d'obtenir le corps de la requête dans un telemetryinitializer personnalisé, seulement pour découvrir que le flux de corps de la demande est fermé/disposé quand je veux lire (rebobinage ne permet pas, car apparemment il a déjà été disposé lorsque l'Insights App telemetryinitializer est en cours d'exécution).
Je fini par résoudre en ayant un middleware qui copie le flux de demande:
public async Task Invoke(HttpContext context)
{
var stream = context.Request.Body;
try
{
using (var buffer = new MemoryStream())
{
// Copy the request stream and rewind the copy
await stream.CopyToAsync(buffer);
buffer.Position = 0L;
// Create another copy and rewind both
var otherBuffer = new MemoryStream();
await buffer.CopyToAsync(otherBuffer);
buffer.Position = 0L;
otherBuffer.Position = 0L;
// Replace the request stream by the first copy
context.Request.Body = buffer;
// Put a separate copy in items collection for other things to use
context.Items["RequestStreamCopy"] = otherBuffer;
context.Response.RegisterForDispose(otherBuffer);
await next(context);
}
}
finally
{
context.Request.Body = stream;
}
}
Et mon initialiseur:
public AiExceptionInitializer(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException("httpContextAccessor");
}
public void Initialize(ITelemetry telemetry)
{
var context = this.httpContextAccessor.HttpContext;
if (context == null)
{
return;
}
lock (context)
{
var request = context.Features.Get<RequestTelemetry>();
if (request == null)
{
return;
}
this.OnInitializeTelemetry(context, request, telemetry);
}
}
protected void OnInitializeTelemetry(HttpContext platformContext, RequestTelemetry requestTelemetry, ITelemetry telemetry)
{
if (telemetry is ExceptionTelemetry exceptionTelemetry)
{
var stream = platformContext.Items["RequestStreamCopy"] as MemoryStream;
try
{
if (stream?.Length <= 0)
{
return;
}
// Rewind the stream position just to be on the safe side
stream.Position = 0L;
using (var reader = new StreamReader(stream, Encoding.UTF8, true, 1024, true))
{
string requestBody = reader.ReadToEnd();
exceptionTelemetry.Properties.Add("HttpRequestBody", requestBody);
}
}
finally
{
if (stream != null)
{
// Rewind the stream for others to use.
stream.Position = 0L;
}
}
}
}
Toutefois, ce devoir copier le flux de demande (DEUX FOIS) pour chaque demande, de ne l'avoir utilisé que sur des échecs me semble plutôt inefficace. Je me demande s'il y a une autre façon de faire quelque chose comme ça où je n'ai pas de copier le flux de chaque demande juste pour sérialiser les défaillants? Je suis conscient que je pourrais "simplement" écrire un middleware qui créerait de nouvelles instances ExceptionTelemetry, mais pour autant que je sache (je me trompe peut-être), il me laisserait deux instances Exception dans Application Insights (ie celui généré par moi et celui généré par les extensions AI), au lieu d'une seule exception avec la propriété ajoutée dont j'ai besoin.
je pourrais peut-être mettre le flux en fonction au lieu, pas sûr quand je dois faire un sur l'autre, mais qui est hors de propos pour la question que je deviner. –
Sera-t-il possible d'écrire un intergiciel de suivi des exceptions mais de l'utiliser uniquement pour capturer le corps de la requête dans le contexte? (Paraphraser: est le corps de la demande toujours disponible dans le middleware de suivi d'exception personnalisée?) Si oui - vous pouvez écrire ce corps dans le contexte que dans cet middleware d'exception, puis, dans initialiseur de télémétrie, vous serez en mesure de vérifier si le corps n'est pas null dans le contexte - utilisez-le pour remplir les propriétés d'exception. Ainsi, en ordonnant votre middleware avant le middleware AI - vous placez le contexte juste avant l'initialisation. –
Pas tout à fait sûr, mais je vais essayer et vous le faire savoir. –