我正在考虑使用log4net作为我刚刚启动的新项目的首选日志框架。我在原型设计过程中遇到的一个问题是我无法找到明确的答案,那就是如何以可配置和整洁的方式清理或屏蔽邮件内容。
假设我说我希望有几个清洁工采取行动,但我也想遵循单一责任原则。一些更清洁的例子:
我知道你永远不应该用纯文本记录这种信息,执行日志的代码永远不会故意这样做。我希望得到最后一级保护,但是如果数据变得格式不正确并且敏感数据以某种方式滑入某个地方则不应该;日志是最糟糕的情况。
选项1:
我发现这篇StackOverflow文章详细介绍了一种可能的解决方案,但它涉及到使用反射。这对于性能来说是不可取的,但操作内部存储机制似乎也很麻烦。 Editing-log4net-messages-before-they-reach-the-appenders
选项2:
关于同一问题的建议答案建议使用PatternLayoutConverter。这适用于单个清理器操作,但您无法使用多个操作,如下所示:
public class CardNumberCleanerLayoutConverter : PatternLayoutConverter
{
protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
{
string message = loggingEvent.RenderedMessage;
// TODO: Replace with real card number detection and masking.
writer.Write(message.Replace("9", "*"));
}
}
<layout type="log4net.Layout.PatternLayout">
<converter>
<name value="cleanedMessage" />
<type value="Log4NetPrototype.CardNumberCleanerLayoutConverter, Log4NetPrototype" />
</converter>
<converter>
<name value="cleanedMessage" />
<type value="Log4NetPrototype.PasswordCleanerLayoutConverter, Log4NetPrototype" />
</converter>
<conversionPattern value="%cleanedMessage" />
</layout>
如上所示,在命名冲突的情况下,最后加载的转换器将是被操作的转换器。使用上面的示例,这意味着将清除密码,但不会清除卡号。
选项3:
我尝试过的第三个选项是使用链接的ForwarderAppender实例,但这很快使配置复杂化,我不认为它是一个理想的解决方案。因为LoggingEvent类具有不可变的RenderedMessage属性,所以我们无法在不创建LoggingEvent类的新实例的情况下更改它,如下所示:
public class CardNumberCleanerForwarder : ForwardingAppender
{
protected override void Append(LoggingEvent loggingEvent)
{
// TODO: Replace this with real card number detection and masking.
string newMessage = loggingEvent.RenderedMessage.Replace("9", "*");
// What context data are we losing by doing this?
LoggingEventData eventData = new LoggingEventData()
{
Domain = loggingEvent.Domain,
Identity = loggingEvent.Identity,
Level = loggingEvent.Level,
LocationInfo = loggingEvent.LocationInformation,
LoggerName = loggingEvent.LoggerName,
ExceptionString = loggingEvent.GetExceptionString(),
TimeStamp = loggingEvent.TimeStamp,
Message = newMessage,
Properties = loggingEvent.Properties,
ThreadName = loggingEvent.ThreadName,
UserName = loggingEvent.UserName
};
base.Append(new LoggingEvent(eventData));
}
}
public class PasswordCleanerForwarder : ForwardingAppender
{
protected override void Append(LoggingEvent loggingEvent)
{
// TODO: Replace this with real password detection and masking.
string newMessage = loggingEvent.RenderedMessage.Replace("4", "*");
// What context data are we losing by doing this?
LoggingEventData eventData = new LoggingEventData()
{
Domain = loggingEvent.Domain,
Identity = loggingEvent.Identity,
Level = loggingEvent.Level,
LocationInfo = loggingEvent.LocationInformation,
LoggerName = loggingEvent.LoggerName,
ExceptionString = loggingEvent.GetExceptionString(),
TimeStamp = loggingEvent.TimeStamp,
Message = newMessage,
Properties = loggingEvent.Properties,
ThreadName = loggingEvent.ThreadName,
UserName = loggingEvent.UserName
};
base.Append(new LoggingEvent(eventData));
}
}
匹配配置(非常难以遵循):
<log4net>
<appender name="LocatedAsyncForwardingAppender" type="Log4NetPrototype.LocatedAsyncForwardingAppender, Log4NetPrototype">
<appender-ref ref="CardNumberCleanerForwarder" />
</appender>
<appender name="CardNumberCleanerForwarder" type="Log4NetPrototype.CardNumberCleanerForwarder, Log4NetPrototype">
<appender-ref ref="PasswordCleanerForwarder" />
</appender>
<appender name="PasswordCleanerForwarder" type="Log4NetPrototype.PasswordCleanerForwarder, Log4NetPrototype">
<appender-ref ref="LogFileAppender" />
</appender>
<appender name="LogFileAppender" type="Log4NetPrototype.LogFileAppender, Log4NetPrototype">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%m" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="LocatedAsyncForwardingAppender" />
</root>
</log4net>
有没有人有另外一个建议如何实施,理论上可以以性能为代价配置n个清洁剂?
答案 0 :(得分:-1)
在你的问题中,你已经说过你应该去找原因而不记录任何敏感数据。这可以通过使用代码评审的第四个选项来强制执行,并查看记录的数据。您的日志记录语句永远不应记录任何敏感数据,因为这会导致安全风险。如果您对项目进行了更改,那么信任任何带过滤器的代码,敏感数据可能会失败。你的QA过程必须非常好地捕捉到这种错误(我从未见过测试人员通过所有日志)。因此,我会选择第4项,确保您不首先记录此类信息。