Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different appender per method

Just started using log4net and trying to get my head around the config and logger hierarchy. Is this hierarchy based on namespaces or class and method/function hierarchy?

Lets say I have the following class structure...

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

  public void Method1()
  {
    log4net.info("message");
  }

  public void Method2()
  {
    log4net.info("message");
  }

}

Is it possible to setup in the config for the log4net.info in method1 to use one appender and the log4net.info in method 2 to use another appender, even if they are off the same type e.g. SmtpAppender. If so how would the config look. here is my first attempt at it.

<appender name="SMTP1" type="log4net.Appender.SMTPAppender">
</appender>
<appender name="SMTP2" type="log4net.Appender.SMTPAppender">
</appender>


<logger name="MyClass.Method1">
    <level value="INFO" />
    <appender-ref ref="SMTP1" />
</logger>
<logger name="MyClass.Method2">
    <level value="INFO" />
    <appender-ref ref="SMTP2" />
</logger>
like image 367
Adrian S Avatar asked Jun 16 '11 21:06

Adrian S


2 Answers

The hierarchy is based on "names". What does that mean?
Well, you can specify a namespace in your logger xml (eg. Foo.Bar) and then fetch a logger for a class in that namespace using the GetLogger method which takes a Type. Any "sub" namespace under Foo.Bar will inherit Foo.Bar's logger config.
Alternatively, you can fetch a logger based on any old string using the GetLogger method which takes a string.

You can fetch loggers a couple of different ways. Most notably, by Type or by string.

Being able to fetch by string, you really can name your loggers anything and fetch them using anything. What you currently have won't work because log4net will fetch the logger based on the class... so you will be using the same logger for both methods.

For what you want to do you have to create two loggers:

public class MyClass
{
  private static readonly ILog log = LogManager.GetLogger(typeof(MyClass).Name + "." + "Method1");
  private static readonly ILog log2 = LogManager.GetLogger(typeof(MyClass).Name + "." + "Method2");

  public void Method1()
  {
    log.info("message");
  }

  public void Method2()
  {
    log2.info("message");
  }
}

Here is the same logger xml file:

<logger name="MyClass.Method1">
    <level value="INFO" />
    <appender-ref ref="SMTP1" />
</logger>
<logger name="MyClass.Method2">
    <level value="INFO" />
    <appender-ref ref="SMTP2" />
</logger>

I'm not too experienced with .Net, so maybe someone can find a better/more robust way to get the loggers for each method using some ninja Reflection, but that is the best I could do.

like image 173
Polaris878 Avatar answered Oct 19 '22 16:10

Polaris878


The same logger can't route to multiple appenders based on the current method, at least not directly. However, what you can do is use log4net's filters

  • http://logging.apache.org/log4net/release/sdk/log4net.Filter.html
  • http://logging.apache.org/log4net/release/sdk/log4net.Filter.FilterSkeleton.html

to do what you want. The log4net framework populates each LoggingEvent with the location information you need (e.g., method name) to do the filtering you want. You'll probably need to write a custom IFilter, though, to filter on that. IFilter is a pretty trivial interface to implement: 1 method/1property. Your filter's Decide() method makes 1 of 3 decisions on each logging event passed to it:

  • Deny. Drops the logging event. The event is not logged and no subsequent filters in the filter chain for the appender are consulted.
  • Neutral. The logging event is passed to the next filter in the filter chain for that appender.
  • Accept. The event is logged. No subsequent filters in the filter chain are consulted.

Then add your filter to the filter chain for each appender you want to have. That can be done on the fly, or it can be configure via your log4net.config.

You should be aware, though, that the quality of the location information you get is dependent on whether it's a Debug or Release build...and whether, I believe, it's got debugging symbols available.

You might also want to look at log4net's various contexts (and context stacks)

  • http://logging.apache.org/log4net/release/manual/contexts.html
  • http://www.beefycode.com/post/Log4Net-Tutorial-pt-6-Log-Event-Context.aspx

if you need to do add some contextual information on which to base your filtering.

like image 31
Nicholas Carey Avatar answered Oct 19 '22 15:10

Nicholas Carey