Skip to content

File collision detection only happens during model analysis and misses runtime/dynamic appender collisions #1041

@srzou

Description

@srzou

Versions

  • logback version: 1.5.33
  • JDK version: 17

Describe the problem

FileCollisionAnalyser currently detects duplicate file / fileNamePattern values during the DEPENDENCY_ANALYSIS phase of model processing.

This works for collisions that are already visible at configuration parse time, but it does not seem to cover cases where appenders are created dynamically after initial configuration.

As a result, two appenders may end up writing to the same file even though the existing collision check did not reject them.

Minimal reproduction

With the configuration below, SiftingAppender creates one nested FileAppender per discriminator value.

If two different discriminator values are used, two different appenders are created (FILE-A and FILE-B), but both write to the same target file.

This case does not appear to be rejected by FileCollisionAnalyser.

logback.xml

<configuration debug="true">
  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />

  <property name="LOG_DIR" value="logs" />
  <property name="TARGET_FILE" value="${LOG_DIR}/shared.log" />

  <appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
    <discriminator>
      <Key>userId</Key>
      <DefaultValue>default</DefaultValue>
    </discriminator>

    <sift>
      <appender name="FILE-${userId}" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${TARGET_FILE}</file>
        <append>true</append>
        <encoder>
          <pattern>%msg%n</pattern>
        </encoder>
      </appender>
    </sift>
  </appender>

  <root level="INFO">
    <appender-ref ref="SIFT" />
  </root>
</configuration>

Example Java snippet

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

public class Demo {
    private static final Logger logger = LoggerFactory.getLogger(Demo.class);

    public static void main(String[] args) {
        MDC.put("userId", "A");
        logger.info("message from A");
        MDC.clear();

        MDC.put("userId", "B");
        logger.info("message from B");
        MDC.clear();
    }
}

Reproduction steps

  1. Start the application with the configuration above.
  2. Log one event with MDC userId=A.
  3. Log another event with MDC userId=B.

Actual behavior

Two different nested appenders are created and both write to the same file (logs/shared.log).

Expected behavior

The second appender should ideally be rejected at startup time because its effective file target is already in use by another appender.

Observations from the code

  • FileCollisionAnalyser is a model analyser in the DEPENDENCY_ANALYSIS phase.
  • It stores seen values in ModelInterpretationContext.
  • It skips initialization only for collisions detected during that model-processing pass.
  • AppenderFactoryUsingSiftModel appears to build dynamic appenders with its own processor setup, and it does not seem to register FileCollisionAnalyser.

So the current protection seems limited to configuration-time model analysis, not runtime appender startup.

Suggested direction

A more robust approach may be to perform collision detection at FileAppender.start() / RollingFileAppender.start() time, using a context-level registry of active file targets.

That registry could:

  • register the effective file target on successful start
  • reject startup if the same target is already owned by another appender
  • unregister on stop

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions