使用正则表达式在java中使用logstash logback屏蔽日志

在当今数据驱动的世界中,数据安全最为重要。日志框架在应用程序监控和调试中发挥着至关重要的作用,但它们可能会无意中暴露不应该暴露的敏感信息。日志屏蔽是一种有效混淆日志消息中敏感数据、保护机密信息的技术。

了解日志回溯

logback 是 java 应用程序中功能强大且最常用的日志框架。它提供灵活的配置选项,包括将日志事件格式化为 json 对象的能力。它是 log4j 框架的继承者,由于其功能和易用性而迅速流行起来。它由 logger、encoders、layout、appender、encoder 组成。

logger: logger 是日志消息的上下文。应用程序将与此类交互以创建日志消息。

编码器: 编码器是在 logback 0.9.91 中引入的,负责将事件转换为字节数组以及将该字节数组写入 outputstream。作为布局引入的编码器只能将事件转换为字符串,这将其范围限制为非二进制输出。

布局: 布局负责根据用户的意愿格式化日志请求,而附加程序负责将格式化的输出发送到其目的地。

appenders: logback 中,输出目的地称为 appender。这会将日志消息放置在其最终目的地中。一个 logger 可以有多个 appender。目前,控制台、文件、远程套接字服务器、mysql、postgresql、oracle 和其他数据库、jms 和远程 unix syslog 守护进程都存在附加程序。

关于 logstash logback 编码器

logstash-logback-encoder 库是增强 spring boot 应用程序日志记录功能的宝贵工具。它提供了一种以结构化 json 格式格式化日志消息的便捷方法,使日志聚合和分析工具(例如 logstash)可以轻松使用它们。 json 格式提供了一种结构化且机器可读的方式来记录信息,使其成为高级日志分析和安全措施的理想选择。 logstash 的好处

  • json 自定义 logstash 允许您自定义 json 输出以包含特定字段和元数据。

  • 动态字段它还允许根据应用程序上下文动态添加字段来记录事件。

  • 提高了可读性 json 格式为日志事件提供了清晰且易于阅读的结构。

  • 增强的搜索和分析日志聚合工具可以轻松解析和查询 json 日志。

  • 机器解析 json 日志非常适合自动分析和警报系统。

屏蔽日志数据的解决方案

这里的主要目标是提供一种解决方案来屏蔽可在运行时定制和配置的数据。

这是我们的简单要求

  1. 在日志中完全屏蔽密码。
  2. 屏蔽电话号码和登录名,日志中最后 5 位除外。

第 1 步
创建 spring boot 应用程序。该解决方案将适用于任何基于 java 的应用程序,只需很少的定制。

第 2 步
配置所有正则表达式以屏蔽数据。请记住,正则表达式在资源利用率方面的成本很高。确保您正在调整正则表达式。正则表达式组将允许我们从字符串中选择所需的子字符串。

使用正则表达式在java中使用logstash logback屏蔽日志

第三步
创建一个类并实现 messagejsonprovider。该接口来自logstash,允许我们在打印到附加程序之前自定义消息。每个日志消息都会调用此接口中的 writeto 方法。

  • 在start()方法中读取所有正则表达式并准备包含所有maskingrule的logmasker。该方法来自 abstractjsonprovider,只是将进程启动标记为 true。

  • maskingrule 将保存正则表达式模式和一个函数。此函数替换日志中识别的字符串。

@data
public class maskingmessagingprovider extends messagejsonprovider {

    public static final string default_rules_delimiter = ",";
    private logmasker logmasker;
    private string rules;

    public maskingmessagingprovider() {
        super();
    }

    @override
    public void start() {
        super.start();
        this.logmasker = logmasker.create(stringutils.tokenizetostringarray(rules, default_rules_delimiter));
    }

    @override
    public void writeto(jsongenerator generator, iloggingevent event) throws ioexception {

        if (isstarted()) {
            jsonwritingutils.writestringfield(generator, getfieldname(), logmasker.mask(event.getformattedmessage()));
        }
    }
}

class logmasker {

    private maskingrule[] masks;

    public logmasker(maskingrule[] masks) {
        super();
        this.masks = masks.clone();
    }

    public static logmasker create(string[] rules) {

        return new logmasker(arrays.stream(rules).map(rule -> maskingrule.create(rule)).toarray(maskingrule[]::new));
    }

    public string mask(string input) {
        string transformed = input;
        for (maskingrule m : masks) {
            transformed = m.mask(transformed);
        }
        return transformed;
    }
}

class maskingrule {
    public static final int reg_ex_default_group_selector = 2;
    public static final string default_replacement = "*";

    private pattern pattern;
    private unaryoperator<string> replacement;

    public maskingrule(pattern maskpattern, unaryoperator<string> replacement) {
        super();
        this.pattern = maskpattern;
        this.replacement = replacement;
    }

    public static maskingrule create(string rule) {
        return new maskingrule(pattern.compile(rule), (in) -> maskingrule.maskdatawithreplacement(in, default_replacement));
    }

    public string mask(string transformed) {
        matcher matcher = pattern.matcher(transformed);
        stringbuffer sb = new stringbuffer();
        while (matcher.find()) {
            matcher.appendreplacement(sb, replacement.apply(getdatatobemasked(matcher)));
        }
        matcher.appendtail(sb);
        return sb.tostring();
    }

    private static string maskdatawithreplacement(string input, string replacement) {
        int repetition = !stringutils.haslength(input) ? 0 : input.length();
        return string.join("", collections.ncopies(repetition, replacement));
    }

    private static string getdatatobemasked(matcher matcher) {
        if (matcher.groupcount() > 1) {
            return matcher.group(reg_ex_default_group_selector);
        }
        return matcher.groupcount() > 0 ? matcher.group(1) : "";
    }
}

步骤 4
logback-spring.xml 文件中配置类。

<configuration>
    <springproperty scope="context" name="rules" source="app.logging.masking.rules"
                    defaultvalue=""/>
    <appender name="console" class="ch.qos.logback.core.consoleappender">
        <encoder class="net.logstash.logback.encoder.loggingeventcompositejsonencoder">
            <providers>
                <provider class="com.daya.logging.logstash.maskingmessagingprovider">
                    <rules>${rules}</rules>
                    <rulesdelimiter>${rulesdelimiter}</rulesdelimiter>
                    <ruledelimiter>${ruledelimiter}</ruledelimiter>
                </provider>
                <threadname/>
                <timestamp/>
                <loglevel/>
                <loggername/>
                <mdc/>
                <version/>
                <stacktrace/>
            </providers>
        </encoder>
    </appender>
    <root level="info">
        <appender-ref ref="console"/>
    </root>
</configuration>

步骤 5
运行应用程序。为简单起见,我采用了一个保存数据的字符串并在应用程序启动时打印它。

@SpringBootApplication
@Slf4j
public class LogDataMaskingApplication {

    public static void main(String[] args) {
        SpringApplication.run(LogDataMaskingApplication.class, args);
        LogDataMaskingApplication.maskingTest();
    }

    public static void maskingTest() {
        String data = "{"loginName":"maskingtest","phoneNumber":"9898981212","password":"Masking@123"}";
        log.info(data);
    }

}

使用正则表达式在java中使用logstash logback屏蔽日志

这是非常基本的解决方案,并且根据消息摘要等要求有很大的增强空间...

您可以在 github 上找到代码。

如有任何问题请留言。

以上就是使用正则表达式在java中使用logstash logback屏蔽日志的详细内容,更多请关注硕下网其它相关文章!