Skip to content

JsonTemplateLayout: Cannot replace recycler factory, template always uses standard factory #3395

Closed
@rschuetz

Description

@rschuetz

Description

I'm trying to plug in a new RecyclerFactoryConverter following https://logging.apache.org/log4j/2.x/manual/json-template-layout.html#extending-recycler. The factory is using a pool to cache the objects, hence it is using a new spec string like pool or pool:capacity=123, which is unknown to the default RecyclerFactories class called by RecyclerFactoryConverter.

The class gets loaded properly, the log contains:

WARN Ignoring TypeConverter [org.apache.logging.log4j.layout.template.json.util.RecyclerFactoryConverter@21ca139c] for type [interface org.apache.logging.log4j.layout.template.json.util.RecyclerFactory] that conflicts with [com.XXX.messaging.log4j.ExtRecycleFactoryConverter@226f885f], since they are not comparable.

To configure the converter I use

-Dlog4j.layout.jsonTemplate.recyclerFactory=pool

However, when initializing log4j2, it fails with

2025-01-14T14:47:01.370078759Z main ERROR Could not create plugin of type class org.apache.logging.log4j.layout.template.json.JsonTemplateLayout for element JsonTemplateLayout: java.lang.IllegalArgumentException: invalid recycler factory: pool java.lang.reflect.InvocationTargetException
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.createBuilder(PluginBuilder.java:167)
	at org.apache.logging.log4j.core.config.plugins.util.PluginBuilder.build(PluginBuilder.java:121)
	at org.apache.logging.log4j.core.config.AbstractConfiguration.createPluginObject(AbstractConfiguration.java:1164)
	at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:1085)
	at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:1077)
	at org.apache.logging.log4j.core.config.AbstractConfiguration.createConfiguration(AbstractConfiguration.java:1077)
	at org.apache.logging.log4j.core.config.AbstractConfiguration.doConfigure(AbstractConfiguration.java:681)
	at org.apache.logging.log4j.core.config.AbstractConfiguration.initialize(AbstractConfiguration.java:264)
	at org.apache.logging.log4j.core.config.AbstractConfiguration.start(AbstractConfiguration.java:313)
	at org.apache.logging.log4j.core.LoggerContext.setConfiguration(LoggerContext.java:631)
	at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:713)
	at org.apache.logging.log4j.core.LoggerContext.reconfigure(LoggerContext.java:735)
	at org.apache.logging.log4j.core.LoggerContext.start(LoggerContext.java:260)
	at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:154)
	at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:46)
	at org.apache.logging.log4j.LogManager.getContext(LogManager.java:197)
	at org.apache.logging.log4j.spi.AbstractLoggerAdapter.getContext(AbstractLoggerAdapter.java:136)
	at org.apache.logging.slf4j.Log4jLoggerFactory.getContext(Log4jLoggerFactory.java:58)
	at org.apache.logging.log4j.spi.AbstractLoggerAdapter.getLogger(AbstractLoggerAdapter.java:46)
	at org.apache.logging.slf4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:32)
	at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:432)
	at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:457)
	at com.XXX.messaging.startup.Messaging$Starter.<clinit>(Messaging.java:111)
	at com.XXX.messaging.startup.Messaging.main(Messaging.java:56)
Caused by: java.lang.IllegalArgumentException: invalid recycler factory: pool
	at org.apache.logging.log4j.layout.template.json.util.RecyclerFactories.ofSpec(RecyclerFactories.java:84)
	at org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults.getRecyclerFactory(JsonTemplateLayoutDefaults.java:118)
	at org.apache.logging.log4j.layout.template.json.JsonTemplateLayout$Builder.<init>(JsonTemplateLayout.java:361)
	at org.apache.logging.log4j.layout.template.json.JsonTemplateLayout$Builder.<init>(JsonTemplateLayout.java:316)
	at org.apache.logging.log4j.layout.template.json.JsonTemplateLayout.newBuilder(JsonTemplateLayout.java:312)
	... 28 more

i.e. it's not using the replacement at all, potentially due to the hardcoded reference in

Replacing it with

    public static RecyclerFactory getRecyclerFactory()
    {
        final String recyclerFactorySpec = PROPERTIES.getStringProperty("log4j.layout.jsonTemplate.recyclerFactory");
        return TypeConverters.convert(recyclerFactorySpec, RecyclerFactory.class, null);
    }

helps, but I'm not sure if this is correct.

Configuration

Version: 2.23.1

Operating system: Ubuntu 24.04

JDK: OpenJDK Runtime Environment Temurin-17.0.10+7 (build 17.0.10+7)

Logs

See above.

Reproduction

Due to customized build systems here a bit complicated. In general it should be sufficient to

  1. create something like ExampleRecyclerFactory (from https://logging.apache.org/log4j/2.x/manual/json-template-layout.html#extending-recycler) and return ThreadLocalRecyclerFactory.INSTANCE just to return something working
  2. make sure the plugin can be found (annotationProcessor("org.apache.logging.log4j", "log4j-core") in gradle)
  3. configure log4j to use JsonTemplateLayout
  4. set -Dlog4j.layout.jsonTemplate.recyclerFactory=pool system property to let the default RecyclerFactories class fail if it gets called despite of the configuration

Metadata

Metadata

Assignees

Labels

bugIncorrect, unexpected, or unintended behavior of existing codelayoutsAffects one or more Layout pluginswaiting-for-maintainer

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions