Search code examples
c#.netlogging.net-7.0asp.net-core-middleware

Using different loggers or logging configurations in .NET 7 API


I have a React app that uses a .NET 7 API. We are using Graylog for logging. The idea is to provide an API endpoint for the react app to POST its error logs. I want to use a different log source if the errors are coming from the web app. In order to do it, I have written a middleware class based on the route of the request. If /logs route is hit, I add a new logger configuration to the Logger factory.

Middleware class:

using Gelf.Extensions.Logging;

namespace MyProject1.Api.Extensions
{
    public class LoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly IConfiguration _configuration;
        private readonly ILoggerFactory _loggerFactory;

        public LoggingMiddleware(
            RequestDelegate next,
            IConfiguration configuration,
            ILoggerFactory loggerFactory)
        {
            _next = next;
            _configuration = configuration;
            _loggerFactory = loggerFactory;
        }

        public async Task Invoke(HttpContext context)
        {
            if (context.Request.Path.StartsWithSegments("/logs"))
            {
                var gelfOptions = new GelfLoggerOptions();
                _configuration.GetSection("Logging:WebAppGELF").Bind(gelfOptions);
                gelfOptions.AdditionalFields["machine_name"] = Environment.MachineName;

                _loggerFactory.AddGelf(gelfOptions);

                await _next(context);
            }
            else
            {
                await _next(context);
            }
        }
    }
}

Here's the logging section in appsettings.json:

  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    },
    "GELF": {
      "Host": "xxx.graylog.com",
      "LogSource": "API"
    },
    "WebAppGELF": {
      "Host": "xxx.graylog.com",
      "LogSource": "WebApp"
    }
  }

The configuration in program.cs:

app.UseMiddleware<LoggingMiddleware>();

using (var serviceScope = app.Services.CreateScope())
{
    var services = serviceScope.ServiceProvider;
    var logger = services.GetRequiredService<MyProject1.Logging.IMyLogger<MyProject1.Api.Extensions.Project1Exception>>();
    app.ConfigureExceptionHandler(logger);
}

With the above, two log entries are being written every time the /logs endpoint is hit. Content is the same but one log entry contains LogSource : API and the other contains LogSource : WebApp. I assume this is happening because the logger factory contains two loggers and both are being invoked.

What's the right way of doing this? Basically, I want to use different loggers or logging configurations based on the controller or class the logger is being used in.


Solution

  • I was able to create a new logger by creating a new logger factory in the controller's constructor. This controller will be called by the web app hence controller specific configuration would do the job.

    public class LogsController : Controller
    {
        private readonly ILogger _logger;
        private readonly IConfiguration _configuration;
    
        public LogsController(IConfiguration configuration)
        {
            _configuration = configuration;
    
            _logger = LoggerFactory
                 .Create(configure => configure.AddGelf(m =>
                 {
                     m.Host = "graylog.xxxxx.com";
                     m.LogSource = _configuration["LoggingWebApp:GELF:LogSource"] ?? "webapp_local";
                     m.AdditionalFields["Project"] = "my_project";
                     m.AdditionalFields["Environment"] = _configuration["Logging:GELF:AdditionalFields:Environment"];
                     m.AdditionalFields["Module"] = "webapp";
                 }))
                 .CreateLogger("webapp");
        }
    
        [HttpPost("error")]
        public void Error([FromBody] WebAppLogErrorRequest model)
        {
            var userId = User.FindFirst("sub")?.Value;
            _logger.LogError(
                "Message: {message}\n"
                + "File Name: {FileName}\n"
                + "Function Name: {FunctionName}\n"
                + "Stack Trace: {StackTrace}\n"
                + "User Id: {UserId}",
                model.Message, model.FileName, model.FunctionName, model.StackTrace, userId ?? "");
        }
    }