apache/druid · warning

Expected [ ] found [ ]. Unknown class for context factory…

Error message

Expected [%s] found [%s]. Unknown class for context factory. Not logging shutdown

What it means

Log4jShutterDownerModule registers a graceful log4j shutdown hook during Guice configuration. If the installed SLF4J/log4j binding provides a LoggerContextFactory that is not Log4jContextFactory, the module cannot install the shutdown registry and warns that shutdown logging will be skipped. This is a guard against running under an unexpected or custom log4j provider.

Solutions

  1. Ensure log4j-core is on the classpath and that no log4j-to-slf4j or other provider jar replaces the default Log4jContextFactory
  2. Remove conflicting logging bindings (e.g. slf4j-log4j12, logback-classic) so log4j-slf4j-impl + log4j-core are used
  3. If a custom LoggerContextFactory is intentional, accept the warning or patch the module to support it

Example fix

// before (pom.xml)
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-to-slf4j</artifactId>
</dependency>
// after
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-slf4j-impl</artifactId>
</dependency>
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
</dependency>
Defensive patterns

Strategy: validation

Validate before calling

Class.forName("org.apache.logging.log4j.core.impl.Log4jContextFactory", false, getClass().getClassLoader());

Type guard

boolean isLog4jCoreOnClasspath() { try { Class.forName("org.apache.logging.log4j.core.impl.Log4jContextFactory", false, Thread.currentThread().getContextClassLoader()); return true; } catch (ClassNotFoundException e) { return false; } }

Prevention

When it happens

Trigger: Guice configure() runs at startup while LogManager.getFactory() returns a factory that is not an instance of org.apache.logging.log4j.core.impl.Log4jContextFactory — e.g. a custom or third-party LoggerContextFactory, a stripped log4j-core jar, or a different provider (slf4j-simple, logback bridge) exposing log4j API.

Common situations: Deploying Druid with a custom logging setup, shading or excluding log4j-core, using a log4j-to-slf4j bridge, or accidentally including multiple logging bindings so a non-default factory wins.

Understand the failure class

Background: "X is not installed. Please install it with pip install Y": missing optional dependency errors — ImportError/ValueError raised when a library's optional extra was never installed — this error's family across 22 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/861f93edb3d397c0. Report an issue: GitHub.

Appendix: source

Thrown at server/src/main/java/org/apache/druid/initialization/Log4jShutterDownerModule.java:60

  public void configure(Binder binder)
  {
    // Instantiate eagerly so that we get everything registered and put into the Lifecycle as early as possible
    // Lifecycle scope is INIT to ensure stop runs in the last phase of lifecycle stop.

    try {
      ClassLoader loader = Thread.currentThread().getContextClassLoader();
      if (loader == null) {
        loader = getClass().getClassLoader();
      }
      // Reflection to try and allow non Log4j2 stuff to run. This acts as a gateway to stop errors in the next few lines
      // In log4j api
      Class.forName("org.apache.logging.log4j.LogManager", false, loader);
      // In log4j core
      Class.forName("org.apache.logging.log4j.core.util.ShutdownCallbackRegistry", false, loader);

      final LoggerContextFactory contextFactory = LogManager.getFactory();
      if (!(contextFactory instanceof Log4jContextFactory)) {
        log.warn(
            "Expected [%s] found [%s]. Unknown class for context factory. Not logging shutdown",
            Log4jContextFactory.class.getName(),
            contextFactory.getClass().getName()
        );
        return;
      }
      final ShutdownCallbackRegistry registry = ((Log4jContextFactory) contextFactory).getShutdownCallbackRegistry();
      if (!(registry instanceof Log4jShutdown)) {
        log.warn(
            "Shutdown callback registry expected class [%s] found [%s]. Skipping shutdown registry",
            Log4jShutdown.class.getName(),
            registry.getClass().getName()
        );
        return;
      }
      binder.bind(Log4jShutdown.class).toInstance((Log4jShutdown) registry);
      binder.bind(Key.get(Log4jShutterDowner.class, Names.named("ForTheEagerness")))
            .to(Log4jShutterDowner.class)

View on GitHub (pinned to 9b90983fd2)