Log4j
支持符合JSR223的脚本语言使用在它的组件上。任何一种支持符合JSR223
标准的语言引擎都可以使用。所有的语言列表可以在JSR223
脚本引擎的页面中找到。很多语言被列在这里,例如javaScript
,Groovy
,BeanShell
,直接支持JSR223
语言框架,只要引入了jar就可以支持选择的语言了。Log4j
通过<script>
,<scriptFile>
和<scriptRef>
这三个元素标签来支持脚本语言的使用。Script
元素包含了脚本的名称,脚本编写的语言,以及脚本的内容。
scriptFile
包含了脚本的名称,和这个脚本所在的位置,语言,字符集,是否对脚本的变化进行监视。
scriptRef
包含了在script元素定义的脚本名称。
脚本的名称通常用来存储脚本,并且和他的脚本引擎在一起,因此每次脚本需要运行的时候都可以被快速定位。然而脚本的名字不是必须的,提供名字方便在运行的时候有助于调试。在script
元素中必须指定脚本语言(language属性),而且这些语言必须是在配置列表中的语言。如果脚本语言没有在scriptFile
元素上指定,那么通过脚本的扩展名来识别脚本语言。如果脚本文件监控需要执行,那么必须在configuration
元素中设置monitorInterval
为非0的值。间隔interval
时长,系统将会检测脚本文件的变化。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug" name="RoutingTest">
<Scripts>
<Script name="selector" 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="groovy.filter" path="scripts/filter.groovy"/>
</Scripts>
<Appenders>
<Console name="STDOUT">
<ScriptPatternSelector defaultPattern="%d %p %m%n">
<ScriptRef ref="selector"/>
<PatternMatch key="NoLocation" pattern="[%-5level] %c{1.} %msg%n"/>
<PatternMatch key="Flow" pattern="[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n"/>
</ScriptPatternSelector>
<PatternLayout pattern="%m%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="EventLogger" level="info" additivity="false">
<ScriptFilter onMatch="ACCEPT" onMisMatch="DENY">
<Script name="GroovyFilter" 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>
<AppenderRef ref="STDOUT"/>
</Logger>
<Root level="error">
<ScriptFilter onMatch="ACCEPT" onMisMatch="DENY">
<ScriptRef ref="groovy.filter"/>
</ScriptFilter>
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>
如果Configuration
元素设置status
为DEBUG
,那么启动的时候当前安装的脚本引擎以及脚本的信息,将会在输出日志中逐条显示出来。
尽管有些引擎不是线程安全的,但是Log4j
会确保他们在线程安全的模式下运行。
2015-09-27 16:13:22,925 main DEBUG Installed script engines
2015-09-27 16:13:22,963 main DEBUG AppleScriptEngine Version: 1.1, Language: AppleScript, Threading: Not Thread Safe,
Compile: false, Names: {AppleScriptEngine, AppleScript, OSA}
2015-09-27 16:13:22,983 main DEBUG Groovy Scripting Engine Version: 2.0, Language: Groovy, Threading: MULTITHREADED,
Compile: true, Names: {groovy, Groovy}
2015-09-27 16:13:23,030 main DEBUG BeanShell Engine Version: 1.0, Language: BeanShell, Threading: MULTITHREADED,
Compile: true, Names: {beanshell, bsh, java}
2015-09-27 16:13:23,039 main DEBUG Mozilla Rhino Version: 1.7 release 3 PRERELEASE, Language: ECMAScript, Threading: MULTITHREADED,
Compile: true, Names: {js, rhino, JavaScript, javascript, ECMAScript, ecmascript}
当脚本被执行,他们会提供一系列的变量,这些变量保证完成 各种他们期望执行的各种任务。脚本可用变量的列表,可以查看每个组件的相关文档。
组件期望脚本的返回值传递给Java代码。对于大多数脚本语言都不是问题,但是JavaScript不允许返回,除非是在一个fuction里。
然后JavaScript
将脚本中的最后一个表达式返回。
如下的代码就是我们期望的结果(result被返回)。
var result;
if (logEvent.getLoggerName().equals("JavascriptNoLocation")) {
result = "NoLocation";
} else if (logEvent.getMarker() != null && logEvent.getMarker().isInstanceOf("FLOW")) {
result = "Flow";
}
result;
Beanshell 备注
JSR 223
脚本引擎规定如果引擎支持编译他们的脚本,需要识别他们支持的编译接口。Beanshell
就有这样的特点。
然后无论什么情况编译方法只要被调用就会报一个错误(不是异常)
Log4j
捕获了这个错误,并且将这个错误打印了出来。所有的BeanShell
脚本在每次执行的时候才进行解释(解释执行)。
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)