Skip to content

<condition> placed inside <if> is silently ignored, produces misleading "Janino not found" error #1024

Description

@freesoft

Summary

When <condition> (the Janino-free alternative introduced in logback 1.5.x) is placed as a child of <if> instead of as a preceding sibling, both <then> and <else> branches are silently skipped. Instead of a helpful error about incorrect <condition> placement, logback falls through to the Janino code path and emits a misleading error:

ERROR in IfModelHandler - Could not find Janino library on the class path. Skipping conditional processing.

This is confusing because the user explicitly opted out of Janino by using the <condition> element.

Logback version

1.5.32 (also confirmed on 1.5.22)

Reproduction

Broken config (<condition> inside <if> — produces misleading Janino error, both branches skipped):

<configuration debug="true">
    <property name="testKey" value="testValue"/>
    <if>
        <condition class="ch.qos.logback.core.boolex.ExpressionPropertyCondition">
            <expression>propertyEquals("testKey", "testValue")</expression>
        </condition>
        <then>
            <appender name="THEN_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
                <encoder><pattern>THEN: %msg%n</pattern></encoder>
            </appender>
            <root level="DEBUG"><appender-ref ref="THEN_APPENDER"/></root>
        </then>
        <else>
            <appender name="ELSE_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
                <encoder><pattern>ELSE: %msg%n</pattern></encoder>
            </appender>
            <root level="DEBUG"><appender-ref ref="ELSE_APPENDER"/></root>
        </else>
    </if>
</configuration>

Working config (<condition> as preceding sibling of <if>):

<configuration debug="true">
    <property name="testKey" value="testValue"/>
    <condition class="ch.qos.logback.core.boolex.ExpressionPropertyCondition">
        <expression>propertyEquals("testKey", "testValue")</expression>
    </condition>
    <if>
        <then>
            <appender name="THEN_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
                <encoder><pattern>THEN: %msg%n</pattern></encoder>
            </appender>
            <root level="DEBUG"><appender-ref ref="THEN_APPENDER"/></root>
        </then>
        <else>
            <appender name="ELSE_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
                <encoder><pattern>ELSE: %msg%n</pattern></encoder>
            </appender>
            <root level="DEBUG"><appender-ref ref="ELSE_APPENDER"/></root>
        </else>
    </if>
</configuration>

Unit test (both tests pass, demonstrating the bug):

package logback.bug;

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.status.Status;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.net.URL;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

class ConditionInsideIfTest {

    private LoggerContext context;

    @BeforeEach
    void setUp() {
        context = new LoggerContext();
    }

    @Test
    void conditionBeforeIf_thenBranchExecuted() throws JoranException {
        configure("condition-before-if.xml");
        Logger root = context.getLogger(Logger.ROOT_LOGGER_NAME);

        // Works: <condition> as sibling before <if>
        assertThat(root.getAppender("THEN_APPENDER")).isNotNull();
        assertThat(root.getAppender("ELSE_APPENDER")).isNull();
    }

    @Test
    void conditionInsideIf_bothBranchesSkipped() throws JoranException {
        configure("condition-inside-if.xml");
        Logger root = context.getLogger(Logger.ROOT_LOGGER_NAME);

        // BUG: Both branches skipped
        assertThat(root.getAppender("THEN_APPENDER")).isNull();
        assertThat(root.getAppender("ELSE_APPENDER")).isNull();

        // BUG: Misleading "Janino not found" error instead of helpful message
        List<Status> errors = context.getStatusManager().getCopyOfStatusList().stream()
                .filter(s -> s.getLevel() >= Status.ERROR)
                .toList();
        assertThat(errors).isNotEmpty()
                .anyMatch(s -> s.getMessage().contains("Janino"));
    }

    private void configure(String resourceName) throws JoranException {
        URL url = getClass().getClassLoader().getResource(resourceName);
        assertThat(url).isNotNull();
        JoranConfigurator configurator = new JoranConfigurator();
        configurator.setContext(context);
        configurator.doConfigure(url);
    }
}

pom.xml dependencies: logback-classic:1.5.32, junit-jupiter, assertj-coreno Janino (testing the <condition> element path).

Expected behavior

When <condition> is placed inside <if>, logback should emit a clear warning or error, e.g.:

<condition> element found inside <if> — it must be placed as a preceding sibling of <if>, not as a child

Actual behavior

  1. The <condition> element inside <if> is processed (the ExpressionPropertyCondition is instantiated and evaluates successfully) but its result is not linked to the <if>.
  2. IfModelHandler doesn't find a condition result, falls through to janinoFallback().
  3. Janino is not on the classpath, so it emits: "Could not find Janino library on the class path. Skipping conditional processing."
  4. Both <then> and <else> branches are silently skipped.

Root cause analysis

In IfModelHandler.handle(), the condition result is looked up from BranchState which is set by ConditionModelHandler only when <condition> is a preceding sibling of <if>. When <condition> is a child of <if>, the ConditionModelHandler runs inside the <if> scope, so the BranchState is not found by IfModelHandler. The handler then falls through to janinoFallback() which:

  1. Looks for a condition attribute on <if> (old Janino syntax) — not found
  2. Checks for Janino on the classpath — not found
  3. Emits the misleading Janino error
  4. Both branches are skipped

Suggested fix

In IfModelHandler, when no BranchState is found and no condition attribute exists, check if <condition> appears as a child element of <if> and emit a helpful warning:

WARN: <condition> element found as child of <if> but it must be a preceding sibling. See https://logback.qos.ch/...

This would save users significant debugging time, as the current "Janino not found" message sends them on a wild goose chase.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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