Search code examples
c#loggingdependency-injectioninterceptorabstraction

C# Logging via DispatchProxy


Considering many possible ways to handle cross-cutting concerns like Logging, I think a good way to handle them is via Interception. (Also Mark Seemann in his book "Dependency Injection in .NET" mentions Interception as a valid solution for cross-cutting dependencies.)

But in my specific case I would like to use .Net Core and it's DispatchProxy. And for Logging Microsoft's Logging Extension.

It is quite easy to intercept method calls with a generic proxy and to log the calls on Debug level. But how do I log with an interceptor information logs that might occur inside the method? I had the idea to implement in each class that needs logging an empty Log() method that accepts abstract log information as parameter. So if the user needs to log anything informational he can call this method and provide the log content. When this method is called the interceptor can intercept and based on the argument distinguish from other methods. Subsequently the information will be logged without the caller needing to depend on the logger interface.

My only concern is that I might violate a principle? Or is there anything else to consider?

public class Foo 
{    
    public void someMethod()
    {
        // do stuff
        this.Log(new MyLog("Log this information"));
        // do stuff
    }

    private void Log(ILog mylog) { }
}


public Interface ILog
{
    string Message;
    Exception Ex;
    LogLevel level;
    // anything worth logging
}

Solution

  • But how do I log with an interceptor information logs that might occur inside the method?

    This is a scenario that is unsuited for Interception (both Dynamic Interception and Decoration). In that case the best approach is to inject a logging abstraction into your class using Constructor Injection:

    public class Foo 
    {
        private readonly ILog log;
    
        public Foo(ILog log) => this.log = log;
    
        public void someMethod()
        {
            // do stuff
            this.log.Log(new MyLog("Log this information"));
            // do stuff
        }
    }
    

    Although you could get this working using a virtual Log method that can be implemented using a Dynamic Proxy framework, this adds much more complexity compared to simply injecting that Volatile Dependency into the constructor. Also consider how you would go about testing those logging calls. You will have to create derived implementations of your components just for the purpose of testing.

    But before injecting loggers into your consumers, do ask yourself: Do I log too much?

    Also note that the second edition of Dependency Injection in .NET goes into more detail about good and bad logging strategies and also gives an alternative to Dynamic Interception (in chapter 10).