Peter's answer almost worked for me; it definitely set me on the right path because I needed a similar solution. What I had to do was subclass PatternConverter
:
public class ConfigurationSettingsConverter : PatternConverter
{
protected override void Convert(TextWriter writer, object state)
{
// use Option as a key to get a configuration value...
if (Option != null)
writer.Write(ConfigUtils.Setting[Option]);
}
}
and add this converter in the ActivateOptions
override of a subclass of PatternString
:
public class ConfigurationSettingsPatternString : PatternString
{
public ConfigurationSettingsPatternString()
{}
public ConfigurationSettingsPatternString(string pattern): base(pattern)
{}
public override void ActivateOptions()
{
AddConverter("cs", typeof(ConfigurationSettingsConverter));
base.ActivateOptions();
}
}
I originally tried to do this in the constructor as Peter answered, but the converter was not returned from the pattern string's underlying call to parse the source string. I also had to register a type converter (not to be confused with a PatternConverter
) anywhere in the code path before log4net was configured:
ConverterRegistry.AddConverter(
// type we want to convert to (from string)...
typeof(ConfigurationSettingsPatternString),
// the type of the type converter that will do the conversion...
typeof(ConfigurationSettingsPatternStringConverter));
Not doing this prevents log4net from being able to convert the value attribute in a FileAppender
's file node (i.e. a string) into a ConfigurationSettingsPatternString
. For example, in this configuration fragment,
<file
type="Some.Name.Space.ConfigurationSettingsPatternString, Some.Assembly"
value="some\path\MyLog.%cs{SomeKey}.log" />
%cs.{SomeKey}
would not get expanded, and log4net throws an exception. Here's the code for the type converter:
public class ConfigurationSettingsPatternStringConverter : 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(ConfigurationSettingsPatternString), source);
return new ConfigurationSettingsPatternString(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();
}
}
This turns out to work well for Windows services hosted in the same executable (for example, you might a %serviceName pattern as the file name to separate the services' logs.