首页 文章

在Log4Net消息到达appender之前编辑它们

提问于
浏览
2

我有一个安全工具,通过电子邮件向用户发送新密码 . 当阈值为VERBOSE时, 生产环境 电子邮件模块(我不拥有并且不想更改)将使用Log4Net记录整个html电子邮件消息正文 . 由于电子邮件包含明文的域用户密码,因此我想在日志消息到达appender之前删除密码 .

有没有办法让我临时插入一个对象到Log4Net堆栈,这将允许我搜索LoggingEvent消息并更改它以掩盖我找到的任何密码?我想插入对象,调用电子邮件模块,然后删除该对象 .

6 回答

  • 2

    我可能会写一个模式转换器 . 你可以找到一个例子here . 您的实现可能是这样的:

    protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
    {
        string msg = loggingEvent.RenderedMessage;
        // remove the password if there is any
        writer.Write(msg);
    }
    
  • 3

    我有一个类似的问题,我通过继承 ForwardingAppender 然后在传递之前修改 LoggingEvent (使用反射)来解决它 .

    using System.Reflection;
    using log4net.Appender;
    using log4net.Core;
    
    class MessageModifyingForwardingAppender : ForwardingAppender
    {
        private static FieldInfo _loggingEventm_dataFieldInfo;
    
        public MessageModifyingForwardingAppender()
        {
            _loggingEventm_dataFieldInfo = typeof(LoggingEvent).GetField("m_data", BindingFlags.Instance | BindingFlags.NonPublic);
        }
    
        protected override void Append(LoggingEvent loggingEvent)
        {
            var originalRenderedMessage = loggingEvent.RenderedMessage;
    
            var newMessage = GetModifiedMessage(originalRenderedMessage);
    
            if (originalRenderedMessage != newMessage)
                SetMessageOnLoggingEvent(loggingEvent, newMessage);
    
            base.Append(loggingEvent);
        }
    
        /// <summary>
        /// I couldn't figure out how to 'naturally' change the log message, so use reflection to change the underlying storage of the message data
        /// </summary>
        private static void SetMessageOnLoggingEvent(LoggingEvent loggingEvent, string newMessage)
        {
            var loggingEventData = (LoggingEventData)_loggingEventm_dataFieldInfo.GetValue(loggingEvent);
            loggingEventData.Message = newMessage;
            _loggingEventm_dataFieldInfo.SetValue(loggingEvent, loggingEventData);
        }
    
        private static string GetModifiedMessage(string originalMessage)
        {
            // TODO modification implementation
            return originalMessage;
        }
    }
    

    它不是很漂亮,但它有效 .

    然后你需要一个看起来像这样的log4net配置

    <log4net>
        <appender name="ModifyingAppender" type="Your.Lib.Log4Net.MessageModifyingForwardingAppender,Your.Lib">
            <appender-ref ref="ConsoleAppender" />
        </appender>
        <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%date %-5level [%thread] %logger: %message%newline"/>
            </layout>
        </appender>
        <root>
            <level value="INFO"/>
            <appender-ref ref="ModifyingAppender"/>
        </root>
    </log4net>
    

    以及适合您需求的 GetModifiedMessage() 实施,您就不在了!

  • 0

    Chris Priest解决方案的一个小改进:继承你的appender不是来自ForwardingAppender,而是来自基类AppenderSkeleton . 它使配置更简单 - 您不需要从您的配置中引用其他appender并立即将其轻松应用于不同的 Logger

    public class PasswordObfuscationAppender : AppenderSkeleton
    {
        private static readonly FieldInfo LoggingEventmDataFieldInfo = typeof(LoggingEvent).GetField(
            "m_data",
            BindingFlags.Instance | BindingFlags.NonPublic);
    
        protected override void Append(LoggingEvent loggingEvent)
        {
            var originalRenderedMessage = loggingEvent.RenderedMessage;
    
            var newMessage = GetModifiedMessage(originalRenderedMessage);
    
            if (originalRenderedMessage != newMessage)
                SetMessageOnLoggingEvent(loggingEvent, newMessage);
        }
    
        /// <summary>
        /// I couldn't figure out how to 'naturally' change the log message, so use reflection to change the underlying storage of the message data
        /// </summary>
        private static void SetMessageOnLoggingEvent(LoggingEvent loggingEvent, string newMessage)
        {
            var loggingEventData = (LoggingEventData)LoggingEventmDataFieldInfo.GetValue(loggingEvent);
            loggingEventData.Message = newMessage;
            LoggingEventmDataFieldInfo.SetValue(loggingEvent, loggingEventData);
        }
    
        private static string GetModifiedMessage(string originalMessage)
        {
            // TODO modification implementation
            return originalMessage;
        }
    }
    

    用法

    <appender name="PasswordObfuscationAppender" type="Foundation.PasswordObfuscationAppender,Foundation" />
    
    <appender name="MainAppender" type="log4net.Appender.RollingFileAppender">
      <file value="..\Logs\File.log" />
    </appender>
    
    <root>
      <level value="DEBUG" />
      <appender-ref ref="PasswordObfuscationAppender" />
      <appender-ref ref="MainAppender" />
    </root>
    
  • -3

    您可以尝试使用Unity Application Block method interceptor拦截对log4net的调用 . 或者您可以编写自定义log4net appender .

  • 2

    另一种解决方案是在直接从Logger到达任何appender之前拦截LoggingEvent . 一个先决条件是能够在创建任何Logger之前修改根层次结构 .

    在下面的示例中,我们只是重新创建一个新的LoggingEvent,但如果您关心密集的内存复制则没有必要,通过反射,您可以访问底层的LoggingEventData(是struct)并直接为字段设置新值 .

    您只需要在任何LogManager.GetLogger()之前调用InterceptLoggerFactory.Apply() .

    public class InterceptLoggerFactory : ILoggerFactory
    {
        public static void Apply() => Apply((Hierarchy)LogManager.GetRepository());
        public static void Apply(Hierarchy h) => h.LoggerFactory = new InterceptLoggerFactory();
    
        public Logger CreateLogger(ILoggerRepository repository, string name)
        {
            if (name == null) return new InterceptRootLogger(repository.LevelMap.LookupWithDefault(Level.Debug));
            return new InterceptLogger(name);
        }
    
    
        class InterceptLogger : Logger
        {
            public InterceptLogger(string name) : base(name)
            {
            }
    
            protected override void CallAppenders(LoggingEvent loggingEvent)
            {
                // Implement interception of property on loggingEvent before any call to any appender (execution is sync).
                /*
                 * var loggingEventData = loggingEvent.GetLoggingEventData();
                 * loggingEventData.Message = [EncryptMessage](loggingEventData.Message);
                 * var newLoggingEvent = new LoggingEvent(loggingEventData);
                 * base.CallAppenders(newLoggingEvent);
                 * */
                base.CallAppenders(loggingEvent);
            }
        }
    
        class InterceptRootLogger : RootLogger
        {
            public InterceptRootLogger(Level level) : base(level)
            {
            }
    
            protected override void CallAppenders(LoggingEvent loggingEvent)
            {
                // Implement interception of property on loggingEvent before any call to any appender (execution is sync).
                base.CallAppenders(loggingEvent);
            }
        }
    }
    
  • 0

    log4net是开源的,你可以修改它 .

相关问题