You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中如何用Log4j合并多行System.out日志至Graylog

解决方案:合并Spring Boot中SSL握手日志为单个Graylog条目

方案一:自定义PrintStream接管System.out

直接替换System.out,将连续的javax.net.ssl开头日志行合并后输出,其他行正常处理。

  • 实现逻辑:

    1. 自定义PrintStream,维护缓冲区暂存javax.net.ssl开头的日志内容
    2. 遇到非SSL日志行时,先输出缓冲区的合并内容,再处理当前行;连续SSL日志行直接追加到缓冲区
    3. Spring Boot启动时替换默认输出流
  • 代码示例:

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;

public class SslMergingPrintStream extends PrintStream {
    private final PrintStream originalOut;
    private final ByteArrayOutputStream sslBuffer = new ByteArrayOutputStream();
    private boolean inSslLog = false;

    public SslMergingPrintStream(PrintStream originalOut) {
        super(originalOut);
        this.originalOut = originalOut;
    }

    @Override
    public void println(String x) {
        if (x.startsWith("javax.net.ssl")) {
            inSslLog = true;
            sslBuffer.write(x.getBytes(StandardCharsets.UTF_8));
            sslBuffer.write('\n');
        } else {
            flushSslBuffer();
            originalOut.println(x);
        }
    }

    @Override
    public void print(String s) {
        if (inSslLog && s != null) {
            sslBuffer.write(s.getBytes(StandardCharsets.UTF_8));
        } else {
            originalOut.print(s);
        }
    }

    private void flushSslBuffer() {
        if (inSslLog) {
            originalOut.print(sslBuffer.toString(StandardCharsets.UTF_8));
            sslBuffer.reset();
            inSslLog = false;
        }
    }

    @Override
    protected void finalize() throws Throwable {
        flushSslBuffer();
        super.finalize();
    }
}
  • 启动时替换输出流:
    在Spring Boot启动类的main方法中添加:
public static void main(String[] args) {
    System.setOut(new SslMergingPrintStream(System.out));
    SpringApplication.run(YourApplication.class, args);
}

方案二:Log4j2自定义Filter(推荐)

先将System.out重定向到Log4j2,再通过自定义Filter合并SSL日志事件。

  • 实现逻辑:

    1. 开启Spring Boot的logging.system.out.redirect-to-slf4j=true配置,将System.out输出转至SLF4J/Log4j2
    2. 自定义Filter拦截javax.net.ssl开头的日志事件,合并连续的同类事件后再输出
  • 核心代码示例:

import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.filter.AbstractFilter;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class SslLogMergerFilter extends AbstractFilter {
    private final BlockingQueue<String> sslEventBuffer = new ArrayBlockingQueue<>(100);

    @Override
    public Result filter(LogEvent event) {
        String message = event.getMessage().getFormattedMessage();
        if (message.startsWith("javax.net.ssl")) {
            sslEventBuffer.offer(message);
            return Result.DENY; // 先拦截暂存
        } else {
            mergeAndFlushSslEvents(event);
            return Result.NEUTRAL; // 正常处理非SSL日志
        }
    }

    private void mergeAndFlushSslEvents(LogEvent baseEvent) {
        if (sslEventBuffer.isEmpty()) return;

        StringBuilder mergedMsg = new StringBuilder();
        String line;
        while ((line = sslEventBuffer.poll()) != null) {
            mergedMsg.append(line).append('\n');
        }

        LogEvent mergedEvent = new LogEvent.Builder(baseEvent)
                .setMessage(mergedMsg.toString())
                .build();
        getNext().filter(mergedEvent);
    }

    @Override
    public void stop() {
        mergeAndFlushSslEvents(new LogEvent.Builder().build());
        super.stop();
    }
}
  • Log4j2配置示例(log4j2.xml):
    在Graylog Appender中添加自定义Filter:
<Appenders>
    <Gelf name="Graylog" server="your-graylog-server" port="12201">
        <PatternLayout pattern="%msg"/>
        <Filters>
            <SslLogMergerFilter onMatch="ACCEPT" onMismatch="NEUTRAL"/>
        </Filters>
    </Gelf>
</Appenders>

方案三:调整SSL日志输出方式(根源解决)

直接让SSL调试日志通过Log4j输出,避免使用System.out:

  • 添加JVM启动参数:
-Djavax.net.debug=ssl:handshake
-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager
  • 引入Log4j JUL桥接依赖:
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-jul</artifactId>
    <version>${log4j.version}</version>
</dependency>

此配置会将SSL调试日志从JUL转至Log4j,后续可直接通过Log4j的批量输出能力合并日志。


内容的提问来源于stack exchange,提问作者sge

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 04:10:38