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.