logginglog4j2

Use Environment Variables With Conditional Logic on Log4j2.properties


I'm using Log4j2 with a log4j2.properties configuration file, and I rely on environment variables for some settings. Here's a sample of my configuration:

rootLogger.level = INFO
rootLogger.appenderRefs = console
rootLogger.appenderRef.console.ref = ConsoleAppender

logger.customlog.name = my.custompackage
logger.customlog.level = ${env:APP_LOG_LEVEL:-INFO}
logger.customlog.appenderRefs = console${env:ENABLE_FLUENTD:true:-,fluentd}
logger.customlog.appenderRef.fluentd.ref = ${env:ENABLE_FLUENTD:true:-FluentdAppender}

appender.console.name = ConsoleAppender
appender.console.type = CONSOLE
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{HH:mm:ss,SSS} %-5p %-60c - %m%n

appender.fluentd.name = FluentdAppender
appender.fluentd.type = ${env:ENABLE_FLUENTD:-Socket}
appender.fluentd.host = ${env:FLUENTD_HOST:-127.0.0.1}
appender.fluentd.port = ${env:FLUENTD_PORT:-24224}
appender.fluentd.layout.type = PatternLayout
appender.fluentd.layout.pattern = ["logs", {"timestamp":"%d{ISO8601}", "level":"%p", "message":"%m"}

I would like a way to enable/disable appenders based on environmental variables. Specifically I would like to disable FluentdAppender when ENABLE_FLUENTD environmental variable is not set to true.

Is there a way to achieve this ?


Solution

  • There is a feature called "Arbiters" that allows you to conditionally insert configuration elements (see documentation). Using the default XML configuration file format, you can use it as follows:

    <EnvironmentArbiter propertyName="ENABLE_FLUENTD" propertyValue="true">
      <Socket name="FluentdAppender"
              host="${env:FLUENTD_HOST:-127.0.0.1}"
              port="${env:FLUENTD_PORT:-24224">
        <JsonTemplateLayout/>
      </Socket>
    </EnvironmentArbiter>
    

    The Java System Properties Configuration file format, which I really don't recommend, has a lot of quirks (see documentation) that render the usage of arbiters hard or impossible. With Java System Properties it should be possible to use:

    appender.fluentd.type = EnvironmentArbiter
    appender.fluentd.propertyName = ENABLE_FLUENTD
    appender.fluentd.propertyValue = true
    appender.fluentd.name = Dummy name required by Properties format
    
    appender.fluentd.0.type = Socket
    appender.fluentd.0.name = FluentdAppender
    appender.fluentd.0.host = ${env:FLUENTD_HOST:-127.0.0.1}
    appender.fluentd.0.port = ${env:FLUENTD_PORT:-24224}
    appender.fluentd.0.layout.type = JsonTemplateLayout
    

    Remark: If you want to output JSON, use a layout that outputs JSON like JSON Template Layout. Pattern layout should be used to output simple text. The pattern ["logs", {"timestamp":"%d{ISO8601}", "level":"%p", "message":"%m"}] you are using does not escape JSON string values correctly. Moreover, if an exception is logged, its stack trace will be appended to this pattern.