Intercept or Decorate calls to ILogger

2019-05-07 15:41发布

I'm currently using castle windsor, along with it's logging facility in my application.

However, in my logging I would like to include some contextual information that is not within the logged message, but stored within the CallContext.

I have tried to do this by intercepting the calls to ILogger using the following:

internal class Program
{
    private static void Main(string[] args)
    {
        var container = new WindsorContainer();
        container.AddFacility<LoggingFacility>(f => f.UseNLog());
        container.Kernel.Resolver.AddSubResolver(new LoggerResolver(container.Kernel));

        var logger = container.Resolve<ILogger>();
    }
}

public class LoggerResolver: ISubDependencyResolver
{
    private readonly IKernel _kernel;
    private static ProxyGenerator _proxyGenerator;

    static LoggerResolver()
    {
        _proxyGenerator = new ProxyGenerator();
    }

    public LoggerResolver(IKernel kernel)
    {
        _kernel = kernel;
    }

    public bool CanResolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency)
    {
        return dependency.TargetType == typeof(ILogger);
    }

    public object Resolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency)
    {
        return _proxyGenerator.CreateInterfaceProxyWithTarget(_kernel.Resolve<ILogger>(), new LoggingInterceptor());
    }
}

public class LoggingInterceptor: IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        //Some modification to message here
        invocation.Proceed();
    }
}

But the variable logger is of type NLog.Loggger rather than the dynamic proxy I was expecting.

1条回答
走好不送
2楼-- · 2019-05-07 15:58

Some digging with ILSpy seems to show that you won't be able to intercept calls to the ILogger instantiation. The ILogger intance is created directly by the NLogFactory (same thing for all logging factories I looked into, log4net, console, etc)

// Castle.Services.Logging.NLogIntegration.NLogFactory
public override ILogger Create(string name)
{
    Logger logger = LogManager.GetLogger(name);
    return new NLogLogger(logger, this);
}

If this is really necessary you should be able to inherit the factories you're interested into (basically, NLogFactory and ExtendedNLogFactory) in order to override the ILogger creation. Then create a instance-based proxy of the ILogger and plug in your custom interceptors. Finally inform the LoggingFacility that you want to use a custom factory.

I started being pessimist about your problem but I found the solution to be quite simple to implement, so you shouldn't have too many problems to achieve what you want. Code? Of course :)

public class MyFactory : NLogFactory
{
    public override ILogger Create(string name)
    {
        var il = base.Create(name);
        var pg =  new ProxyGenerator();
        return pg.CreateInterfaceProxyWithTarget<ILogger>(il, new LoggingInterceptor());
    }
}

public class LoggingInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        //Some modification to message here
        invocation.Proceed();
    }
}

internal class Program
{
    private static void Main(string[] args)
    {
        var container = new WindsorContainer();
        container.AddFacility<LoggingFacility>(f => f.UseNLog().LogUsing<MyFactory>());

        var logger = container.Resolve<ILogger>();
        logger.Debug("data"); // we intercept this call, success
        Console.ReadLine();
    }
}
查看更多
登录 后发表回答