Skip to content

Implementing a Custom ILogger for .NET Core

Consider This New Logging Framework

This article corresponds with the MSDN article: Essential .NET – Logging with .NET Core. This GitHub repo contains the code referenced in the article. Not, in particular, the unit test LogCritical_Exception_Success for an example of handling an exception using the custom logger.

  public void LogCritical_Exception_Success()
  {
        string message = "The amount of caffeine has reach critical levels.";
        CustomLogger.CustomLogger customLogger = null;
        CustomLoggerProvider logProvider =
        new CustomLoggerProvider((sender, eventArgs) => customLogger = eventArgs.CustomLogger);
        ApplicationLogging.LoggerFactory.AddProvider(logProvider);
        Logger.LogCritical(message, new Exception("Sample exception."));
        Assert.AreEqual($"{message}\r\nSystem.Exception: Sample exception.", customLogger.LogDataQueue.Dequeue());
  }

The Custom Logger implementation is straightforward:

 public class CustomLogger : ILogger 
  {
        public Queue LogDataQueue = new Queue();

        public IDisposable BeginScopeImpl(object state)
        {
            return null;
        }

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

        public void Log(LogLevel logLevel, int eventId, object state, Exception exception, Func<object, Exception, string> formatter)
        {
            string message = string.Empty;

            if (formatter != null)
            {
                message = formatter(state, exception);
            }
            else
            {
                message = LogFormatter.Formatter(state, exception);
            }

            LogDataQueue.Enqueue(message);
        }
  }

Here is the the extension method for adding a custom logger provider:

  public static class CustomLoggerFactoryExtensions
  {
        public static ILoggerFactory AddCustomLogger(
            this ILoggerFactory factory, out CustomLoggerProvider logProvider)
        {
            logProvider = new CustomLoggerProvider();
            factory.AddProvider(logProvider);
            return factory;
        }
  }

The custom logger provider:

  public class CustomLoggerProvider : ILoggerProvider
  {
        public CustomLoggerProvider() { }
        public CustomLoggerProvider(EventHandler onCreateLogger)
        {
            OnCreateLogger = onCreateLogger;
        }
        public ConcurrentDictionary<string, customlogger=""> Loggers { get; set; } = new ConcurrentDictionary<string, customlogger="">();

        public ILogger CreateLogger(string categoryName)
        {
            CustomLogger customLogger = Loggers.GetOrAdd(categoryName, new CustomLogger());
            OnCreateLogger?.Invoke(this, new CustomLoggerProviderEventArgs(customLogger));
            return customLogger;
        }

        public void Dispose() { }

        public event EventHandler OnCreateLogger = delegate { };
  }
</string,></string,>

The provider event args:

  public class CustomLoggerProviderEventArgs
  {
        public CustomLogger CustomLogger { get; }
        public CustomLoggerProviderEventArgs(CustomLogger logger)
        {
            CustomLogger = logger;
        }
  }

One uses the ApplicationLogging static class to set up the custom logger with the following pattern:

  public static class ApplicationLogging
  {
        public static ILoggerFactory LoggerFactory { get; } = new LoggerFactory();
        public static ILogger CreateLogger() =>
            LoggerFactory.CreateLogger();
  }
*        The following pattern will set up the custom logger
            CustomLogger.CustomLogger customLogger = null;
            CustomLoggerProvider logProvider =
                new CustomLoggerProvider((sender, eventArgs) => customLogger = eventArgs.CustomLogger);
            ApplicationLogging.LoggerFactory.AddProvider(logProvider);
*/

Have a Question?

Check out my other tutorials and leave any questions in the comment section below!

Tags:

10 thoughts on “Implementing a Custom ILogger for .NET Core”

  1. thank you so much for articulating everything at one place, i just downloaded from git and started debugging and able to understand fully

    1. Dilip,

      Mark said the approach, starting with C# 10, would be to use the CallerArgumentExpressionAttribute. He’s writing a blog about this very subject that you should see on our blog in the near future. Thanks for the read!

  2. How do I pass and read custom object in the log method implementation of my custom logger?

    // Write log message
    myCustomLogger.LogError(“My Message”, customDataObject);

    // Log method implementation
    public void Log(…)

    In other words, How do I read customDataObject in above Log method?

    Thanks in advance.

    1. string formatter(Message msg, Exception ex)
      {
      var content = $”[{logLevel}]\r\n{msg.Title}\r\n{msg.Body}\r\n{msg.Author}\r\n{msg.CreatedDate}\r\n\r\n”;
      return content;
      }
      log.Log(logLevel, new EventId(), message, null, (msg, ex) => formatter(msg, ex));

      where Message is a custom object -> in yout custom provider, “TState state” parameter of Log method will be your passed object

  3. How does this handle exceptions? I was kind of thinking that CustomLogger.Log() would implement try/catch logic to handle exceptions.

Leave a Reply

Your email address will not be published. Required fields are marked *