java (log4j) 按对象类型记录过滤器

2023-12-05

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

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

这将记录一些敏感数据CustomerData.

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

如果 log4j 无法实现,那么其他日志框架怎么样?


Log4j2 提供了多种方法来实现此目的:

  • Filters
  • 重写日志事件

Filters

Log4j2允许您配置filters在特定记录器上,或在特定附加程序上,或在全局上(因此过滤器适用于所有日志事件)。过滤器为您提供的是强制接受日志事件、强制拒绝日志事件或保持“中立”的能力。在您的情况下,您可能希望拒绝包含敏感数据的日志事件。

您可以创建一个自定义过滤器实现(参见插件文档了解如何安装自定义过滤器),或者您可以使用一些内置过滤器。要么是内置的正则表达式过滤器 or 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=“ 在他们中:

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

脚本过滤器示例

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

鉴于上述应用程序类,以下log4j2.xml配置将过滤掉包含完全限定类名为的任何参数的任何日志事件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>

重写日志事件

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

为此,您创建一个重写Appender。从手册中:

RewriteAppender 允许在 LogEvent 之前对其进行操作 由另一个 Appender 处理。这可以用来掩盖敏感 诸如密码之类的信息或将信息注入每个 事件。

重写策略示例:

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

CAUTION:当以无垃圾模式(默认)运行时,Log4j2 使用可重用对象来存储消息和日志事件。这些不是 适合重写。 (这在用户中没有很好地记录 手册。)如果您想使用重写附加器,你需要 部分关闭无垃圾日志记录通过设置系统属性log4j2.enable.threadlocals to false.

使用您的自定义配置您的重写附加程序MaskSensitiveDataPolicy重写政策。为了让 Log4j2 了解您的插件,请在packages的属性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>

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

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.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java (log4j) 按对象类型记录过滤器 的相关文章

  • ClientRequestFactory RestEasy 已弃用...还有其他 RestEasy 替代方案吗?

    我需要使用其他人创建的 RestService 的接口来创建轻松的客户端 这工作很好 除了一件事 当我从rest easy 2 3 5 Final更新到resteasy 3 0 x时 Client RequestFactory类看起来像 D
  • Mockito 匹配器和基元数组

    有了 Mockito 我想verify 方法调用byte 在它的参数列表中 但我没有找到如何写这个 myMethod byte 我只想要类似的东西anyByteArray 如何使用 Mockito 做到这一点 我会尝试any byte cl
  • 使用 Firebase Java API 检索/格式化数据的最佳方式

    我在用着Firebase用于数据存储Android项目 并使用Firebase Java API来处理数据 不过 我不确定我是否尽可能高效地完成此操作 并且我希望获得一些有关检索和格式化数据的最佳实践的建议 我的Firebase存储库看起来
  • log4j:ERROR 在 tomcat 7 中部署 war 时无法创建自定义 Appender

    我的 log4j xml 中有以下标签
  • Spring Kafka - 为任何主题的分区消耗最后 N 条消息

    我正在尝试读取请求的卡夫卡消息数 对于非事务性消息 我们将从 endoffset N 对于 M 个分区 开始轮询并收集当前偏移量小于每个分区的结束偏移量的消息 对于幂等 事务消息 我们必须考虑事务标记 重复消息 这意味着偏移量将不连续 在这
  • Logback线程池

    我使用 Logback 进行日志记录 我有一个问题 我将 AsyncAppender 与 ConsoleAppender 结合使用 当应用程序启动时 它会创建具有 logback 线程名称的线程池 所有日志记录工作均由 AsyncAppen
  • CDI 对象无法使用注入的构造函数进行代理

    当尝试将参数注入 CDI bean ApplicationScoped 的构造函数时 我遇到以下问题 Caused by org jboss weld exceptions UnproxyableResolutionException WE
  • 如何使用 Selenium WebDriver 和 Java 滚动特定的 DIV?

    WebDriver 无法识别某些 WebElement WebDriver 无法找到浏览器可见区域中不可见的元素 In order to make the WebElement viewable by WebDriver We need t
  • 对 Java Servlet 进行单元测试

    我想知道对 servlet 进行单元测试的最佳方法是什么 只要内部方法不引用 servlet 上下文 测试内部方法就不是问题 但是测试 doGet doPost 方法以及引用上下文或使用会话参数的内部方法呢 有没有办法简单地使用经典工具 例
  • 无法解析 ListView 适配器中的 getSystemService 方法

    我正在研究约翰霍顿的Android 编程初学者 目前正在尝试创建一个笔记应用程序 霍顿刚刚介绍ListViews 但是 我遇到了麻烦adapter class public class NoteAdapter extends BaseAda
  • 如何统计lucene索引中每个文档的term数?

    我想知道 lucene 索引中每个文档的术语数量 我一直在 API 和互联网上搜索 但没有结果 你能帮助我吗 Lucene 的构建是为了回答相反的问题 即哪些文档包含给定术语 因此 为了获取文档的术语数量 您必须进行一些修改 第一种方法是存
  • 使用 IntelliJ 调试 Java 进程 - 连接到套接字但不连接到目标 VM

    现在已解决 请参阅问题末尾 我正在尝试使用 IntelliJ Community Edition 的调试器来调试 Java 进程 套接字正在侦听 但是当我尝试连接时 调试过程显示以下内容 连接到目标虚拟机 地址 8003 传输 socket
  • 如何在 Jersey RESTful Web 服务中放置 cookie?

    我想通过 Jersey API 将 cookie 从 PUT webservice result 放置到 POST webservice 这是我的代码 WebResource service1 client resource http te
  • Guice 字段注入不起作用(返回 null)

    我在使用 Guice 时遇到空值问题 接下来我将向您展示一个类似场景的示例 我知道字段注入是一种不好的做法 但我希望它在演示中像这样工作 我有一个名为B 这是我要注入的 class B Inject public B public void
  • android Studio 日志不起作用

    我无法在手机上使用日志 我只在 Android Studio 中收到此错误 11 01 03 28 56 427 24227 24375 com example segall caveofprogcourse E GED 无法获取 GED
  • JNA Windows 服务启动类型

    我一直在使用 JNA 并且能够使用下面的代码返回 Windows 服务的状态 即启动或停止 但我不确定如何返回服务的启动类型 我确信 JNA 之外还有其他方法 但如果可能的话我想继续使用 JNA import com sun jna imp
  • IntelliJ IDEA 中的项目语言级别是多少?

    我正在使用 Java 7 SDK 和 IntelliJ IDEA IDE java version 1 7 0 11 Java TM SE Runtime Environment build 1 7 0 11 b21 Java HotSpo
  • 如何指示 yum 安装特定版本的 OpenJDK

    我尝试安装openjdk in the redhat服务器 如何安装指定版本 我要安装的版本是 11 0 4 使用以下命令安装的版本是11 0 6 yum install java 11 openjdk devel 曾与 yum showd
  • Tomcat 与 Weblogic JNDI 查找

    我们使用的 Weblogic 服务器已配置为允许 JNDI 数据源名称 例如 appds 对于开发 本地主机 我们可能会运行 Tomcat 并且在 server xml 的 部分中声明时 Tomcat 会将 JNDI 数据源挂在 JNDI
  • Java 中的下载管理器 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我需要通过 FTP HTTP 从 Java 获取几个大文件 几个演出 有现成的库 java 命令行工具

随机推荐