Search code examples
c#serilog.net-8.0microsoft.extensions.logging

'ILogger' does not contain a definition for 'BeginScope' using Serilog


I've been utilizing Microsoft.Extensions.Logging for my ASP.NET Core 8 Web API project logging implementation and recently decided to switch to Serilog. The migration process was successful, except for the aspect of updating the 'BeginScope' feature of Microsoft.Extensions.Logging.

Upon attempting to update, I'm encountering an error message that states:

'ILogger' does not contain a definition for 'BeginScope' and no accessible extension method 'BeginScope' accepting a first argument of type 'ILogger' could be found.

I've tried to troubleshoot the issue on my own but have been unable to find a solution so far. I'm hoping someone here could shed some light on what might be causing this error and how to possibly resolve it.

Any guidance on how to correctly update the 'BeginScope' feature when migrating from Microsoft.Extensions.Logging to Serilog in an ASP.NET Core 8 Web API project would be greatly appreciated.

Code

using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Hosting;
using Serilog;
using Demo.Exceptions;
using Demo.GuardClauses;

namespace Demo.Middlewares;

internal class GlobalExceptionHandler(ILogger logger, IWebHostEnvironment env) : IExceptionHandler
{
    private readonly ILogger _logger = logger.ForContext<GlobalExceptionHandler>();
    private readonly IWebHostEnvironment _env = env.IsNotNull();

    public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
    {
        
        var errorResult = exception switch
        {
            FluentValidation.ValidationException fluentException => ExceptionDetails.HandleFluentValidationException(fluentException),
            UnauthorizedException unauthorizedException => ExceptionDetails.HandleUnauthorizedException(unauthorizedException),
            ForbiddenException forbiddenException => ExceptionDetails.HandleForbiddenException(forbiddenException),
            NotFoundException notFoundException => ExceptionDetails.HandleNotFoundException(notFoundException),
            TimeOutException timeOutException => ExceptionDetails.HandleTimeOutException(timeOutException),
            _ => ExceptionDetails.HandleDefaultException(exception),
        };

        var errorLogLevel = exception switch
        {
            FluentValidation.ValidationException or UnauthorizedException => LogEventLevel.Warning,
            _ => LogEventLevel.Error
        };

        LogErrorMessage(errorLogLevel, exception, errorResult);

        var response = httpContext.Response;

        if (!response.HasStarted)
        {
            response.ContentType = "application/problem+json";
            response.StatusCode = errorResult.Status!.Value;
            await response.WriteAsJsonAsync(errorResult, cancellationToken).ConfigureAwait(false);
        }
        else
        {
            _logger.Warning("Can't write error response. Response has already started.");
        }

        return true;
    }

    private void LogErrorMessage1(LogEventLevel errorLogLevel, Exception exception, ExceptionDetails details)
    {
        var properties = new Dictionary<string, object>
        {
            {
                "TraceId",
                details.TraceId
            }
        };

        if (details.Errors != null)
        {
            properties.Add("Errors", details.Errors);
        }

        if (_env.IsDevelopment())
        {
            properties.Add("StackTrace", exception.StackTrace!.Trim());
        }

        using (_logger.BeginScope(properties))
        {
            _logger.Write(errorLogLevel, "{title} | {details} | {traceId}", details.Title, details.Detail, details.TraceId);
        }
    }
}

Can anyone please help me here by providing their guidance? Any help would be greatly appreciated.


Solution

  • Serilog's specific analogous feature is LogContext:

    Serilog.Context.LogContext can be used to dynamically add and remove properties from the ambient "execution context"; for example, all messages written during a transaction might carry the id of that transaction, and so-on.

    You need to enable it in configuration:

    var log = new LoggerConfiguration()
        .Enrich.FromLogContext()
    

    And use LogContext.PushProperty:

    log.Information("No contextual properties");
    
    using (LogContext.PushProperty("A", 1))
    {
        log.Information("Carries property A = 1");
    
        using (LogContext.PushProperty("A", 2))
        using (LogContext.PushProperty("B", 1))
        {
            log.Information("Carries A = 2 and B = 1");
        }
    
        log.Information("Carries property A = 1, again");
    }