按对象类型列出的java(log4j)日志过滤器
我有一个log4j当前实现的日志语句:按对象类型列出的java(log4j)日志过滤器,java,logging,filter,log4j,Java,Logging,Filter,Log4j,我有一个log4j当前实现的日志语句: log.info("Failed to create message for {}", CustomerData); 这将在CustomerData中记录一些敏感数据 是否有方法阻止对CustomerData的任何实例进行日志记录?可能在log4j配置中,或者通过自定义过滤器 如果log4j无法实现,那么其他日志框架如何?防止这种情况的最简单方法是编写/覆盖您的CustomerData.toString()方法 除此之外,您可以扩展slf4j,但不要问我
log.info("Failed to create message for {}", CustomerData);
这将在CustomerData
中记录一些敏感数据
是否有方法阻止对CustomerData
的任何实例进行日志记录?可能在log4j配置中,或者通过自定义过滤器
如果log4j无法实现,那么其他日志框架如何?防止这种情况的最简单方法是编写/覆盖您的
CustomerData.toString()
方法
除此之外,您可以扩展slf4j,但不要问我如何做
在Log4j/SLF4j/任何日志框架中都没有提供(可能永远不会提供)
为了满足您的特定需求,最简单的方法是拥有自己的记录器装饰器
它可以是SLF4J或Log4j2的自定义日志记录实现。或者只是针对记录器的某种工厂(例如,类似于SLF4j中的LoggerFactory,或者针对Log4j2的Logger.getLogger()
)
它可以在内部创建自定义记录器实现,将其委托给真正的记录器,并且您可以在日志impl中执行额外的检查
e、 g.(psuedo代码)
但是正如您所看到的,有很多缺点,比如性能下降
如果您正在使用Logback(我相信您也可以对Log4j2执行类似的操作),那么您可以实现自己的Appender或编码器等
登录Logback时,它会在内部创建一个日志事件,其中包含日志消息和参数。因此,您不必在appender(或编码器等)中实际格式化日志消息,只需进行参数检查,如果异常看起来不正确,则抛出异常
此方法的注意事项:
-仅当启用日志级别时,才会到达Appender。因此,若在配置中将日志级别设置为警告,那个么您将无法捕获由logger.info(“message{}”,senstive)完成的日志消息代码>
-它更多地与您正在使用的日志实现的内部实现有关,这意味着很难切换到其他实现(我相信这在现实生活中是罕见的)
优点是,如果您还没有自己的日志API,那么可以使用SLF4J/Log4j2 API来保存代码更改
编辑:
刚刚在Log4j2中检查,它允许您替换MessageFactory(用于基于message+params构造消息字符串)
信息工厂
MessageFactory用于生成消息对象。
应用程序可能会取代标准的ParameterizedMessageFactory(或
ReusableMessageFactory(在无垃圾模式下)通过设置
将系统属性log4j2.messageFactory设置为自定义
MessageFactory类
Logger.entry()和Logger.exit()方法的流消息具有
独立的工厂。申请可取代
通过设置系统属性的值来设置DefaultFlowMessageFactory
log4j2.flowMessageFactory设置为自定义flowMessageFactory的名称
班级
因此,与上述方法类似,您可以创建自己的MessageFactory
,进行额外的参数检查Log4j2提供了许多方法来实现这一点:
- 过滤器
- 重写日志事件
过滤器
Log4j2允许您在特定记录器、特定附加器或全局上进行配置(因此过滤器适用于所有日志事件)。过滤器提供的功能是强制接受日志事件、强制拒绝日志事件或保持“中立”。在您的情况下,您可能希望拒绝包含敏感数据的日志事件
您可以创建(有关如何安装自定义过滤器的信息,请参阅),也可以使用一些内置过滤器。对于您的目的,内置或a都应该足够了
正则表达式过滤器示例
假设这是一个包含敏感数据的类:
public class Customer {
public String name;
public String password;
@Override
public String toString() {
return "Customer[name=" + name + ", password=" + password + "]";
}
}
您的应用程序日志如下所示:
public class CustomerLoggingApp {
public static void main(String[] args) {
Logger log = LogManager.getLogger();
Customer customer = new Customer();
customer.name = "Jesse Zhuang";
customer.password = "secret123";
log.info("This is sensitive and should not be logged: {}", customer);
log.info("But this message should be logged.");
}
}
您可以配置一个正则表达式过滤器,用于查看格式化(或未格式化)消息,并拒绝任何包含单词“Customer
”后跟“,password=
”的日志消息:
重写日志事件
另一个有趣的选择是重写日志事件,这样消息就不会被完全过滤掉,而只是屏蔽敏感数据。例如,在日志中将密码字符串替换为“***”
为此,您需要创建一个。从手册中:
RewriteAppender允许在运行日志事件之前对其进行操作
由另一个附加程序处理。这可用于遮罩敏感区域
密码等信息或将信息注入每个
事件
重写策略示例:
package com.jesse.zhuang;
import org.apache.logging.log4j.core.Core;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.ObjectMessage;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.message.ReusableMessage;
@Plugin(name = "MaskSensitiveDataPolicy", category = Core.CATEGORY_NAME,
elementType = "rewritePolicy", printObject = true)
public class MaskSensitiveDataPolicy implements RewritePolicy {
private String[] sensitiveClasses;
@PluginFactory
public static MaskSensitiveDataPolicy createPolicy(
@PluginElement("sensitive") final String[] sensitiveClasses) {
return new MaskSensitiveDataPolicy(sensitiveClasses);
}
private MaskSensitiveDataPolicy(String[] sensitiveClasses) {
super();
this.sensitiveClasses = sensitiveClasses;
}
@Override
public LogEvent rewrite(LogEvent event) {
Message rewritten = rewriteIfSensitive(event.getMessage());
if (rewritten != event.getMessage()) {
return new Log4jLogEvent.Builder(event).setMessage(rewritten).build();
}
return event;
}
private Message rewriteIfSensitive(Message message) {
// Make sure to switch off garbage-free logging
// by setting system property `log4j2.enable.threadlocals` to `false`.
// Otherwise you may get ReusableObjectMessage, ReusableParameterizedMessage
// or MutableLogEvent messages here which may not be rewritable...
if (message instanceof ObjectMessage) {
return rewriteObjectMessage((ObjectMessage) message);
}
if (message instanceof ParameterizedMessage) {
return rewriteParameterizedMessage((ParameterizedMessage) message);
}
return message;
}
private Message rewriteObjectMessage(ObjectMessage message) {
if (isSensitive(message.getParameter())) {
return new ObjectMessage(maskSensitive(message.getParameter()));
}
return message;
}
private Message rewriteParameterizedMessage(ParameterizedMessage message) {
Object[] params = message.getParameters();
boolean changed = rewriteSensitiveParameters(params);
return changed ? new ParameterizedMessage(message.getFormat(), params) : message;
}
private boolean rewriteSensitiveParameters(Object[] params) {
boolean changed = false;
for (int i = 0; i < params.length; i++) {
if (isSensitive(params[i])) {
params[i] = maskSensitive(params[i]);
changed = true;
}
}
return changed;
}
private boolean isSensitive(Object parameter) {
return parameter instanceof Customer;
}
private Object maskSensitive(Object parameter) {
Customer result = new Customer();
result.name = ((Customer) parameter).name;
result.password = "***";
return result;
}
}
这将使上述示例程序产生以下输出。请注意,密码被屏蔽,但敏感对象的其他属性被保留:
2018-01-09 22:18:30,561 INFO CustomerLoggingApp This is sensitive and should not be logged: Customer[name=Jesse Zhuang, password=***]
2018-01-09 22:18:30,569 INFO CustomerLoggingApp But this message should be logged.
不清楚您要问什么。CustomerData.toString()
方法是什么样子的?@stdunbar CustomerData.toString将被记录。例如,“客户姓名:Jon Smith,客户性别:男性,客户年龄:25岁,客户余额:$100”。正如您所见,这包含敏感数据,我想知道是否有任何方法可以设置一个过滤器来阻止记录CustomerData
@romac类型的任何对象,您对此有何澄清或具体疑问?我认为这是一个简单而直接的问题。我想问的是排除或阻止某一类型/类别的任何对象的可能性CustomerData
,以及如果可能的话如何做到这一点。@JesseZhuang我认为这个问题有点混乱。当您键入log.info(“abcdefgh{},someStringHere)时,它将记录字符串“abcdefgh”+不管someStringHere的值是什么。因此,在您的情况下,无论CustomerData.toString()的结果是什么,都将在记录之前连接到上一个字符串的末尾。如果你
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<RegexFilter regex=".*Customer.*, password=.*" onMatch="DENY" onMismatch="NEUTRAL"/>
<PatternLayout>
<pattern>%d %level %c %m%n</pattern>
</PatternLayout>
</Console>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<ScriptFilter onMatch="DENY" onMisMatch="NEUTRAL">
<Script name="DropSensitiveObjects" language="groovy"><![CDATA[
parameters.any { p ->
// DENY log messages with Customer parameters
p.class.name == "Customer"
}
]]>
</Script>
</ScriptFilter>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout>
<pattern>%d %level %c %m%n</pattern>
</PatternLayout>
</Console>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>
package com.jesse.zhuang;
import org.apache.logging.log4j.core.Core;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.impl.Log4jLogEvent;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.ObjectMessage;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.message.ReusableMessage;
@Plugin(name = "MaskSensitiveDataPolicy", category = Core.CATEGORY_NAME,
elementType = "rewritePolicy", printObject = true)
public class MaskSensitiveDataPolicy implements RewritePolicy {
private String[] sensitiveClasses;
@PluginFactory
public static MaskSensitiveDataPolicy createPolicy(
@PluginElement("sensitive") final String[] sensitiveClasses) {
return new MaskSensitiveDataPolicy(sensitiveClasses);
}
private MaskSensitiveDataPolicy(String[] sensitiveClasses) {
super();
this.sensitiveClasses = sensitiveClasses;
}
@Override
public LogEvent rewrite(LogEvent event) {
Message rewritten = rewriteIfSensitive(event.getMessage());
if (rewritten != event.getMessage()) {
return new Log4jLogEvent.Builder(event).setMessage(rewritten).build();
}
return event;
}
private Message rewriteIfSensitive(Message message) {
// Make sure to switch off garbage-free logging
// by setting system property `log4j2.enable.threadlocals` to `false`.
// Otherwise you may get ReusableObjectMessage, ReusableParameterizedMessage
// or MutableLogEvent messages here which may not be rewritable...
if (message instanceof ObjectMessage) {
return rewriteObjectMessage((ObjectMessage) message);
}
if (message instanceof ParameterizedMessage) {
return rewriteParameterizedMessage((ParameterizedMessage) message);
}
return message;
}
private Message rewriteObjectMessage(ObjectMessage message) {
if (isSensitive(message.getParameter())) {
return new ObjectMessage(maskSensitive(message.getParameter()));
}
return message;
}
private Message rewriteParameterizedMessage(ParameterizedMessage message) {
Object[] params = message.getParameters();
boolean changed = rewriteSensitiveParameters(params);
return changed ? new ParameterizedMessage(message.getFormat(), params) : message;
}
private boolean rewriteSensitiveParameters(Object[] params) {
boolean changed = false;
for (int i = 0; i < params.length; i++) {
if (isSensitive(params[i])) {
params[i] = maskSensitive(params[i]);
changed = true;
}
}
return changed;
}
private boolean isSensitive(Object parameter) {
return parameter instanceof Customer;
}
private Object maskSensitive(Object parameter) {
Customer result = new Customer();
result.name = ((Customer) parameter).name;
result.password = "***";
return result;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" packages="com.jesse.zhuang">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout>
<pattern>%d %level %c %m%n</pattern>
</PatternLayout>
</Console>
<Rewrite name="obfuscateSensitiveData">
<AppenderRef ref="Console"/>
<MaskSensitiveDataPolicy />
</Rewrite>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="obfuscateSensitiveData"/>
</Root>
</Loggers>
</Configuration>
2018-01-09 22:18:30,561 INFO CustomerLoggingApp This is sensitive and should not be logged: Customer[name=Jesse Zhuang, password=***]
2018-01-09 22:18:30,569 INFO CustomerLoggingApp But this message should be logged.