4

My web app supports multiple instances, e.g. instance 1, 2, each of which logs data into its own file via log4net. That is I want to log data into different files based on instance id programmatically. The file path should be:

D:\Projects\Log\1\Reporting.log for instance 1

D:\Projects\Log\2\Reporting.log for instance 2

My web app support 3 instances, do I need to have 3 loggers in C#, which ONLY differ by its log file path, as shown above?

Below is Log4Net.config

 <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <configSections>
        <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
      </configSections>
      <log4net>

        <appender name="ExceptionLogFileAppender" type="log4net.Appender.RollingFileAppender">

          <file value=????How to specify this????? />
          <appendToFile value="true" />
          <rollingStyle value="Date" />
          <datePattern value="-yyyy-MM-dd.lo\g" />
          <param name="StaticLogFileName" value="false" />
          <layout type="log4net.Layout.PatternLayout">
            <param name="Header" value="------------------------------------------&#13;&#10;" />
            <param name="Footer" value="------------------------------------------&#13;&#10;" />
            <conversionPattern value="%d|[%t]|%-5p|%c|%m%n"/>
          </layout>
        </appender>
        <!-- Setup the root category, add the appenders and set the default level -->
        <root>
          <level value="ERROR" />
          <appender-ref ref="ExceptionLogFileAppender" />
        </root>

      </log4net>
    </configuration>

UPDATE

The instance id must be 1,2, etc.

The instance id would have more than 100 in the future.

Any idea would be much appreciated!

Pingpong
  • 7,681
  • 21
  • 83
  • 209

4 Answers4

2

Well, I can't think of a way to log each instance (1, 2, 3), but you can easily log them by their PID. I'd change the <file> element to:

<file type="log4net.Util.PatternString">
    <conversionPattern value="log\%processid\yourFileName.%date{yyyyMMMdd}.log" />
</file>

Then remove the element, that should give:

 <appender name="ExceptionLogFileAppender" type="log4net.Appender.RollingFileAppender">    
      <file type="log4net.Util.PatternString">
         <conversionPattern value="log\%processid\yourFileName.%date{yyyyMMMdd}.log" />
      </file>
      <appendToFile value="true" />
      <rollingStyle value="Date" />
      <param name="StaticLogFileName" value="false" />
      <layout type="log4net.Layout.PatternLayout">
          <param name="Header" value="------------------------------------------&#13;&#10;" />
          <param name="Footer" value="------------------------------------------&#13;&#10;" />
          <conversionPattern value="%d|[%t]|%-5p|%c|%m%n"/>
      </layout>
  </appender>

Then, each instance should log according to it's PID:

c:\log\13242\yourFileName.20111025.log

Or, you can make the pid part of the file name instead of a different directory, which I'd probably recommend so you don't litter the c:\log\ folder with multiple directories:

<file type="log4net.Util.PatternString">
    <conversionPattern value="log\yourFileName.%processid.%date{yyyyMMMdd}.log" />
</file>

This will give you files like:

c:\log\yourFileName.13142.20111025.log
c:\log\yourFileName.13152.20111025.log
James Michael Hare
  • 37,767
  • 9
  • 73
  • 83
  • Thanks for your advice, but the instance id must be 1,2, etc. – Pingpong Oct 25 '11 at 17:42
  • @landoncz's linked example is better. The process Id changes to often to be useful for distinguishing the different instances - an AppPool can be recycled at any time. – Joe Oct 25 '11 at 17:48
  • @Joe: true, just suggesting alternatives since there isn't an instance placeholder in log4net.Util.PatternString – James Michael Hare Oct 25 '11 at 17:51
2

See this example. Basically, if the number of instances is manageable and finite, you can create a log appender for each one. Then, you add a filter to each one, and set the StringToMatch property for each one to the instance ID.

Note that this is not completely dynamic in that you will need to specify each of these appenders ahead of time.

landoncz
  • 1,997
  • 14
  • 15
1

This should work, it's a bit of work and it's based on the answer by kit here.

Before calling XmlConfigurator.Configure(); add

ConverterRegistry.AddConverter(typeof(InstancePatternString), typeof(InstancePatternStringConverter));

Then add the following classes to your solution:

public class InstancePatternString  : PatternString
{
    public InstancePatternString(string pattern): base(pattern)
    {
    }

    public override void ActivateOptions()
    {
        AddConverter("cs", typeof(InstancePatternConverter));
        base.ActivateOptions();
    }
}

public class InstancePatternConverter  : PatternConverter 
{
    override protected void Convert(TextWriter writer, object state) 
    {
        switch(Option)
        {
            case "instance":
                writer.Write(MyContext.Instance);
                break;
        }
    }
}

public class InstancePatternStringConverter : IConvertTo, IConvertFrom
{
    public bool CanConvertFrom(Type sourceType)
    {
        return sourceType == typeof(string);
    }

    public bool CanConvertTo(Type targetType)
    {
        return typeof(string).IsAssignableFrom(targetType);
    }

    public object ConvertFrom(object source)
    {
        var pattern = source as string;
        if (pattern == null)
            throw ConversionNotSupportedException.Create(typeof(InstancePatternString), source);
        return new InstancePatternString(pattern);
    }

    public object ConvertTo(object source, Type targetType)
    {
        var pattern = source as PatternString;
        if (pattern == null || !CanConvertTo(targetType))
            throw ConversionNotSupportedException.Create(targetType, source);
        return pattern.Format();
    }
}

Make sure to change MyContext.Instance here to a statically accessible property representing your instance.

Finally change your web.config from:

<file value=????How to specify this????? />

to:

<file type="ConsoleApp.InstancePatternString, ConsoleApp" value="%cs{instance}\Reporting.log" />

Where ConsoleApp is the assembly in which you've added these classes. This will result in log files being created in seperate instance directories. i.e. 1\Reporting.log, 2\Reporting.log, etc.

The advantage of this approach is that adding future properties is quite easy and just requires an addition to the switch statement to be usable within any future log filenames / locations.

Community
  • 1
  • 1
Johannes Kommer
  • 6,401
  • 1
  • 39
  • 45
0

Not precisely a file system log solution, but what about logging to a DB table and including the instance detail? This would make it easy to differentiate instance-specific messages, and is almost zero maintenance and highly scaleable.

G Wood
  • 41
  • 1
  • 2