java(log4j)按对象类型记录过滤器 [英] java (log4j) logging filter by object type

查看:144
本文介绍了java(log4j)按对象类型记录过滤器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前使用log4j实现了一个日志记录语句:

I have a logging statement currently implemented with log4j:

log.info("Failed to create message for {}", CustomerData);

这将在 CustomerData 中记录一些敏感数据。

This will log some sensitive data in CustomerData.

有没有办法阻止记录 CustomerData 的任何实例?也许在log4j配置中或通过自定义过滤器?

Is there a way to block logging of any instances of CustomerData? Maybe in log4j configuration or via a custom filter?

如果log4j不可能,那么其​​他日志框架怎么样?

If not possible with log4j, how about other logging frameworks?

推荐答案

Log4j2提供了多种方法来实现这一目标:

Log4j2 offers a number of ways to accomplish this:

Log4j2允许您在特定记录器上配置过滤器 ,或在特定的appender上,或全局的(因此过滤器适用于所有日志事件)。什么过滤器可以强制接受日志事件,强制降级日志事件,或者是中性。在您的情况下,您可能希望DENY记录包含敏感数据的事件。

Log4j2 allows you to configure filters on specific loggers, or on specific appenders, or globally (so the filter applies to all log events). What filters give you is the ability to force-ACCEPT a log event, or force-DENY a log event, or to be "NEUTRAL". In your case you may want to DENY log events that contain sensitive data.

您可以创建自定义过滤器实现(请参阅插件有关如何安装自定义过滤器的文档,或者您可以使用某些内置过滤器。内置的 RegexFilter ScriptFilter 应该足以满足您的目的。

You can create a custom Filter implementation (see the plugin docs for how to install your custom filter), or you can use some of the built-in filters. Either the built-in RegexFilter or a ScriptFilter should be sufficient for your purposes.

假设这是一个包含敏感数据的类:

Let's say that this is a class that contains sensitive data:

public class Customer {
    public String name;
    public String password;

    @Override
    public String toString() {
        return "Customer[name=" + name + ", password=" + password + "]";
    }
}

您的应用程序日志记录如下所示:

And your application logging looks something like this:

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 = 的日志消息其中:

You can configure a regex filter that looks at the formatted (or unformatted) message and DENY any log messages that have the word "Customer" followed by ", password=" in them:

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



脚本过滤器示例



另一个非常灵活过滤器是 ScriptFilter 。下面的示例使用Groovy,但您也可以使用Java或Java安装上可用的任何其他脚本语言。

Script filter example

Another very flexible filter is ScriptFilter. The below example uses Groovy, but you can also use JavaScript or whatever other scripting language is available on your Java installation.

鉴于上述应用程序类,以下 log4j2.xml 配置将过滤掉任何包含完全限定类名为 Customer

Given the above application classes, the following log4j2.xml configuration would filter out any log events containing any parameters whose fully qualified class name is Customer:

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



重写日志事件



另一个有趣的选择是重写日志事件,以便消息不会被完全过滤掉,而只是屏蔽敏感数据。例如,您在日志中用***替换密码字符串。

Rewriting Log Events

Another interesting option is to rewrite the log event so that the message does not get completely filtered out, but instead you just mask the sensitive data. For example you replace the password string with "***" in the log.

为此,您需要创建 RewriteAppender 。从手册:

To do this, you create a RewriteAppender. From the manual:


RewriteAppender允许在另一个Appender处理
之前操作LogEvent。这可用于屏蔽敏感的
信息,例如密码或将信息注入每个
事件。

The RewriteAppender allows the LogEvent to manipulated before it is processed by another Appender. This can be used to mask sensitive information such as passwords or to inject information into each event.

重写政策示例:

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




注意:在无垃圾模式(默认)下运行时,Log4j2使用可重用对象进行消息和日志事件。这些不是
适合重写。 (这在用户
手册中没有很好地记录。)如果你想使用重写appender,你需要通过设置系统属性来
部分关闭无垃圾日志

log4j2.enable.threadlocals to false

CAUTION: When running in garbage-free mode (the default), Log4j2 uses reusable objects for messages and log events. These are not suitable for rewriting. (This is not documented well in the user manual.) If you want to use the rewrite appender, you need to partially switch off garbage-free logging by setting system property log4j2.enable.threadlocals to false.

使用自定义 MaskSensitiveDataPolicy 重写策略配置重写appender。要让Log4j2了解您的插件,请在配置 packages 属性中指定插件包的名称:

Configure your rewrite appender with your custom MaskSensitiveDataPolicy rewrite policy. To let Log4j2 know about your plugin, specify the name of the package of your plugin in the packages attribute of the Configuration:

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

这将使上面的示例程序产生以下输出。请注意,密码被屏蔽,但敏感对象的其他属性仍然保留:

This will make the above example program produce the following output. Note that the password is masked but other attributes of the sensitive object are preserved:

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.

这篇关于java(log4j)按对象类型记录过滤器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆