<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>Hi again,</p>
    <p>Of course, I sent the email after spending all morning on it and
      then I worked it out.<br>
      The problem was in the method I didn't send:<br>
    </p>
    <pre>  @Override
  @SuppressWarnings("unchecked")
  public ExecutionStatus configure(LoggerContext lc) {
    configure(lc, !Strings.isNullOrEmpty(System.getenv(KUBERNETES_ENV_KEY)));
    return ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY;
  }

</pre>
    <p>I was returning ExecutionStatus.NEUTRAL, which I think was
      allowing BasicConfigurator to run after mine.</p>
    <p>As things stand now it shouldn't matter what order Configurators
      are called in, if mine is called first no others will be invoked
      and if mine is called last it clears everything else out.<br>
    </p>
    <p>Jim<br>
    </p>
    <p><br>
    </p>
    <div class="moz-cite-prefix">On 04/05/2023 14:14, logback users list
      via logback-user wrote:<br>
    </div>
    <blockquote type="cite"
      cite="mid:mailman.1362.1683206090.23762.logback-user@qos.ch">
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <p>Hi,</p>
      <p>I have a custom Configurator installed via META_INF.services in
        a bunch of different services.<br>
        I do it this way because I was finding that managing logback.xml
        files across a lot of different services was awkward and
        unnecessary log levels were leaking into production.<br>
        I also have a custom class for changing specific log levels
        based on env vars or system properties, and a vertx router for
        changing them dynamically at runtime.</p>
      <p>All of this has been working work a few years, but I have
        recently found that in some circumstances I'm getting duplicate
        messages (at first it was just in some builds, but now it's in
        production so I need to do something about it).</p>
      <p>I've added a load of debug spew to my classes, but it appears
        that the default configuration is being applied after my custom
        Configurator and I can't work out what is causing that.<br>
        And, of course, right now I can only reproduce this by executing
        a jar on the command line, rather than in a unit test.</p>
      <p>My main method looks like this:</p>
      <pre>  public static void main(String[] args) {
    Main main = new Main(args);
    logger.info("Starting ({})", LoggerFactory.getILoggerFactory());
</pre>
      <pre>    LoggerContext lc = ((LoggerContext) LoggerFactory.getILoggerFactory());
    System.out.println(lc.getName());
    for (ch.qos.logback.classic.Logger logger : lc.getLoggerList()) {
      System.out.println("\t" + logger.getName());
      for (Iterator<Appender<ILoggingEvent>> iter = logger.iteratorForAppenders(); iter.hasNext(); ) {
        Appender<ILoggingEvent> a = iter.next();
        System.out.println("\t\t" + a.getName());
      }
    }
    // irrelevant stuff here    
  }

and my custom Configurator is this:
<pre>  /**
   * Perform the configuration.
   * @param lc The LoggerContext to configure.
   * @param asJson If true then logs will be recorded as JSON.
   */
  public void configure(LoggerContext lc, boolean asJson) {    
    @SuppressWarnings("unchecked")
    Map<Object, Object> ruleRegistry = (Map<Object, Object>) lc.getObject(CoreConstants.PATTERN_RULE_REGISTRY);
    if (ruleRegistry == null) {
      ruleRegistry = new HashMap<>();      
      lc.putObject(CoreConstants.PATTERN_RULE_REGISTRY, ruleRegistry);
    }
    registerConverters(ruleRegistry);

    Appender<ILoggingEvent> appender;
    if (asJson) {
      appender = configureJsonOutput(lc, createConsoleAppender(lc));
    } else {
      appender = configureMultiLineOutput(lc, createConsoleAppender(lc));
    }
    appender.start();
    
    System.out.println(lc.getName());
    for (Logger logger : lc.getLoggerList()) {
      System.out.println("\t" + logger.getName());
      for (Iterator<Appender<ILoggingEvent>> iter = logger.iteratorForAppenders(); iter.hasNext(); ) {
        Appender<ILoggingEvent> a = iter.next();
        System.out.println("\t\t" + a.getName());
      }
      logger.detachAndStopAllAppenders();
    }
    
    Logger rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME);
    rootLogger.addAppender(appender);
    rootLogger.setLevel(Level.INFO);
    rootLogger.setAdditive(true);

    System.out.println(lc.getName());
    for (Logger logger : lc.getLoggerList()) {
      System.out.println("\t" + logger.getName());
      for (Iterator<Appender<ILoggingEvent>> iter = logger.iteratorForAppenders(); iter.hasNext(); ) {
        Appender<ILoggingEvent> a = iter.next();
        System.out.println("\t\t" + a.getName());
      }
    }
  }

</pre>The output from this is:
<pre>NOTE: Picked up JDK_JAVA_OPTIONS: -server -XX:-OmitStackTraceInFastThrow
default
        ROOT
default
        ROOT
                STDOUTPUT
2023-05-04 12:55:18.653 [main] c.groupgti.shared.configservice.Main INFO  - Starting (ch.qos.logback.classic.LoggerContext[default])
13:55:18.653 [main] INFO com.groupgti.shared.configservice.Main -- Starting (ch.qos.logback.classic.LoggerContext[default])
default
        ROOT
                STDOUTPUT
                console
        com
        com.groupgti
        com.groupgti.shared
        com.groupgti.shared.configservice
        com.groupgti.shared.configservice.Main
        + more packages
</pre>
</pre>
      So my custom Configurator is running, but then (I think when the
      static loggers are created) the console appender is being added to
      the ROOT logger.<br>
      <p>I tried naming my appender "console", that just resulted in two
        appenders called "console" :)</p>
      <p>How can I stop the default "console" appender being added to
        root?</p>
      <p>Thanks</p>
      <p>Jim<br>
      </p>
      <br>
      <br>
      <p><br>
      </p>
      <br>
      <fieldset class="moz-mime-attachment-header"></fieldset>
      <pre class="moz-quote-pre" wrap="">_______________________________________________
logback-user mailing list
<a class="moz-txt-link-abbreviated" href="mailto:logback-user@qos.ch">logback-user@qos.ch</a>
<a class="moz-txt-link-freetext" href="https://mailman.qos.ch/cgi-bin/mailman/listinfo/logback-user">https://mailman.qos.ch/cgi-bin/mailman/listinfo/logback-user</a></pre>
    </blockquote>
  </body>
</html>