Editing Log4Net Messages Before They Reach Additives

I have a security tool that sends users a new password by email. The production email module (which I do not own and do not want to change) will log the entire text of the html email message using Log4Net when the threshold is VERBOSE. Since the letter contains the password of domain users in text form, I would like to remove the password from the log messages before it reaches the add-ons.

Is there a way to temporarily push an object onto the Log4Net stack that will allow me to search for a LoggingEvent message and modify it to mask any passwords that I find? I'd like to insert an object, call the email module, and then delete the object.

+2
c # logging log4net log4net-configuration appender
source share
7 answers

I would probably write a template converter. You can find an example here. Your implementation may be like this:

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

I had a similar problem and decided to inherit it from ForwardingAppender and then modified the LoggingEvent (using reflection) before passing it on.

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

It is not very beautiful, but it works.

Then you need the log4net configuration, which looks something like this:

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

and an implementation of GetModifiedMessage() that suits your needs and you're out!

+3
source share

A slight improvement in Chris Priest's solution: to inherit your appender not from ForwardingAppender, but from the AppenderSkeleton base class. This simplifies setup - you don’t need to refer to other applications from your application and can easily apply it to other registrars now

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

Using

 <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
source share

Another solution is to intercept LoggingEvent before it reaches any application directly from Logger. One of the prerequisites is the ability to change the root hierarchy before creating any registrar.

In the example below, we simply recreate the new LoggingEvent, but you do not need to, if you need an intensive copy of the memory, with reflection you can access the base LoggingEventData (is struct) and directly set the new values ​​to the fields.

You just need to call InterceptLoggerFactory.Apply () before any LogManager.GetLogger ().

 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); } } } 
+1
source share

You can try to intercept calls in log4net using the Unity application blocking method interceptor . Or you can write a custom log file.

0
source share

This improves the performance of @ jeremy-fizames, where you don't have to worry about whether ILoggerFactory is ILoggerFactory before the LogManager.GetLogger() any LogManager.GetLogger() . You can use the log4net plugin framework to ensure that this InterceptLoggerFactory installed before assigning any root registrar. The [assembly:] attribute ensures that log4net finds and loads the plugin before it configures the logging.

This solution works without changing the settings of existing applications and whether you work when loading the log4net configuration from XML and / or programmatically at runtime.

 // Register intercept as a log4net plugin [assembly: log4net.Config.Plugin(typeof(InterceptPlugin))] public class InterceptPlugin : log4net.Plugin.PluginSkeleton { public InterceptPlugin() : base("Intercept") {} public override void Attach(ILoggerRepository repository) { base.Attach(repository); ((Hierarchy)repository).LoggerFactory = new InterceptLoggerFactory(); } } // @jeremy-fizames ILoggerFactory public class InterceptLoggerFactory : ILoggerFactory { 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). 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
source share

log4net is open source, you can change it.

-3
source share

All Articles