1

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?

user1589188
  • 5,316
  • 17
  • 67
  • 130

1 Answers1

2

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.

Phil Clay
  • 4,028
  • 17
  • 22
  • Yes thank you. I guess you are right, but would love to wait a bit and see if there is other answer (like using groovy you mentioned) from others before accepting that "there is no way to do it in pure xml" as the answer. Also, doing it in program loses the ease of config change. But credit to you for your effort to show that possibility to everyone, kudos. – user1589188 Oct 16 '19 at 03:29