0
votes

I have an encoder using net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder to configure the log content via a mix of stock providers, patterns and custom providers. The same is to be used in different appenders, e.g. console, file, rolling file and a custom one for unit testing. I don't want to repeat the same configuration in every appender as it is exactly the same, so I wonder if there is a way to share it across different appenders?

e.g.

<configuration>
  <appender name="FILE_LOG" class="ch.qos.logback.core.FileAppender">
    <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
      ... configuration can be shared ...
    </encoder>
  </appender>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
      ... repeated the exact same configuration ...
    </encoder>
  </appender>

I know there is this "include" feature, but it seems it has to start from the root level (i.e. appender) for inclusion. Is there a way to "include" at the encoder level?

1

1 Answers

1
votes

Unfortunately, logback does not provide a way to share encoder configuration completely in xml configuration files.

Instead, you could

  1. define a class that extends net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder,
  2. perform configuration programmatically within that class, and then
  3. reference your class in the xml.

For example:

public class JsonEncoder extends LoggingEventCompositeJsonEncoder {
    public JsonEncoder() {
    }
    @Override
    public void start() {
        // Note: you can access logback properties via getContext().getProperty(...)

        JsonProviders<ILoggingEvent> providers = getProviders();
        providers.addProvider(new LoggingEventFormattedTimestampJsonProvider());
        providers.addProvider(new LogLevelJsonProvider());
        providers.addProvider(new MessageJsonProvider());
        providers.addProvider(new ThreadNameJsonProvider());
        providers.addProvider(new LoggerNameJsonProvider());
        providers.addProvider(new MdcJsonProvider());
        providers.addProvider(new LogstashMarkersJsonProvider());
        providers.addProvider(new StackTraceJsonProvider());
        providers.addProvider(new ArgumentsJsonProvider());
        super.start();
    }
}
<configuration>
  <appender name="FILE_LOG" class="ch.qos.logback.core.FileAppender">
    <encoder class="your.package.JsonEncoder"/>
  </appender>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="your.package.JsonEncoder"/>
  </appender>

You could also ditch the logback.xml and go to a full programmatic configuration, where you would use a factory method to create each encoder. Note that separate encoder instances are required for each appender, but you could construct each encoder instance identically.

Also, I haven't tried it personally, but you might be able to use the groovy configuration to share encoder configuration, but that unfortunately requires adding groovy to your classpath.