7

This question is related to Steven’s answer - here. He proposed a very good logger wrapper. I will paste his code below:

public interface ILogger
{
    void Log(LogEntry entry);
}

public static class LoggerExtensions
{
    public static void Log(this ILogger logger, string message)
    {
        logger.Log(new LogEntry(LoggingEventType.Information,
            message, null));
    }

    public static void Log(this ILogger logger, Exception exception)
    {
        logger.Log(new LogEntry(LoggingEventType.Error, 
            exception.Message, exception));
    }

    // More methods here.
}

So, my question is what is the proper way to create implementation that proxies to Serilog?

Note: this question is related to this question about log4net but now specific to Serilog.

Community
  • 1
  • 1
Steven
  • 166,672
  • 24
  • 332
  • 435
  • 2
    why are you referring to yourself? :D it's a circular reference – Anonymous Duck Sep 16 '16 at 03:52
  • 2
    @Sherlock since this is just a copy of an identical question (asked by someone else) about log4net. I just needed this information to be publicly documented and stackoverflow is very useful for this. – Steven Sep 16 '16 at 06:06
  • 2
    Note, passing `exception.Message` through as the message is suboptimal with Serilog; instead, if you have to deal with events without a message of their own, use a constant [message template](https://messagetemplates.org) like `{ExceptionMessage}` and parameterize it with `exception.Message`. This avoids attempting to parse the message as a template, and won't pollute the internal message template cache. Cheers! – Nicholas Blumhardt Oct 31 '18 at 03:08
  • Hi Nick, feel free to update my answer or add your own. – Steven Oct 31 '18 at 07:11

1 Answers1

12

So, my question is what is the proper way to create implementation that proxies to Serilog?

you should create something like:

public class SerilogAdapter : ILogger
{
    private readonly Serilog.ILogger m_Adaptee;

    public SerilogAdapter(Serilog.ILogger adaptee)
    {
        m_Adaptee = adaptee;
    }

    public void Log(LogEntry entry)
    {
        if (entry.Severity == LoggingEventType.Debug)
            m_Adaptee.Debug(entry.Exception, entry.Message);
        if (entry.Severity == LoggingEventType.Information)
            m_Adaptee.Information(entry.Exception, entry.Message);
        else if (entry.Severity == LoggingEventType.Warning)
            m_Adaptee.Warning(entry.Message, entry.Exception);
        else if (entry.Severity == LoggingEventType.Error)
            m_Adaptee.Error(entry.Message, entry.Exception);
        else
            m_Adaptee.Fatal(entry.Message, entry.Exception);
    }
}

Does that mean that every class that will log sth (so basically every), should have ILogger in its constructor?

As I understand from Stevens answer: Yes, you should do this.

what is the best way to use it later in the code?

If you are using a DI container, then just use the DI container to map ILogger to SerilogAdapter. You also need to register Serilog.ILogger, or just give an instance of Serilog logger to the DI container to inject it to the SerilogAdapter constructor.

If you don't use a DI container, i.e., you use Pure DI, then you do something like this:

Serilog.ILogger log = Serilog.Log.Logger.ForContext("MyClass");

ILogger logging_adapter = new SerilogAdapter(log);

var myobject = new MyClass(other_dependencies_here, logging_adapter);
rogaa
  • 370
  • 2
  • 16
Steven
  • 166,672
  • 24
  • 332
  • 435
  • How should this be used with the current dependency injection used in Core 2.2? Within ConfigureServices I have: services.AddSingleton(); Problem is, this throws the following error: InvalidOperationException: Unable to resolve service for type 'Serilog.ILogger' while attempting to activate 'MyProject.Logging.Adapters.SerilogAdapter' – RichieMN Sep 04 '19 at 20:06
  • @RichieMN you've probably solved this issue by now but, just to document it here - just add `services.AddSingleton(Serilog.Log.Logger);` too. – Quinton Smith Jun 12 '20 at 09:55
  • Thank you for the response! – RichieMN Jun 16 '20 at 21:13
  • Will the SerilogAdapter.Log pickup the CallerMember, LineNumber etc values from the calling class or will it always populate as SerilogAdapter class as the initiator ? for eg - if Class A is writing log by calling logging_adapter.log on line 10, then will the Serilog log message with Callermember as ClassA or SerilogAdapter? – Sai Jan 08 '21 at 08:52
  • Am I missing something or am I correct in my understanding this will negate the structured logging that Serilog supports? – quaabaam Aug 13 '21 at 19:33
  • Structured logging is not part of the abstraction as specified here. You should design the abstraction so it suits your application. – Steven Aug 13 '21 at 20:10