Reputation: 8009
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="------------------------------------------ " />
<param name="Footer" value="------------------------------------------ " />
<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!
Upvotes: 4
Views: 3287
Reputation: 41
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.
Upvotes: 0
Reputation: 6451
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.
Upvotes: 1
Reputation: 2017
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.
Upvotes: 2
Reputation: 38397
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="------------------------------------------ " />
<param name="Footer" value="------------------------------------------ " />
<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
Upvotes: 2