speed258
speed258

Reputation: 65

ASP .NET Core Serilog+Graylog not logging exceptions

Hello I have issue with serilog logging exceptions to graylog. It does not log only exceptions to graylog, but everything else is logged in graylog and everything is logged in console just fine

Problem occurs on: Asp .Net core 7

I have created extension class for logging:

public static class SerilogStartupExtensions
{
private static Serilog.Core.Logger _loggerConfiguration = null;

public static IHostBuilder AddUsageSerilog(this IHostBuilder builder, IConfiguration configuration)
{
    if (_loggerConfiguration == null)
    {
        _loggerConfiguration = BuildLoggerConfiguration(configuration);
    }

    return builder.UseSerilog(_loggerConfiguration);
}

public static ILoggingBuilder AddSerilog(this ILoggingBuilder builder)
{
    return builder.AddSerilog(_loggerConfiguration);
}

public static IApplicationBuilder UseSerilogRequestLogging(this IApplicationBuilder app)
{
    return app.UseSerilogRequestLogging(options =>
    {
        //options.MessageTemplate = "[{Timestamp:HH:mm:ss} {SourceContext} [{Level}] [{RemoteIpAddress}] {Message}{NewLine}{Exception}";
        options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
        {
            diagnosticContext.Set("RemoteIpAddress", httpContext.Connection.RemoteIpAddress);
        };
    });
}

private static Serilog.Core.Logger BuildLoggerConfiguration(IConfiguration configuration)
{
    return new LoggerConfiguration()
        .ReadFrom.Configuration(configuration)
        .Enrich.FromLogContext()
        .MinimumLevel.Is(LogEventLevel.Information)
        .MinimumLevel.Override("System", LogEventLevel.Warning)
        .MinimumLevel.Override("Microsoft", LogEventLevel.Error)
        .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning)
        .WriteTo.Console()
        .WriteTo.Graylog(new GraylogSinkOptions
        {
            HostnameOrAddress = "nice_server_ip",
            Port = 12201,
            TransportType = TransportType.Udp,
            MinimumLogEventLevel = LogEventLevel.Verbose,
            Facility = "my.facility",
            MessageGeneratorType = MessageIdGeneratorType.Timestamp,
        })
        .CreateLogger();
    }
 }

And the usage: Program.cs

builder.Host
.AddUsageSerilog(configuration)
.UseSystemd();

 builder.Logging.AddSerilog();

 builder.Services
.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add(HeaderNames.Accept);
    logging.RequestHeaders.Add(HeaderNames.ContentType);
    logging.RequestHeaders.Add(HeaderNames.ContentDisposition);
    logging.RequestHeaders.Add(HeaderNames.ContentEncoding);
    logging.RequestHeaders.Add(HeaderNames.ContentLength);

    logging.MediaTypeOptions.AddText("application/json");
    logging.MediaTypeOptions.AddText("multipart/form-data");
    logging.MediaTypeOptions.AddText("application/problem+json");

    logging.RequestBodyLogLimit = 1024;
    logging.ResponseBodyLogLimit = 1024;
})

 builder.Configuration
     .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
     .AddJsonFile($"appsettings.{enviroment}.json", optional: true, reloadOnChange: true)
     .AddEnvironmentVariables();

 app
    .UseHttpLogging()
    .UseSerilogRequestLogging();

Appsettings.json

"Serilog": {
"Using": [
  "Serilog",
  "Serilog.Sinks.Console"
],
"MinimumLevel": {
  "Default": "Information",
  "Override": {
    "Microsoft.EntityFrameworkCore": "Warning",
    "Microsoft.Hosting.Lifetime": "Information",
    "Microsoft.AspNetCore": "Warning",
    "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information",
    "HangFire": "Warning"
  }
},
"Properties": {
  "Application": "server.good"
}
},

Attribute who logs all exceptions:

public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
    var serviceProvider = context.HttpContext.RequestServices;

    serviceProvider
        .GetRequiredService<ILogger<ApiExceptionFilterAttribute>>()
        .LogError(context.Exception, "Api action failure.");

    var problem = context.Exception switch
    {
        AccessViolationException _ => ProblemDetailsEx.CreateUnauthorizedErrorResponse(
            ErrorCode.ActionNotAllowed),
        _ => ProblemDetailsEx.CreateInternalServerErrorResponse(),
    };

    context.Result = new ObjectResult(problem)
    {
        StatusCode = problem.Status
    };
}
}

Implementation

[ApiExceptionFilter]
public abstract class ApiControllerBase : ControllerBase
{
}

Usage

public class HomeController : ApiControllerBase
{
    //controller actions
}

Who causing this issue to not log exceptions in graylog?

Update #1 After playing with .Log overload I found that if I put exception in string it does logged in gray log

    try
    {
        throw new Exception();
    }
    catch (Exception ex)
    {
        _logger.Log(LogLevel.Error, ex.ToString());
        throw;
    }

Upvotes: 1

Views: 1088

Answers (1)

Mike Ries
Mike Ries

Reputation: 1

I believe the issue is that the Graylog sink for Serilog uses the System.Text.Json for serialization and deserialization. This package will itself throw a System.NotSupportedException if the log event contains data types that Microsoft has deemed a security risk. This often occurs when serializing a stack trace: https://github.com/dotnet/runtime/issues/43026.

When this occurs, the sink will log the 'NotSupportedException' details to the Serilog SelfLog and you can find it there, if you have that configured.

Unfortunately, I'm not aware of a better workaround than ex.ToString(). You might have some success in reducing the stack trace depth, but that's probably not a very general solution.

It probably wouldn't be difficult to update the sink to catch the NotSupportedException and handle it better.

Upvotes: 0

Related Questions