Log4Net can't find %username property when I name the file in my appender

前端 未结 5 1279
滥情空心
滥情空心 2020-12-19 10:48

log4net doesn\'t do the correct PatternString substitution for my login name. I want my log to be

Logs\\YYYYMMDD\\MSMQcore_[username].lo

相关标签:
5条回答
  • 2020-12-19 11:27

    Just one improvement to the Kit's solution: Use attribute

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

    And the call to

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

    is no longer required.

    0 讨论(0)
  • 2020-12-19 11:30
    <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="Running on ${COMPUTERNAME} / ${USERNAME} %newline %logger %date%newline Thread ID=[%thread]%newline %-5level - %message%newline" />
    </layout>
    

    This worked well for me.

    0 讨论(0)
  • 2020-12-19 11:36

    Using "%username" works for me;

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

    But then again I am in the context of a standard WinForms application, not an ASP.NET app. Not sure if this is what you're looking for.

    0 讨论(0)
  • 2020-12-19 11:43

    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 multiple services hosted within the same executable (for example, you might add a %serviceName pattern as the file name to separate the services' logs.

    0 讨论(0)
  • 2020-12-19 11:44

    Using the environment variable pattern works for me:

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

    Update: if the USERNAME environment variable is not an option, subclassing PatternString could be an alternative. Here is a simple implementation:

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

    The new setting will look like this:

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

    Update 2: to answer why %identity and %property{user} doesn't work:

    The %identity pattern picks up the identity property on the current thread. This property is in my tests null, and is probably so until one assigns a specific Windows identity to the running thread. This will not work in the context of the appender because you will not know which thread will perform the actual appending.

    The %property pattern picks up properties from the GlobalContext and ThreadContext classes. By default, only the log4net:HostName (LoggingEvent.HostNameProperty) is registered in the GlobalContext. So unless you actively register properties in those contexts you cannot use them with the %property pattern. Again, ThreadContext is useless in the context of the appender since you have no way of knowing which thread will be doing the appending.

    That said, registering a property called username in the GlobalContext.Properties collection, somewhere in the application startup routine perhaps, will enable the %property{username} to work as expected.

    0 讨论(0)
提交回复
热议问题