Quarkus日志JSON格式发至Logstash TCP及Logback延迟初始化问题
1. 是否存在Quarkus原生方式将JSON日志发送至Logstash TCP输入?
Quarkus官方并未提供原生内置的Logstash TCP JSON日志发送器,官方推荐的GELF方案之外,社区最常用且成熟的方案就是你已经尝试的**quarkus-logging-logback 配合 logstash-logback-encoder**组合——这也是目前能满足自定义JSON字段需求、灵活性最高的实现方式。
如果不想依赖Logback,也可以通过自定义Quarkus扩展实现日志的TCP JSON发送,但需要自行处理日志事件捕获、JSON序列化和TCP通信,开发成本较高,不推荐作为首选方案。
2. 如何让Logback日志器延迟初始化,直至所有SCC属性加载完成?
核心思路是让Quarkus的日志系统延迟到Spring Cloud Config(SCC)配置加载完成后再初始化,以下两种方案可实现需求:
方案一:利用Quarkus内置的日志延迟初始化配置
Quarkus提供quarkus.log.init-at-startup配置项,默认值为true(启动时立即初始化日志系统),将其设为false可让日志系统延迟到第一次调用Logger时才初始化,此时SCC配置已完成加载。
步骤如下:
在
application.properties中添加配置:# 延迟日志系统初始化 quarkus.log.init-at-startup=false # 配置Spring Cloud Config加载参数 quarkus.spring-cloud-config.enabled=true quarkus.spring-cloud-config.server-url=<你的SCC服务器地址>在
logback.xml中使用Quarkus配置占位符引用SCC中的Logstash地址:<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender"> <!-- 引用SCC加载的配置,localhost:5044为默认值 --> <destination>${quarkus.logstash.tcp.address:localhost:5044}</destination> <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder"> <providers> <!-- 自定义JSON字段示例 --> <pattern> <pattern> { "timestamp": "%date{ISO8601}", "level": "%level", "message": "%message", "service": "my-quarkus-app", "custom_tag": "${quarkus.log.custom.tag:default-tag}" } </pattern> </pattern> </providers> </encoder> </appender>注:
quarkus.logstash.tcp.address和quarkus.log.custom.tag需要在你的SCC配置仓库中预先定义。(可选)若需捕获Quarkus启动早期日志,可搭配
quarkus.log.early-logging配置,将启动初期日志输出到控制台,后续日志再发送至Logstash。
方案二:动态配置Logback Appender
如果方案一无法满足需求,可通过Quarkus启动事件监听,在SCC配置加载完成后手动初始化Logstash Appender:
- 创建启动事件观察者类:
import io.quarkus.runtime.StartupEvent; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Observes; import jakarta.inject.Inject; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import net.logstash.logback.appender.LogstashTcpSocketAppender; import net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder; import net.logstash.logback.pattern.LoggingEventPatternJsonProvider; @ApplicationScoped public class LogstashAppenderInitializer { @Inject @ConfigProperty(name = "quarkus.logstash.tcp.address") String logstashAddress; void onStartup(@Observes StartupEvent event) { LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); // 初始化Logstash TCP Appender LogstashTcpSocketAppender appender = new LogstashTcpSocketAppender(); appender.setName("LOGSTASH"); appender.setContext(loggerContext); appender.setDestination(logstashAddress); // 配置JSON编码器 LoggingEventCompositeJsonEncoder encoder = new LoggingEventCompositeJsonEncoder(); encoder.setContext(loggerContext); LoggingEventPatternJsonProvider patternProvider = new LoggingEventPatternJsonProvider(); patternProvider.setPattern("{\"timestamp\":\"%date{ISO8601}\",\"level\":\"%level\",\"message\":\"%message\",\"service\":\"my-quarkus-app\"}"); encoder.getProviders().add(patternProvider); encoder.start(); appender.setEncoder(encoder); appender.start(); // 将Appender添加到根Logger rootLogger.addAppender(appender); } } - 移除
logback.xml中对应的Logstash Appender配置,避免重复初始化。 - 确保SCC的
quarkus.logstash.tcp.address属性能被正确注入到观察者类中。
该方式灵活性更高,可完全控制Appender的初始化时机,但需要编写更多代码。
内容的提问来源于stack exchange,提问作者troosan

