1

I would like to use Spring environment values as custom fields in the Logstash encoder of a Logback appender.

There is a general configuration tag to use properties

<property resource="logstash.properties" />

And there is a special configuration tag from Spring for this purpose

<springProperty name="appEnv" source="environment"/>

The properties of both tags can then be used in the custom fields of the Logstash encoder

<encoder class="net.logstash.logback.encoder.LogstashEncoder">
    <customFields>{"application.environment":"${appEnv}"</customFields>
</encoder>

Problem is, as far as I understand, that this only works under certain circumstances. The problem is probably that Logback has already finished configuring when the Spring environment is built.

It seems to work when

  • The property is local and static (available on configuration time)
  • The property is in bootstrap.properties

It seems NOT to work when

  • The property is dynamic as when retrieved from Spring config server

My property values delivered from config server are null when Logback is configured and therefore the log shows them as appEnv_IS_UNDEFINED for a property called appEnv.

Because most examples just use the spring.application.name this seems to be mostly unnoticed.

To solve the timing problem, I searched for a way to reload the Logback configuration onApplicationEvent. I found this answer that confirms my problem and offers a skeleton solution.

I found other solutions where the Logback appender that uses the Logstash encoder is completely programmatically built and added to the LoggerContext.

However, I wonder if there is also a way to stick with the XML configuration of the appender and "just reload" the config programmatically when the Spring environment is ready. How would I do this?

I found this answer to do the reload, but it does not work for my case. The appEnv_IS_UNDEFINED continue to appear in the log file.

burki
  • 6,741
  • 1
  • 15
  • 31

1 Answers1

0

I was able to solve my problem by implementing a Spring ApplicationContextInitializer.

In the called initialize method I can access my Logback Appender and Encoder via RootLogger.

Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
RollingFileAppender jsonFileAppender = (RollingFileAppender) rootLogger.getAppender(LOGSTASH_APPENDER_NAME);
LogstashEncoder encoder = (LogstashEncoder) jsonFileAppender.getEncoder();

From the LogstashEncoder, I can get the customFields

String customFields = encoder.getCustomFields();

And there I found the unresolved properties in the JSON String as expected

{"application.environment":"appEnv_IS_UNDEFINED"}

Since I can get the built Spring Environment from the passed ApplicationContext

springEnvironment = applicationContext.getEnvironment();

I can match unresolved properties with the Regex (\w+)_IS_UNDEFINED and replace them with the real value from the Spring Environment.

Surprisingly, I do not need to reload or restart anything. It is sufficent to just set the fixed customFields on the Encoder. Immediately after, the Log messages contain the correct values.

encoder.setCustomFields(fixedCustomFields);

With this Initializer in place, I can fully configure my appender and the LogstashEncoder in logback-spring.xml or an included file.

burki
  • 6,741
  • 1
  • 15
  • 31