Scripts

Log4j provides support for JSR 223 scripting languages to be used in some of its components.

In order to enable a scripting language, its name must be included in the log4j2.scriptEnableLanguages configuration property.

Each component that allows scripts can contain on of the following configuration elements:

Script

This element specifies the content of the script directly and has:

  • a required language configuration attribute that specifies the name of the JSR 223 language to use,

  • a required scriptText configuration attribute that contains the text of the script. In the XML configuration format, the text of the script can also be written as content of the <Script> XML element. This allows the usage of a CDATA block.

The element can be assigned a name using the name configuration attribute.

See also Plugin reference.

ScriptFile

This element points to an external script file and has:

  • a required path attribute that points to the path to a file name.

  • an optional language attribute that specifies the name of the JSR 223 language to use. If not provided, the language is deduced from the extension of the file.

  • an optional isWatched attribute. If set to true the script file will be monitored for changes.

The element can be assigned a name using the name configuration attribute.

See also Plugin reference.

ScriptRef

This element references a named script from the global Scripts container plugin in the configuration file.

See also Plugin reference.

The environment in which the script runs is different for each Log4j script-based component.

Snippet from an example log4j2.xml
<Appenders>
  <Console name="STDOUT">
    <PatternLayout>
      <ScriptPatternSelector defaultPattern="%d %p %m%n">
        <ScriptRef ref="SELECTOR_SCRIPT"/>
        <PatternMatch key="NoLocation" pattern="[%-5level] %c{1.} %msg%n"/>
        <PatternMatch key="Flow"
                      pattern="[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n"/>
      </ScriptPatternSelector>
    </PatternLayout>
  </Console>
</Appenders>
<Loggers>
  <Logger name="EventLogger">
    <ScriptFilter onMatch="ACCEPT" onMismatch="DENY">
      <Script name="EVENT_LOGGER_FILTER" language="groovy"><![CDATA[
        if (logEvent.getMarker() != null
            && logEvent.getMarker().isInstanceOf("FLOW")) {
          return true;
        } else if (logEvent.getContextMap().containsKey("UserId")) {
          return true;
        }
        return false;
        ]]>
      </Script>
    </ScriptFilter>
  </Logger>
  <Root level="INFO">
    <ScriptFilter onMatch="ACCEPT" onMismatch="DENY">
      <ScriptRef ref="ROOT_FILTER"/>
    </ScriptFilter>
    <AppenderRef ref="STDOUT"/>
  </Root>
</Loggers>
<Scripts>
  <Script name="SELECTOR_SCRIPT" language="javascript"><![CDATA[
    var result;
    if (logEvent.getLoggerName().equals("JavascriptNoLocation")) {
      result = "NoLocation";
    } else if (logEvent.getMarker() != null
        && logEvent.getMarker().isInstanceOf("FLOW")) {
      result = "Flow";
    }
    result;
    ]]>
  </Script>
  <ScriptFile name="ROOT_FILTER" path="scripts/filter.groovy"/>
</Scripts>
xml

A special note on Beanshell

JSR 223 scripting engines are supposed to identify that they support the Compilable interface if they support compiling their scripts.

Beanshell does extend the Compilable interface, but an attempt to compile a script ends up in an Error being thrown. Log4j catches the throwable, but issues a warning in Status Logger.

2015-09-27 16:13:23,095 main DEBUG Script BeanShellSelector is compilable
2015-09-27 16:13:23,096 main WARN Error compiling script java.lang.Error: unimplemented
            at bsh.engine.BshScriptEngine.compile(BshScriptEngine.java:175)
            at bsh.engine.BshScriptEngine.compile(BshScriptEngine.java:154)
            at org.apache.logging.log4j.core.script.ScriptManager$MainScriptRunner.<init>(ScriptManager.java:125)
            at org.apache.logging.log4j.core.script.ScriptManager.addScript(ScriptManager.java:94)