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

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

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

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

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

我遇到了类似的问题,我通过inheritanceForwardingAppender然后在传递它之前修改LoggingEvent (使用reflection)来解决它。

 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); } ///  /// I couldn't figure out how to 'naturally' change the log message, so use reflection to change the underlying storage of the message data ///  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配置

               

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

Chris Priest解决方案的一个小改进:inheritance你的appender不是来自ForwardingAppender,而是来自基类AppenderSkeleton。 它使配置更简单 – 您不需要从您的配置中引用其他appender并立即将其应用于不同的记录器

 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); } ///  /// I couldn't figure out how to 'naturally' change the log message, so use reflection to change the underlying storage of the message data ///  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; } } 

用法

          

您可以尝试使用Unity Application Block方法拦截器拦截对log4net的调用。 或者您可以编写自定义log4net appender。

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

在下面的示例中,我们只是重新创建一个新的LoggingEvent,但如果您关心密集的内存副本则没有必要,通过reflection,您可以访问底层的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); } } } 

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