Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add two logger files in .net core using LoggerFactory

public class Startup
{
    public IConfiguration Configuration { get; } 

    public void ConfigureServices(IServiceCollection services)
    {   
        loggerFactory.AddFile(logFilePath1);
        services.AddSingleton<ILoggerFactory>(loggerFactory);

        loggerFactory.AddFile(logFilePath2);
        services.AddSingleton<ILoggerFactory>(loggerFactory);
    }
}

With in the startup.cs class, I create two loggers . Since it has two loggers how can I set the Ilogger data in the controller? can it do using normal way? Or is there any different way to pass logger filename when logged within the controller?

like image 246
Oshini Gunarwardena Avatar asked Sep 05 '25 09:09

Oshini Gunarwardena


1 Answers

OK, so you want to have two different loggers in a single controller and you want these two loggers to log to different files. The .NET Core logging does not have good support for this scenario so it requires a bit of hacking to achieve this. Whenever I find myself in a situation where I get a a lot of resistance from the framework I'm using I reconsider if what I'm trying to do is good idea and if it is whether I should use another framework so you might want to do the same. With that in mind here is a way to achieve what you want.

Loggers can be identified by a category. In your case you want a single controller to have two different loggers so you have to use ILoggerFactory to create the loggers (you could use the generic ILogger<T> interface but it becomes a bit weird because you need two different types for T):

public class MyController : Controller
{
    private readonly ILogger logger1;
    private readonly ILogger logger2;

    public Controller1(ILoggerFactor loggerFactory)
    {
        logger1 = loggerFactory.Create("Logger1");
        logger2 = loggerFactory.Create("Logger2");
    }
}

The categories of the loggers are Logger1 and Logger2.

Each logger will by default log to all the configured providers. You want a logger with one category to log to one provider and a logger with another category to log to another provider.

While you can create filters that are based on category, provider and log level the problem is that you want to use the same provider for both categories. Providers are identified by their type so you cannot create a rule that targets a specific instance of a provider. If you create a rule for the file provider it will affect all configured file providers.

So this is where the hacking starts: You have to create your own provider types that are linked to the files to be able to filter on each file.

.NET Core does not have support for logging to files so you need a third party provider. You have not specified which provider you use so for this example I will use the Serilog file sink together with the Serilog provider that allows you to plug a Serilog logger into the .NET Core logging framework.

To be able to filter on provider you have to create your own provider. Luckily, that is easily done by deriving from the SerilogLoggerProvider:

class SerilogLoggerProvider1 : SerilogLoggerProvider
{
    public SerilogLoggerProvider1(Serilog.ILogger logger) : base(logger) { }
}

class SerilogLoggerProvider2 : SerilogLoggerProvider
{
    public SerilogLoggerProvider2(Serilog.ILogger logger) : base(logger) { }
}

These two providers does not add any functionality but allows you to create filter that targets a specific provider.

Next step is crating two different Serilog loggers that log to different files:

var loggerConfiguration1 = new LoggerConfiguration()
    .WriteTo.File("...\1.log");
var loggerConfiguration2 = new LoggerConfiguration()
    .WriteTo.File("...\2.log");
var logger1 = loggerConfiguration1.CreateLogger();
var logger2 = loggerConfiguration2.CreateLogger();

You configure your logging in Main by calling the extension method .ConfigureLogging:

.ConfigureLogging((hostingContext, loggingBuilder) =>
    {
        loggingBuilder
            .AddProvider(new SerilogLoggerProvider1(logger1))
            .AddFilter("Logger1", LogLevel.None)
            .AddFilter<SerilogLoggerProvider1>("Logger1", LogLevel.Information)
            .AddProvider(new SerilogLoggerProvider2(logger2))
            .AddFilter("Logger2", LogLevel.None)
            .AddFilter<SerilogLoggerProvider2>("Logger2", LogLevel.Information);
    })

Each provider (which is associated with a specific file) are added and then two filters are configured for each provider. I find the filter evaluation rules hard to reason about but the two filters added - one with LogLevel.None and another with LogLevel.Information - actually achieves the desired result of ensuring that log messages for the two different categories are routed correctly to the two different providers. If a third provider is added it will not be affected by these filters and messages from both categories will be logged by the third provider.

like image 103
Martin Liversage Avatar answered Sep 08 '25 23:09

Martin Liversage