You are asking how to do this with an attribute. The @Jonathan suggestion looks like it will probably work fine, but you could achieve a reasonably good result using the built-in features of log4net.
If you want to group classes into "categories", you can get a log based on the name of the category, not the name of the class. When you customize your output format, you can use the log formatting token to tell log4net to write the log name to the output file.
Normally, you could get a registrar based on the class name as follows:
public class Typical { private static readonly ILog logger = LogManager.GetLogger (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); public void F() { logger.Info("this message will be tagged with the classname"); } }
This is perfectly acceptable for retrieving registrars based on an arbitrary name:
public class A { private static readonly ILog logger = LogManager.GetLogger("REF"); public void F() { logger.Info("this message will be tagged with REF"); } } public class B { private static readonly ILog logger = LogManager.GetLogger("REF"); public void F() { logger.Info("this message will be tagged with REF"); } } public class C { private static readonly ILog logger = LogManager.GetLogger("UMP"); public void F() { logger.Info("this message will be tagged with UMP"); } }
In the previous example, classes A and B are considered to be in the same category, so they retrieve the logger with the same name. Class C is in a different category, so it got a registrar with a different name.
You can configure your registrars (in the configuration file) with your own category hierarchy:
App App.DataAccess App.DataAccess.Create App.DataAccess.Read App.DataAccess.Update App.DataAccess.Delete App.UI App.UI.Login App.UI.Query App.UI.Options
You can also configure the log output format to register only part of the registrar’s full name. Something like that:
%logger:2
Get the last 2 parts of the full name. For example, if your full class name is:
NameSpaceA.NameSpaceB.NameSpaceC.Class
Then the above format displays this as the log name:
NameSpaceC.Class
I am not 100% sure about the syntax, because I have not used it, and now I can not find a good example.
One of the drawbacks of this approach is that you need to define and remember what your categories are, and you must decide which category is appropriate for each class (you also have this problem if you want to decorate each class with an attribute containing its category). In addition, if you have several classes in the same category, you cannot enable or disable logging or change the level of logging for a subset of these classes.
It might be useful to write a single namespace from the namespace hierarchy:
Perhaps you can "classify" your classes based on their namespace. Thus, you might want to register the immediate parent class namespace as your category.
So, for the full class name above, you can write "NameSpaceC" as a "category" or log name.
I'm not sure you can do this out of the box using log4net, but you can easily write PatternLayoutConverter to get the registrar name and remove the class name and any higher level namespaces.
Here is a link to an example of a custom PatternLayoutConverter. Takes a parameter that in my case I wanted to use to search for values in a dictionary. In this case, the parameter can represent the offset from END from the full name of the registrar (the same interpretation as the log4net parameter embedded in the log name layout object), but additional code can be added to register ONLY one namespace on this index.
Custom property log4net PatternLayoutConverter (with index)
Again, given this full class name:
NameSpaceA.NameSpaceB.NameSpaceC.Class
You can consider the immediate parent namespace as a "category". If you defined a custom PatternLayoutConverter, category , and it accepted a parameter, then your configuration might look like this:
%category
By default, it returns a substring between the last and next to the last characters '.' . Given a parameter, it can return any discrete namespace to the chain.
PatternLayoutConverter might look something like this (untested):
class CategoryLookupPatternConverter : PatternLayoutConverter { protected override void Convert(System.IO.TextWriter writer, LoggingEvent loggingEvent) {
Or, using the Option property to get the Nth namespace name (relative to the end):
class CategoryLookupPatternConverter : PatternLayoutConverter { protected override void Convert(System.IO.TextWriter writer, LoggingEvent loggingEvent) {
@Jonathan’s idea is pretty cool, but it adds some extra coding on your part to define and support the new logger shell (but many people do this and don’t think this is a particularly burdensome burden). Of course, my own ideas for PatternLayoutConverter also require custom code on your part.
Another drawback is that GetCategory looks like it can be quite expensive to call every time the log is called.