5 votes

Les propriétés de Serilog Logcontext disparaissent après le traitement d'une exception

Dans mon site web, j'intègre Serilog pour enregistrer mes erreurs dans un puits personnalisé. La journalisation est enrichie d'un LogContext où certaines propriétés personnalisées doivent être passées. Si j'utilise Log.Information(), il arrive à mon puits avec les propriétés dans le LogEvent. Cela fonctionne donc très bien.

L'objectif principal est de combiner le système de journalisation à un logiciel intermédiaire de gestion des exceptions. Ainsi, dans le gestionnaire d'exception, l'erreur est attrapée, qui est lancée à partir d'une méthode du contrôleur. Partout où je place le _logger.Log() dans le gestionnaire d'exception, aucune propriété personnalisée n'est disponible dans le Sink. Lors du débogage, le filtre LogContextFilter est passé avant d'aller dans le Sink, mais aucune propriété du filtre n'est trouvée.

Quelqu'un a-t-il une idée ?

Démarrage

Log.Logger = new LoggerConfiguration()
            .WriteTo.PasSink(new SerLogServiceClient.SerLogServiceClient(new SerLogServiceClientOptions()))
            .Enrich.FromLogContext()
            .CreateLogger();

services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).AddMvcOptions(mo =>
        {
            mo.Filters.Add(typeof(LogContextFilter));
        });

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseMiddleware<LogContextMiddleware>();
        app.UseErrorHandler(o =>
        {
            o.ExceptionHandlingPath = "/Home/Error";
            o.Context = ExceptionHandler.Context.MVC;
        });

        //app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseStaticFiles(new StaticFileOptions
        {
            FileProvider = new PhysicalFileProvider(
                Path.Combine(Directory.GetCurrentDirectory(), "Content")),
            RequestPath = "/Content"
        });

        app.UseAuthentication();

        app.UseSession();
        //app.UseCookiePolicy();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }

LogContextFilter

public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        using (LogContext.Push(
            new PropertyEnricher("UserCode", context.HttpContext.User.Claims.FirstOrDefault(s => s.ToString().StartsWith("UserCode"))?.Value),
            new PropertyEnricher("Test", "Will this go through?")))
        {
            await next.Invoke();
        }
    }

ExceptionHandlerMiddleware

public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next.Invoke(context);
        }
        catch (HttpRequestException hex)
        {
            //check response naar reynaersexception??
            //deserialize naar re
            throw new NotSupportedException();  //als test
        }
        catch  (Exception ex)
        {

            if (context.Response.HasStarted)
            {
                throw ex;
            }

            _logger.LogError(ex.Message);

            var originalPath = context.Request.Path;
            try
            {
                if (_options.Context == Context.MVC)
                {
                    context.Response.Clear();
                    context.Response.StatusCode = 500;
                    context.Response.OnStarting(Callback, context.Response);

                    //set features
                    var exceptionHandlerFeature = new ReynaersExceptionHandlerFeature()
                    {
                        Error = ex,
                        Path = context.Request.Path.Value,
                    };
                    context.Features.Set<IExceptionHandlerFeature>(exceptionHandlerFeature);
                    context.Features.Set<IExceptionHandlerPathFeature>(exceptionHandlerFeature);

                    //continue lifecycle with updated context
                    if (_options.ExceptionHandlingPath.HasValue)
                    {
                        context.Request.Path = _options.ExceptionHandlingPath;
                    }

                    await _next.Invoke(context);
                }
            }
            catch (Exception ex2)
            {
                // Suppress secondary exceptions, re-throw the original.
                Log.Error(ex2.Message);
                context.Request.Path = originalPath;
                throw ex;
            }
        }
    }

9voto

Tolyandre Points 94

Cela se produit parce que l'exception est enregistrée dans un gestionnaire qui s'exécute en dehors de l'application using (LogContext.Push(..)) Par conséquent, les propriétés personnalisées ont déjà disparu du contexte.

...

// in mvc's OnActionExecutionAsync()
        using (LogContext.Push(
            new PropertyEnricher("UserCode", ".."),
            new PropertyEnricher("Test", "Will this go through?")))
        {
            await next.Invoke(); // code that throws
        }

...

// later in ExceptionHandlerMiddleware, no custom properties
_logger.LogError(ex.Message);

Il y a quelque temps, j'ai fait des recherches sur ce problème et j'ai écrit Enrichisseur de contexte .

Cette bibliothèque capture le contexte à partir du moment où une exception a été levée. Ensuite, ThrowContextEnricher peut être utilisé pour enrichir le journal des exceptions avec le contexte original.

Log.Logger = new LoggerConfiguration()
    .Enrich.With<ThrowContextEnricher>()  // Adds enricher globally
    .Enrich.FromLogContext()
    .WriteTo
    ...
    .CreateLogger();
...

// in mvc's OnActionExecutionAsync()
// push your properties as normal
        using (LogContext.Push(
            new PropertyEnricher("UserCode", ".."),
            new PropertyEnricher("Test", "Will this go through?")))
        {
            await next.Invoke(); // code that throws
        }

...

// in exception handler
// properties get logged now
// notice the exception is passed too, not just message
_logger.LogError(ex, ex.Message);

0voto

illug Points 294

Je me suis également débattu avec ce problème et j'ai trouvé la réponse il y a quelques mois (je ne la trouve plus maintenant). Je l'ai cherchée et c'est ainsi que je suis tombé sur votre question). Je suis presque sûr que vous avez trouvé une solution maintenant, mais cela pourrait aider quelqu'un.

Mais essayez cette variante :

catch (Exception ex2) when (LogUnexpectedError(ex2))
{
    // Suppress secondary exceptions, re-throw the original.        
    context.Request.Path = originalPath;
    throw ex;
}

private bool LogUnexpectedError(Exception ex)
{
    Log.Error(ex.Message);
    return true;
}

Si je me souviens bien, la partie when est le seul endroit où vous pouvez gérer l'exception avant que le LogExceptionFilter ne sorte de son champ d'application. J'espère que cela vous aidera.

Mise à jour : J'ai trouvé l'endroit où j'avais trouvé cela à l'origine : https://andrewlock.net/how-to-include-scopes-when-logging-exceptions-in-asp-net-core/#using-exception-filters-to-capture-scopes

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