2

Every method in my project that uses whatever external resource (DB, web service call etc.) has to do logging. The problem is that I end up with a lot of somehow duplicated code. Sentences seem the same, but their parameters change (namely method name).

This seems a very good point to refactor my code. Apart from AOP libraries I was wondering how I could at least avoid magic strings and write a disposable object and wrap method body inside a using statement like so:

public void LoggedMethod(int param)
{
    using (new AutoLog())
    {
        // do whatever needs to be done
    }
}

My AutoLog class would be disposable which would write a log call at time of instantiation and disposal. I know I could likely (ab)use StackTrace class for this, but AFAIK this would slow down my methods considerably as this particular class is painfully slow.

My log entries should be (all of them of course contain method name):

  • start of a method call
  • end of the method call
  • (optional) method parameters - serialised of course (JSON?)
  • (optional) execution time

Question

How should I implement my AutoLog class to work as fast as possible? If I could also read method parameters it would be even better so I could serialize them and log them as well.

Robert Koritnik
  • 103,639
  • 52
  • 277
  • 404
  • 1
    Why not to use **Proxy** or **Decorator** patterns to implemented required functionality? You could either write your own implementation, or use Castle Project Dynamci Proxy. – Akim Jan 30 '13 at 15:03

1 Answers1

1

Every method in my project that uses whatever external resource (DB, web service call etc.) has to do logging

I can hardly believe that. You might be logging too much.

Sentences seem the same, but their parameters change (namely method name).

In that case you are probably missing some general abstraction in your code. In my experience you should:

have a general abstraction for code that is architecturally strongly related.

Take a look for instance at this article about command handlers and this article about repositories and custom queries. Both articles define a single generic interface around a architectural concept. (you should probably read at least the first article for the rest of my answer to make sense).

Using a generic interface, makes it very easy to wrap a whole set of related operations with a decorator. Take for instance this decorator that can be wrapped around all operations that execute a use case:

public class LoggingCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    private readonly ICommandHandler<TCommand> decoratee;
    private readonly ILogger logger;

    public LoggingCommandHandlerDecorator(
        ICommandHandler<TCommand> decoratee,
        ILogger logger)
    {
        this.decoratee = decoratee;
        this.logger = logger;
    }

    void ICommandHandler<TCommand>.Handle(TCommand command)
    {
        using (new AutoLog())
        {
            this.decoratee.Handle(command);
        }
    }
}

How should I implement my AutoLog class to work as fast as possible?

You know what, decorators are just a thin wrapper and there is almost no overhead. With this approach all the method arguments you would usually have are now packed in a single object. This is extremely convenient for logging, because you can simply serialize the whole TCommand to XML, JSon, or a more readable format and you will only have to write this code once.

And since you're worried about performance, since this LoggingCommandHandlerDecorator<TCommand> is a generic type, what you can do is add a static constructor to that type (static constructors run once per closed generic version of that interface) and precompile some code that allows you to efficiently serialize that command message. There will be absolutely nothing that can compete with that. Even AOP frameworks use reflection and build dynamic arrays with boxing when accessing the parameters of a method.

Note that there is nothing "poor man's" about applying decorators like this. However, you will quickly find yourself into trouble when you try this when your architecture does not follow the SOLID principles. Those principles are the key in getting flexible and maintainable software.

Community
  • 1
  • 1
Steven
  • 166,672
  • 24
  • 332
  • 435