1

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.

1
  • Do you want inject different loggers for controllers ? Have a look at this. Or log from other classes, see this Commented Aug 17, 2023 at 7:43

1 Answer 1

0

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 ?? "");
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.