title: Log4j 2.9.1 adding ThresholdFilter to Appender programmatically
body: <p>I have a serious problem with log4j 2.9.x
  I write "serious" because the documentation of log4j is reeeeally crap!
  There is no clear and up-to-date documentation for this version. Even the existing documented code don't pass the compiler since they did breaking changes every now and then and you can't be sure which version they have documented. I googled a whole day but what i found is mostly outdated too.<br>
  So I am a bit confused configuring log4j2 programmatically.</p>
  
  <p>what I want to achieve:</p>
  
  <p>1) additional "loglevel" that always prints to console independently of current loglevel (analog to gradles LIFECYCLE)</p>
  
  <p>2) log to console and file</p>
  
  <p>3) filename determined at runtime</p>
  
  <p>I found a way to achieve 1) and 2) with XML. I haven't found a suitable way for 3) . I dont like to use a static context or system variable for the filename since i expect concurrency issues. </p>
  
  <p>Therefore my approach is to configure log4j programmatically. Here my configuration factory:</p>
  
  <pre><code>package my.abc
  
  import org.apache.logging.log4j.Level
  import org.apache.logging.log4j.core.Filter
  import org.apache.logging.log4j.core.LoggerContext
  import org.apache.logging.log4j.core.config.Configuration
  import org.apache.logging.log4j.core.config.ConfigurationFactory
  import org.apache.logging.log4j.core.config.ConfigurationSource
  import org.apache.logging.log4j.core.config.Order
  import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder
  import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder
  import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder
  import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration
  import org.apache.logging.log4j.core.config.plugins.Plugin
  
  @Plugin(name = "CustomConfigurationFactory", category = ConfigurationFactory.CATEGORY)
  @Order(50)
  class CustomConfigurationFactory extends ConfigurationFactory {
  
      private static String MESSAGE_PATTERN = "%d{HH:mm:ss.SSS} [%t] %-5level 123 %logger{36} - %msg%n"
  
      Level logLevel
      String packageToScan
  
      CustomConfigurationFactory(Level logLevel, String packageToScan) {
          this.logLevel = logLevel
          this.packageToScan = packageToScan
      }
  
      private Configuration createConfiguration(final String name, ConfigurationBuilder&lt;BuiltConfiguration&gt; builder) {
          builder.setConfigurationName(name)
          builder.setStatusLevel(Level.ERROR)
  
          //add appender
          builder.add(createConsoleAppender(builder))
          builder.add(createLifecycleAppender(builder))
  
          //add logger
          builder.add(createLogger(builder))
  
  //        builder.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.DENY)
  //                .addAttribute("level", logLevel))
  
          builder.add(builder.newRootLogger(Level.TRACE).add(builder.newAppenderRef("stdout")))
          return builder.build()
      }
  
      @Override
      Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) {
          return getConfiguration(loggerContext, source.toString(), null)
      }
  
      @Override
      Configuration getConfiguration(final LoggerContext loggerContext, final String name, final URI configLocation) {
          ConfigurationBuilder&lt;BuiltConfiguration&gt; builder = newConfigurationBuilder()
          return createConfiguration(name, builder)
      }
  
      @Override
      protected String[] getSupportedTypes() {
          return [{ "*" }]
      }
  
      private AppenderComponentBuilder createConsoleAppender(ConfigurationBuilder&lt;BuiltConfiguration&gt; builder) {
          AppenderComponentBuilder appenderBuilder = builder.newAppender("stdout", "CONSOLE")
          appenderBuilder.add(builder.newLayout("PatternLayout").
                  addAttribute("pattern", MESSAGE_PATTERN))
          appenderBuilder.add(builder.newFilter("MarkerFilter", Filter.Result.DENY,
                  Filter.Result.NEUTRAL).addAttribute("marker", "lifecycle"))
  //        appenderBuilder.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT,
  //                Filter.Result.DENY).addAttribute("level", logLevel))
          return appenderBuilder
      }
  
      private AppenderComponentBuilder createLifecycleAppender(ConfigurationBuilder&lt;BuiltConfiguration&gt; builder) {
          AppenderComponentBuilder appenderBuilder = builder.newAppender("lifecycle", "CONSOLE")
          appenderBuilder.add(builder.newLayout("PatternLayout").
                  addAttribute("pattern", "%d{HH:mm:ss.SSS} [%t] LIFECYCLE %logger{36} - %msg%n"))
          appenderBuilder.add(builder.newFilter("MarkerFilter", Filter.Result.ACCEPT, Filter.Result.DENY)
                  .addAttribute("marker", "lifecycle"))
          return appenderBuilder
      }
  
      private LoggerComponentBuilder createLogger(ConfigurationBuilder&lt;BuiltConfiguration&gt; builder) {
          builder.newLogger(packageToScan, "trace")
                  .add(builder.newAppenderRef("stdout"))
                  .add(builder.newAppenderRef("lifecycle"))
                  .addAttribute("additivity", false)
      }
  }
  </code></pre>
  
  <p>You may noticed the 2 outcommented lines. Thats my problem. </p>
  
  <p>When i use <code>builder.add(builder.newFilter("ThresholdFilter"...</code> all logs will be filtered out when they dont fit the loglevel. Even the "lifecycle"-logs that i want to be logged in any case.</p>
  
  <p>When i use 
  <code>appenderBuilder.add(builder.newFilter("ThresholdFilter...</code> i got the error message <code>2017-11-23 12:12:34,905 main ERROR appender CONSOLE has no parameter that matches element ThresholdFilter</code></p>
  
  <p>What is wierd. Because when i configure everything with xml, i can do exactly that thing(adding thresholdfilter to a single appender) and it works.</p>
  
  <pre><code>&lt;Appender type="Console" name="STDOUT" &gt;
     &lt;Filters&gt;
        &lt;Filter type="MarkerFilter" marker="lifecycle" onMatch="DENY" onMismatch="NEUTRAL"/&gt;
        &lt;Filter type="ThresholdFilter" level="${logLevel}" onMatch="ACCEPT" onMismatch="DENY" /&gt;
     &lt;/Filters&gt;
     &lt;Layout type="PatternLayout" pattern="${pattern}"/&gt;
  &lt;/Appender&gt;
  </code></pre>
  
  <p>To beeing complete here the whole xml that works for erequirements 1) and 2)</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  &lt;Configuration status="info" strict="true" name="XMLConfigTest"
                 packages="org.apache.logging.log4j.test"&gt;
      &lt;Properties&gt;
          &lt;Property name="logLevel"&gt;error&lt;/Property&gt;
          &lt;Property name="filename"&gt;target/test.log&lt;/Property&gt;
          &lt;Property name="pattern"&gt;%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n&lt;/Property&gt;
      &lt;/Properties&gt;
  
      &lt;Appenders&gt;
          &lt;Appender type="Console" name="STDOUT"&gt;
              &lt;Filters&gt;
                  &lt;Filter type="MarkerFilter" marker="lifecycle" onMatch="DENY" onMismatch="NEUTRAL"/&gt;
                  &lt;Filter type="ThresholdFilter" level="${logLevel}" onMatch="ACCEPT" onMismatch="DENY"/&gt;
              &lt;/Filters&gt;
              &lt;Layout type="PatternLayout" pattern="${pattern}"/&gt;
          &lt;/Appender&gt;
          &lt;Appender type="Console" name="LIFECYCLE"&gt;
              &lt;Filters&gt;
                  &lt;Filter type="MarkerFilter" marker="lifecycle" onMatch="ACCEPT" onMismatch="DENY"/&gt;
              &lt;/Filters&gt;
              &lt;Layout type="PatternLayout" pattern="%d{HH:mm:ss.SSS} [%t] LIFECYCLE %logger{36} - %msg%n"/&gt;
          &lt;/Appender&gt;
          &lt;Appender type="File" name="File" fileName="${filename}"&gt;
              &lt;Layout type="PatternLayout"&gt;
                  &lt;Pattern&gt;%d %p %C{1.} [%t] %m%n&lt;/Pattern&gt;
              &lt;/Layout&gt;
          &lt;/Appender&gt;
      &lt;/Appenders&gt;
  
      &lt;Loggers&gt;
          &lt;Logger name="my.abc" level="trace" additivity="false"&gt;
              &lt;AppenderRef ref="STDOUT"/&gt;
              &lt;AppenderRef ref="LIFECYCLE"/&gt;
          &lt;/Logger&gt;
  
          &lt;Root level="error"&gt;
              &lt;AppenderRef ref="STDOUT"/&gt;
          &lt;/Root&gt;
      &lt;/Loggers&gt;
  &lt;/Configuration&gt;
  </code></pre>
  
  <p>There has to be a way to add ThresholdFilter to the appender. I found the class ThresholdFilter. But i have absolutely no idea how to use it. instantiating works <code>ThresholdFilter.createFilter(...)</code> but where can I add it? </p>
  
  <p>To cut a long story short..</p>
  
  <p>How can I add ThresholdFilter to a single Appender without xml?</p>