views:

662

answers:

4

I have a web application with multiple web services. This web application has business and persistency layer. Is it possible to use separate log appender (log file) per web service?

This applies to web pages too. Essentially I want to separate my logs NOT based on the class or layer/namespace, but from point of entry, which can be web service or web page.

+1  A: 

You can define which logger to get in each web service:

namespace log4net
{
    public class LogManager
    {
     public static ILog GetLogger(string name);
     public static ILog GetLogger(Type type);
    }
}

And then in your app, you can call each logger appropriately:

public class MyApp 
{
    // Define a static logger variable so that it references the
    // Logger instance named "MyApp".
private static readonly ILog log = LogManager.GetLogger(typeof(MyApp));

static void Main(string[] args) 
{
 // Set up a simple configuration that logs on the console.
 BasicConfigurator.Configure();

 log.Info("Entering application.");
 Bar bar = new Bar();
 bar.DoIt();
 log.Info("Exiting application.");
}
}

Make sure you change the typeof to the right webservice.

Source and More information here.

George Stocker
A: 

Gortok, That's the typical way we handle logs. However I mean something different and I think I wasn't clear enough in my question.

Here's a hypothetical example: We have a web application that has some Admin pages and some Customer pages and we want to have two different admin.log and customer.log files for each set of pages. Both set of pages are using business layer (including CustomerService, OrderService, etc.) and repository layer CustomerRepository, OrderRepository, etc.). All layers and classes in application are being used in both sets of pages but we want to have the log separate. This makes it easier when an exception happens in admin pages and you go through a log that is relevant to track the problem. The solution that you mentioned which is the typical way to log things doesn't cover this situation because when you use typeof(MyApp) (MyApp being name of class or namespace) will not indicate what appender to use based on what page user initiated his request.

Maybe one way to handle this situation is to use a domain value object across the application and set its value to the appropriate logger when the request is initiated by the user in UI level and use that value as constructor parameter when you define your logger in each class. But I wonder if there's a better way to handle this approach in log4net that I don't know about.

Is my question clearer now?

Ali Bolourian
+1  A: 

I would suggest using Nested Diagnostic Contexts (NDCs) in each page and then filter log output based on the NDC.

The NDC information will be included in events logged from the current thread context, which will include page code, service code, repository code etc.

Using filtering on the NDC content you can separate log output from the various services.

Peter Lillevold
A: 

I could do this using PropertyFilter. I defined the following appender in config file:

<appender name="UiFileAppender" type="log4net.Appender.RollingFileAppender">
  <file value="Log1.log" />
  <appendToFile value="true" />
  <rollingStyle value="Size" />
  <maxSizeRollBackups value="10" />
  <maximumFileSize value="5MB" />
  <staticLogFileName value="true" />
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
  </layout>
  <filter type="log4net.Filter.PropertyFilter">
    <Key value="channel"/>
    <StringToMatch value="ui" />
    <AcceptOnMatch value="true" />
  </filter>
  <filter type="log4net.Filter.DenyAllFilter" />
</appender>
<appender name="WsFileAppender" type="log4net.Appender.RollingFileAppender">
  <file value="Log1.log" />
  <appendToFile value="true" />
  <rollingStyle value="Size" />
  <maxSizeRollBackups value="10" />
  <maximumFileSize value="5MB" />
  <staticLogFileName value="true" />
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
  </layout>
  <filter type="log4net.Filter.PropertyFilter">
    <Key value="channel"/>
    <StringToMatch value="ws" />
    <AcceptOnMatch value="true" />
  </filter>
  <filter type="log4net.Filter.DenyAllFilter" />
</appender>

The important part of the above appender is tags. When the user is using UI I set "channel" property to "ui" like following code:

log4net.GlobalContext.Properties["channel"] = "ui";

and when user uses web services I use the following code:

log4net.GlobalContext.Properties["channel"] = "ws";

I need to set "channel" property on highest level of entry when request is initiated.

Ali Bolourian