当我在appender中命名文件时,Log4Net找不到%username属性

时间:2009-10-14 00:20:20

标签: .net log4net

log4net没有为我的登录名做正确的PatternString替换。我希望我的日志是

  

日志\ YYYYMMDD \ MSMQcore_ [用户名]的.log

当我使用%username属性时,我在路径中获取域,这会在其中添加另一个文件夹间接。我只想要用户名。

  

日志\ YYYYMMDD \ MSMQcore_ [域] \ [用户名]的.log

任何人都有一个在appender的文件名中插入“用户名”的例子?我尝试了很多东西,我还在摸不着头脑。

<appender name="core_Appender" type="log4net.Appender.RollingFileAppender" >
<!-- <file type="log4net.Util.PatternString"  value="Logs/%date{yyyyMMdd}/MSMQcore_%identity.log" /> -->
<!-- <file type="log4net.Util.PatternString"  value="Logs/%date{yyyyMMdd}/MSMQcore_%property{user}.log" /> -->
<file type="log4net.Util.PatternString"  value="Logs/%date{yyyyMMdd}/MSMQcore_%username.log" />
</appender>

5 个答案:

答案 0 :(得分:9)

使用环境变量模式对我有用:

<file type="log4net.Util.PatternString" value="Logs\\%env{USERNAME}.txt" />

更新:如果USERNAME环境变量不是选项,则可以替代子类化PatternString。这是一个简单的实现:

public class MyPatternString : PatternString
{
    public MyPatternString()
    {
        AddConverter("usernameonly", typeof(UserNameOnlyConverter));
    }    
}

public class UserNameOnlyConverter : PatternConverter 
{
    override protected void Convert(TextWriter writer, object state) 
    {
        var windowsIdentity = WindowsIdentity.GetCurrent();
        if (windowsIdentity != null && windowsIdentity.Name != null)
        {
            var name = windowsIdentity.Name.Split('\\')[1];
            writer.Write(name);
        }
    }
}

新设置如下所示:

<file type="MyPatternString" value="Logs\\%usernameonly.txt" />

更新2:以回答%identity和%property {user}无效的原因:

%identity pattern选取当前线程上的identity属性。这个属性在我的测试中为null,可能是这样,直到为运行的线程分配一个特定的Windows标识。这在appender的上下文中不起作用,因为你不知道哪个线程会执行实际的附加。

%属性模式从GlobalContext和ThreadContext类中获取属性。默认情况下,只在GlobalContext中注册 log4net:HostName (LoggingEvent.HostNameProperty)。因此,除非您在这些上下文中主动注册属性,否则不能将它们与%属性模式一起使用。同样,ThreadContext在appender的上下文中是无用的,因为你无法知道哪个线程将执行追加。

也就是说,在应用程序启动例程中的某个地方注册GlobalContext.Properties集合中名为 username 的属性,将使%property {username}能够按预期工作。

答案 1 :(得分:3)

彼得的回答几乎为我工作;它肯定让我走上了正确的道路,因为我需要一个类似的解决方案。我必须做的是子类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]);
    }
}

并在ActivateOptions的子类的PatternString覆盖中添加此转换器:

public class ConfigurationSettingsPatternString : PatternString
{
    public ConfigurationSettingsPatternString()
    {}

    public ConfigurationSettingsPatternString(string pattern): base(pattern)
    {}

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

我最初尝试在构造函数中执行此操作,因为Peter回答,但转换器未从模式字符串的底层调用返回以解析源字符串。在配置 log4net 之前,我还必须在代码路径中的任何位置注册一个类型转换器(不要与PatternConverter混淆):

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));

不执行此操作可防止 log4net 转换FileAppender文件节点中的属性(即一个字符串)到ConfigurationSettingsPatternString。例如,在此配置片段中,

<file
  type="Some.Name.Space.ConfigurationSettingsPatternString, Some.Assembly"
  value="some\path\MyLog.%cs{SomeKey}.log" />

%cs.{SomeKey}不会展开, log4net 会引发异常。这是类型转换器的代码:

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();
    }
}

这适用于托管在同一可执行文件中的Windows多个服务(例如,您可以添加%serviceName 模式作为文件名以分隔服务的日志。

答案 2 :(得分:1)

使用“%username”对我有用;

<parameter>
    <parameterName value="@identity" />
    <dbType value="String" />
    <size value="255" />
    <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%username" />
    </layout>
</parameter>

但是我再次处于标准WinForms应用程序的上下文中,而不是ASP.NET应用程序。不确定这是否是您正在寻找的。

答案 3 :(得分:1)

Kit的解决方案只有一项改进: 使用属性

[TypeConverter("namespace.ConfigurationSettingsPatternStringConverter")]
public class ConfigurationSettingsPatternString : PatternString
{

致电

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));

不再需要。

答案 4 :(得分:0)

<layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="Running on ${COMPUTERNAME} / ${USERNAME} %newline %logger %date%newline Thread ID=[%thread]%newline %-5level - %message%newline" />
</layout>

这对我有用。