我想将 Spring 环境值用作 Logback 追加器的 Logstash 编码器中的自定义字段。
有一个通用配置标记可以使用属性
<property resource="logstash.properties" />
并且有一个来自 Spring 的特殊配置标签用于此目的
<springProperty name="appEnv" source="environment"/>
然后,可以在 Logstash 编码器的自定义字段中使用这两个标记的属性
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<customFields>{"application.environment":"${appEnv}"</customFields>
</encoder>
问题是,据我了解,这只在某些情况下有效。问题可能是Logback 在构建 Spring 环境时已经完成了配置。
它似乎在以下情况下起作用
- 该属性是本地和静态的(在配置时可用)
- 该属性位于 bootstrap.properties 中
它似乎不起作用时
- 该属性是动态的,就像从Spring 配置服务器检索时一样
配置Logback时,从配置服务器传递的属性值null
,因此日志将它们显示为名为appEnv
的属性的appEnv_IS_UNDEFINED
。
因为大多数例子只是使用spring.application.name
这似乎大多没有被注意到。
为了解决计时问题,我寻找了一种重新加载 Logback 配置onApplicationEvent
的方法。我找到了这个答案,它证实了我的问题并提供了一个框架解决方案。
我发现了其他解决方案,其中使用 Logstash 编码器的 Logback 追加器完全以编程方式构建并添加到LoggerContext
中。
但是,我想知道是否还有一种方法可以坚持使用追加器的XML配置,并在Spring环境准备就绪时以编程方式"重新加载"配置。我该怎么做?
我找到了这个答案来重新加载,但它对我的情况不起作用。appEnv_IS_UNDEFINED
继续显示在日志文件中。
我能够通过实现 SpringApplicationContextInitializer
来解决我的问题。
在调用initialize
方法中,我可以通过RootLogger访问我的Logback Appender和Encoder。
Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
RollingFileAppender jsonFileAppender = (RollingFileAppender) rootLogger.getAppender(LOGSTASH_APPENDER_NAME);
LogstashEncoder encoder = (LogstashEncoder) jsonFileAppender.getEncoder();
从 LogstashEncoder 中,我可以获取自定义字段
String customFields = encoder.getCustomFields();
在那里,我按预期在 JSON 字符串中找到了未解析的属性
{"application.environment":"appEnv_IS_UNDEFINED"}
因为我可以从传递的ApplicationContext
中获得构建的弹簧Environment
springEnvironment = applicationContext.getEnvironment();
我可以将未解析的属性与正则表达式(w+)_IS_UNDEFINED
匹配,并将它们替换为 Spring 环境中的实际值。
令人惊讶的是,我不需要重新加载或重新启动任何东西。只需在编码器上设置固定的自定义字段就足够了。紧接着,日志消息包含正确的值。
encoder.setCustomFields(fixedCustomFields);
有了这个初始值设定项,我可以在logback-spring.xml
或包含的文件中完全配置我的追加器和 LogstashEncoder。