I have a security tool that sends users their new password through email. The production email module (that I don’t own and don’t want to change) will log the entire html email message body using Log4Net when the threshold is VERBOSE. Since the email contains a domain user’s password in clear text, I would like to remove the password from the log messages before it reaches the appenders.
Is there a way for me to temporary insert an object into the Log4Net stack that would allow me to search the LoggingEvent message and alter it to mask out any passwords that I find? I’d like to insert the object, call the email module, and then remove the object.
I had a similar problem, and I solved it by inheriting from ForwardingAppender and then modifying 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's not very pretty, but it works.
Then you need a log4net config that 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 need, and you are away!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With