Scripts
Log4j provides support for JSR 223 scripting languages to be used in some of its components.
Additional runtime dependencies are required to enable JSR 223 scripting:
We assume you use
We assume you use
In order to enable a scripting language, its name must be included in the
|
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 aCDATA
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 totrue
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.
-
XML
-
JSON
-
YAML
-
Properties
<?xml version="1.0" encoding="UTF-8"?>
<Configuration xmlns="https://logging.apache.org/xml/ns"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
https://logging.apache.org/xml/ns
https://logging.apache.org/xml/ns/log4j-config-3.xsd">
<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>
</Configuration>
{
"Configuration": {
"Appenders": {
"Console": {
"name": "STDOUT",
"PatternLayout": {
"ScriptPatternSelector": {
"defaultPattern": "%d %p %m%n",
"ScriptRef": {
"ref": "SELECTOR_SCRIPT",
"PatternMatch": [
{
"key": "NoLocation",
"pattern": "[%-5level] %c{1.} %msg%n"
},
{
"key": "Flow",
"pattern": "[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n"
}
]
}
}
}
},
"Loggers": {
"Logger": {
"name": "EventLogger",
"ScriptFilter": {
"onMatch": "ACCEPT",
"onMismatch": "DENY",
"Script": {
"name": "EVENT_LOGGER_FILTER",
"language": "groovy",
"scriptText": "if (logEvent.getMarker() != null && logEvent.getMarker().isInstanceOf('FLOW'))) { return true; } else if (logEvent.getContextMap().containsKey('UserId')) { return true; } return false;"
}
}
},
"Root": {
"level": "INFO",
"ScriptFilter": {
"onMatch": "ACCEPT",
"onMismatch": "DENY",
"ScriptRef": {
"ref": "ROOT_FILTER"
}
},
"AppenderRef": {
"ref": "STDOUT"
}
},
"Scripts": {
"Script": {
"name": "SELECTOR_SCRIPT",
"language": "javascript",
"scriptText": "var result; if (logEvent.getLoggerName().equals('JavascriptNoLocation')) { result = 'NoLocation'; } else if (logEvent.getMarker() != null && logEvent.getMarker().isInstanceOf('FLOW')) { result = 'Flow'; } result;"
},
"ScriptFile": {
"name": "ROOT_FILTER",
"path": "scripts/filter.groovy"
}
}
}
}
}
}
Configuration:
Appenders:
Console:
name: "STDOUT"
PatternLayout:
ScriptPatternSelector:
defaultPattern: "%d %p %m%n"
ScriptRef:
ref: "SELECTOR_SCRIPT"
PatternMatch:
- key: "NoLocation"
pattern: "[%-5level] %c{1.} %msg%n"
- key: "Flow"
pattern: "[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n"
Loggers:
Logger:
name: "EventLogger"
ScriptFilter:
onMatch: "ACCEPT"
onMismatch: "DENY"
Script:
name: "EVENT_LOGGER_FILTER"
language: "groovy"
scriptText: |
if (logEvent.getMarker() != null
&& logEvent.getMarker().isInstanceOf("FLOW")) {
return true;
} else if (logEvent.getContextMap().containsKey("UserId")) {
return true;
}
return false;
Root:
level: "INFO"
ScriptFilter:
onMatch: "ACCEPT"
onMismatch: "DENY"
ScriptRef:
ref: "ROOT_FILTER"
AppenderRef:
ref: "STDOUT"
Scripts:
Script:
name: "SELECTOR_SCRIPT"
language: "javascript"
scriptText: |
var result;
if (logEvent.getLoggerName().equals("JavascriptNoLocation")) {
result = "NoLocation";
} else if (logEvent.getMarker() != null
&& logEvent.getMarker().isInstanceOf("FLOW")) {
result = "Flow";
}
result;
ScriptFile:
name: "ROOT_FILTER"
path: "scripts/filter.groovy"
Appenders.Console.name = STDOUT
Appenders.Console.PatternLayout.ScriptPatternSelector.defaultPattern = %d %p %m%n
Appenders.Console.PatternLayout.ScriptPatternSelector.ScriptRef.ref = SELECTOR_SCRIPT
Appenders.Console.PatternLayout.ScriptPatternSelector.PatternMatch[1].key = NoLocation
Appenders.Console.PatternLayout.ScriptPatternSelector.PatternMatch[1].pattern = [%-5level] %c{1.} %msg%n
Appenders.Console.PatternLayout.ScriptPatternSelector.PatternMatch[2].key = Flow
Appenders.Console.PatternLayout.ScriptPatternSelector.PatternMatch[2].pattern = \
[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n
Loggers.Logger.name = EventLogger
Loggers.Logger.ScriptFilter.onMatch = ACCEPT
Loggers.Logger.ScriptFilter.onMismatch = DENY
Loggers.Logger.ScriptFilter.Script.name = EVENT_LOGGER_FILTER
Loggers.Logger.ScriptFilter.Script.language = groovy
Loggers.Logger.ScriptFilter.Script.scriptText = \
if (logEvent.getMarker() != null && logEvent.getMarker().isInstanceOf("FLOW"))) {\
return true;\
} else if (logEvent.getContextMap().containsKey("UserId")) {\
return true;\
}\
return false;
Loggers.Root.level = INFO
Loggers.Root.ScriptFilter.onMatch = ACCEPT
Loggers.Root.ScriptFilter.onMismatch = DENY
Loggers.Root.ScriptFilter.ScriptRef.ref = ROOT_FILTER
Loggers.Root.AppenderRef.ref = STDOUT
Scripts.Script.name = SELECTOR_SCRIPT
Scripts.Script.language = javascript
Scripts.Script.scriptText = \
var result;\
if (logEvent.getLoggerName().equals("JavascriptNoLocation")) {\
result = "NoLocation";\
} else if (logEvent.getMarker() != null && logEvent.getMarker().isInstanceOf("FLOW")) {\
result = "Flow";\
}\
result;
Scripts.ScriptFile.name = ROOT_FILTER
Scripts.ScriptFile.path = scripts/filter.groovy
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 the 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)