How can i log in some different files with custom logger in ASP.NET Core 3.1 WebApi?

Viewed 2990

I created custom Logger and LoggerProvider for writing logs in text file. This is my classes.

FileLogger:

public class FileLogger : ILogger
{
    private string filePath;
    private static object _lock = new object();
    public FileLogger(string path)
    {
        filePath = path;
    }
    public IDisposable BeginScope<TState>(TState state)
    {
        return null;
    }

    public bool IsEnabled(LogLevel logLevel)
    {
        return true;
    }

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        if (formatter != null)
        {
            lock (_lock)
            {
                File.AppendAllText(filePath, formatter(state, exception) + Environment.NewLine);
            }
        }
    }
}

FileLoggerProvider:

public class FileLoggerProvider : ILoggerProvider
{
    private string path;
    public FileLoggerProvider(string _path)
    {
        path = _path;
    }
    public ILogger CreateLogger(string categoryName)
    {
        return new FileLogger(path);
    }

    public void Dispose()
    {
    }
}

FileLoggerExtensions:

public static class FileLoggerExtensions
{
    public static ILoggerFactory AddFile(this ILoggerFactory factory, string filePath)
    {
        factory.AddProvider(new FileLoggerProvider(filePath));
        return factory;
    }
}

And I want to log information in three different files. This is how I'm trying to do it:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();

        app.UseAuthorization();

        string logPath = CheckLogDir();
        loggerFactory.AddFile(Path.Combine(logPath, "auction_log.txt"));
        var auctionLogger = loggerFactory.CreateLogger("AuctionLogger");
        loggerFactory.AddFile(Path.Combine(logPath, "requests_log.txt"));
        var requestsLogger = loggerFactory.CreateLogger("RequestLogger");
        loggerFactory.AddFile(Path.Combine(logPath, "error_log.txt"));
        var errorLogger = loggerFactory.CreateLogger("ErrorLogger");

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }

Then i call my loggers in controller like this:

public MainController(ILogger<MainController> auctionLogger, ILogger<MainController> requestLogger, ILogger<MainController> errorLogger, marketplaceContext context)
    {
        _auctionLogger = auctionLogger;
        _requestLogger = requestLogger;
        _errorLogger = errorLogger;
        _db = context;
    }

Then I'm trying to use them in different methods of controller and write different things. For example in GET method I'm using this:

_requestLogger.LogInformation("Processing request {0}", Request.Path);

But it writes this information to all three files, not only to requestLog.txt. What is my mistake? Help me please.

1 Answers

In many situations you want a log message to be logged by all loggers ("log writers"). E.g. console and file. That's what you have configured here.

    // add provider
    loggerFactory.AddFile(Path.Combine(logPath, "auction_log.txt"));
    // create logger but you do nothing with it. This line seems useless.
    var auctionLogger = loggerFactory.CreateLogger("AuctionLogger");
    // ... two more times

Note that dependency injection does not care about your parameter name. You get the logger based on the type ILogger<MainController> which is the same for all three parameters and that's why all your loggers do the same.

    public MainController(
      ILogger<MainController> auctionLogger, 
      ILogger<MainController> requestLogger, 
      ILogger<MainController> errorLogger, 
      marketplaceContext context)

I think you should create one Logger that is configured with all three log file paths and then decide based on log level which file to use.

Note that you should not decide which log file to use by creating multiple loggers. That way you can never modify logging, e.g. what if you want to run in docker sometimes and want to log everything to console?

In short

  • Create log message and tell the logging system as much as possible about it (but at least the log level and message). We don't care about how and where we'll output the message here.
  • Configure logging app wide and tell the system how and where to log messages - this may include writing log level "warning" to file a and "error" to file b, ...
Related