Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class decorator to declare static member (e.g., for log4net)?

I'm using log4net, and we have a lot of this in our code:

public class Foo {
    private static readonly ILog log = LogManager.GetLogger(typeof(Foo));
    ....
}

One downside is that it means we're pasting this 10-word section all over, and every now and then somebody forgets to change the class name. The log4net FAQ also mentions this alternative possibility, which is even more verbose:

public class Foo {
    private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    ...
}

Is it possible to write a decorator to define this? I'd really like to say simply:

[LogMe]  // or perhaps: [LogMe("log")]
public class Foo {
    ...
}

I've done similar things in other languages, but never a statically-compiled language like C#. Can I define class members from a decorator?

Edit: Heh. I'm a Lisp programmer. I appreciate the suggestions to switch languages, but really, if I was going to switch languages for better metaprogramming capabilities, I'd go all the way to Lisp instead of being half-assed about it. Unfortunately using a different language isn't an option on this project.

like image 328
Ken Avatar asked May 17 '10 21:05

Ken


4 Answers

This is exactly a task for AOP - Aspect Oriented Programming. Have a look at PostSharp, it's a .NET AOP framework, it will allow you to do exactly what you want.

This works by modifying (or weaving) IL code in post-compilation, and adding the logging aspect to the decorated method.

EDIT: It appears that PostSharp now is a commercial product. If you're looking for an open-source (free) solution, I suggest LinFu AOP.

like image 200
Igal Tabachnik Avatar answered Oct 23 '22 15:10

Igal Tabachnik


We use something almost identical:

private static readonly ILog Logger = LogManager.GetLogger();

This method is implemented as follows:

[MethodImpl(MethodImplOptions.NoInlining)]
public static ILog GetLogger()
{
    Type t;
    try { t = new StackFrame(1, false).GetMethod().DeclaringType; }
    catch { t = typeof(UnknownObject); }
    return GetLogger(t);
}
private static class UnknownObject { }

It still proves to be more of a pain that I'm willing to go through. I much prefer a static object with static methods for logging. Obtaining the calling method is not that expensive provided your not getting file info. Compared to the expense of just calling Log4Net obtaining the calling type is nothing (depending some on the loggers used).

like image 34
csharptest.net Avatar answered Oct 23 '22 17:10

csharptest.net


Attributes in .NET are not decorators / active components that influence the class/member they're attached to. Attributes are meta-data that can be retrived with reflection. There is no decorator-like facility in C#, although there are AOP solutions that extend .NET with what you want. The simplest solution is probably to just copy that line to each class though.

like image 1
dtb Avatar answered Oct 23 '22 15:10

dtb


We wrapped log4net so we could switch it out with ease. It is something that we could very possibly change our mind on in the future.

While we are not doing this, and you will probably take a performance hit to do so....and I'm really hesitant to even suggest it because I'm not sure it is a good idea.....you could...if you felt devilish enough....wrap log4net and do something like this in your wrapper.

var callingType = new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().DeclaringType

If you're smart about how you're logging you could only incur that cost when you need the log message.

like image 1
Mark Avatar answered Oct 23 '22 17:10

Mark