Skip to content

Breaking change in logback 1.5.13 breaks %property{key} patterns #1060

@jordanjennings

Description

@jordanjennings

Describe the bug
When upgrading logback-classic to 1.5.13 or newer (which is transitively part of Spring Boot 3.3.8+ and 3.4.2+) you can no longer use %property{key} patterns due to a breaking internal change in logback.

See discussion here: qos-ch/logback#931

To Reproduce
Steps to reproduce the behavior:

  1. Use this build.gradle (or equivalent maven configuration)
plugins {
  id 'java'
}

repositories {
  mavenCentral()
}

dependencies {
  implementation 'net.logstash.logback:logstash-logback-encoder:8.0'
  implementation 'ch.qos.logback:logback-classic:1.5.13'
}
  1. Use this logback.xml configuration
<?xml version="1.0" encoding="UTF-8"?>
<configuration>

  <appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
      <providers>
        <timestamp />
        <logLevel />
        <pattern>
          <pattern>
            {
              "java.runtime.version": "%property{java.runtime.version}"
            }
          </pattern>
        </pattern>
        <message />
      </providers>
    </encoder>
  </appender>

  <root level="info">
    <appender-ref ref="JSON"/>
  </root>

</configuration>
  1. Start the app, sample Main.java for reference:
package com.example;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Main {

  private static final Logger log = LoggerFactory.getLogger(Main.class);

  public static void main(String[] args) {
    log.info("Hello World!");
  }
}

  1. See error on startup:
09:48:37,738 |-ERROR in net.logstash.logback.composite.loggingevent.LoggingEventPatternJsonProvider@57d7f8ca - Invalid [pattern]: Invalid JSON property '//process.runtime.version' (was '%property{java.runtime.version}'): class java.lang.String cannot be cast to class java.util.function.Supplier (java.lang.String and java.util.function.Supplier are in module java.base of loader 'bootstrap') net.logstash.logback.pattern.AbstractJsonPatternParser$JsonPatternException: Invalid JSON property '//process.runtime.version' (was '%property{java.runtime.version}'): class java.lang.String cannot be cast to class java.util.function.Supplier (java.lang.String and java.util.function.Supplier are in module java.base of loader 'bootstrap')
	at net.logstash.logback.pattern.AbstractJsonPatternParser$JsonPatternException: Invalid JSON property '//process.runtime.version' (was '%property{java.runtime.version}'): class java.lang.String cannot be cast to class java.util.function.Supplier (java.lang.String and java.util.function.Supplier are in module java.base of loader 'bootstrap')
	at 	at net.logstash.logback.pattern.AbstractJsonPatternParser.parseNode(AbstractJsonPatternParser.java:328)
	at 	at net.logstash.logback.pattern.AbstractJsonPatternParser.parseObject(AbstractJsonPatternParser.java:378)
	at 	at net.logstash.logback.pattern.AbstractJsonPatternParser.parse(AbstractJsonPatternParser.java:307)
	at 	at net.logstash.logback.composite.AbstractPatternJsonProvider.initializeNodeWriter(AbstractPatternJsonProvider.java:104)
	at 	at net.logstash.logback.composite.AbstractPatternJsonProvider.start(AbstractPatternJsonProvider.java:85)
	at 	at net.logstash.logback.composite.JsonProviders.start(JsonProviders.java:48)
	at 	at net.logstash.logback.composite.AbstractCompositeJsonFormatter.start(AbstractCompositeJsonFormatter.java:120)
	at 	at net.logstash.logback.encoder.CompositeJsonEncoder.start(CompositeJsonEncoder.java:129)
	at 	at ch.qos.logback.core.model.processor.ImplicitModelHandler.postHandleComplex(ImplicitModelHandler.java:207)
	at 	at ch.qos.logback.core.model.processor.ImplicitModelHandler.postHandle(ImplicitModelHandler.java:186)
	at 	at ch.qos.logback.core.model.processor.DefaultProcessor.secondPhaseTraverse(DefaultProcessor.java:257)
	at 	at ch.qos.logback.core.model.processor.DefaultProcessor.secondPhaseTraverse(DefaultProcessor.java:253)
	at 	at ch.qos.logback.core.model.processor.DefaultProcessor.secondPhaseTraverse(DefaultProcessor.java:253)
	at 	at ch.qos.logback.core.model.processor.DefaultProcessor.traversalLoop(DefaultProcessor.java:90)
	at 	at ch.qos.logback.core.model.processor.DefaultProcessor.process(DefaultProcessor.java:106)
	at 	at ch.qos.logback.core.joran.GenericXMLConfigurator.processModel(GenericXMLConfigurator.java:222)
	at 	at ch.qos.logback.core.joran.GenericXMLConfigurator.doConfigure(GenericXMLConfigurator.java:178)
	at 	at ch.qos.logback.core.joran.GenericXMLConfigurator.doConfigure(GenericXMLConfigurator.java:123)
	at 	at ch.qos.logback.core.joran.GenericXMLConfigurator.doConfigure(GenericXMLConfigurator.java:66)
	at 	at ch.qos.logback.classic.util.DefaultJoranConfigurator.configureByResource(DefaultJoranConfigurator.java:68)
	at 	at ch.qos.logback.classic.util.DefaultJoranConfigurator.configure(DefaultJoranConfigurator.java:35)
	at 	at ch.qos.logback.classic.util.ContextInitializer.invokeConfigure(ContextInitializer.java:128)
	at 	at ch.qos.logback.classic.util.ContextInitializer.autoConfig(ContextInitializer.java:103)
	at 	at ch.qos.logback.classic.util.ContextInitializer.autoConfig(ContextInitializer.java:66)
	at 	at ch.qos.logback.classic.spi.LogbackServiceProvider.initializeLoggerContext(LogbackServiceProvider.java:52)
	at 	at ch.qos.logback.classic.spi.LogbackServiceProvider.initialize(LogbackServiceProvider.java:41)
	at 	at org.slf4j.LoggerFactory.bind(LoggerFactory.java:199)
	at 	at org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:186)
	at 	at org.slf4j.LoggerFactory.getProvider(LoggerFactory.java:486)
	at 	at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:472)
	at 	at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:421)
	at 	at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:447)
	at 	at com.example.Main.<clinit>(Main.java:8)
Caused by: java.lang.ClassCastException: class java.lang.String cannot be cast to class java.util.function.Supplier (java.lang.String and java.util.function.Supplier are in module java.base of loader 'bootstrap')
	at 	at ch.qos.logback.core.pattern.parser.Compiler.createConverter(Compiler.java:100)
	at 	at ch.qos.logback.core.pattern.parser.Compiler.compile(Compiler.java:64)
	at 	at ch.qos.logback.core.pattern.parser.Parser.compile(Parser.java:85)
	at 	at ch.qos.logback.core.pattern.PatternLayoutBase.start(PatternLayoutBase.java:132)
	at 	at net.logstash.logback.pattern.PatternLayoutAdapter.start(PatternLayoutAdapter.java:100)
	at 	at net.logstash.logback.pattern.AbstractJsonPatternParser.buildLayout(AbstractJsonPatternParser.java:231)
	at 	at net.logstash.logback.pattern.AbstractJsonPatternParser.makeLayoutValueGetter(AbstractJsonPatternParser.java:204)
	at 	at net.logstash.logback.pattern.AbstractJsonPatternParser.makeComputableValueGetter(AbstractJsonPatternParser.java:190)
	at 	at net.logstash.logback.pattern.AbstractJsonPatternParser.parseNode(AbstractJsonPatternParser.java:324)
	at 	... 32 common frames omitted
  1. Observe log output is incorrect (it is missing the %property):
{"@timestamp":"2025-04-03T09:48:37.74953-04:00","level":"INFO","message":"Hello World!"}

Expected behavior

%property patterns work as expected

Additional context
In reported issue to logback they indicated no appetite to fix their breaking change since the fix is easy on the logstash-logback-encoder side. The fix is detailed in the linked ticket.

  • logstash-logback-encoder version = 8.0
  • logback version = 1.5.13
  • jackson version = default
  • java version = reproduced on 17 and 21

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions