日志

日志库选择

简而言之,在现代(比如2015年)的应用程序中, 记录日志推荐使用SLF4J API。 要让 FreeMarker 2.3.x. 使用SLF4J,在项目中加入依赖 org.slf4j:log4j-over-slf4j 即可, 要确保 log4j:log4j 不能存在。(从 FreeMarker 2.4.x 开始,尽管没有什么害处, 但也不再需要 log4j-over-slf4j 了。)

如果你对这些细节好奇,或者不能使用SLF4J,那么就继续阅读吧...

FreeMarker 整合了如下的日志包:SLF4JApache Commons LoggingLog4J 1.x,Avalon LogKit java.util.logging。默认情况下, FreeMarker(在2.3.x版本下)会按如下顺序来查找日志包, 而且会自动使用第一个发现的包: LOG4J(从2.3.22开始,如果正确安装了log4j-over-slf4j,则会使用SLF4J来代替), Apache Avalon LogKit, java.util.logging。 正如你所见,Log4j有最高的优先级。org.apache.log4j.Logger 类会检测Log4j的存在,那么也就是说,像log4j-over-slf4jlog4j-1.2-api,Log4j重定向也会有最高优先级。

在 FreeMarker 2.4 版本之前,因为向后兼容性的限制, SLF4J和Apache Commons Logging不会被自动搜索。但是如果你正确安装了 org.slf4j:log4j-over-slf4j(也就意味着, 在类路径下没有真实的Log4j,SLF4J有一个像 logback-classic 的支持实现),那么FreeMarker会直接使用SLF4J API来代替Log4j API (从FreeMarker 2.3.22版本开始)。

请注意,应用Log4j2日志有个相似的技巧:如果 org.apache.logging.log4j:log4j-1.2-api 可用, FreeMarker 2.3.x会使用它,因为它看起来就像Log4j, 但是所有的消息都会自动到Log4j2中。

如果自动检测没有给出你想要的结果,那么你可以设置系统属性 org.freemarker.loggerLibrary 来明确选择 (从2.3.22版本开始)一个日志库,比如:

java ... -Dorg.freemarker.loggerLibrary=SLF4J

系统属性支持的值有: SLF4JCommonsLoggingJUL (即 java.util.logging), Avalonauto (默认行为), none (关闭日志)。

请注意,为了可靠的运行,系统属性应该在JVM启动时(向上面那样)就该设置好, 而不是在Java代码之后。

推荐使用SLF4J,因为它在 FreeMarker 中运行的更好, 也是因为从 FreeMarker 2.4 版本开始它有自动检测的最高优先级。

日志分类

由FreeMarker产生的所有日志信息会被记录到名称由 freemarker.开头的日志记录器中。 现在被使用的记录器是:

日志分类名称 目标
freemarker.beans 记录Beans包装器模块的日志信息。
freemarker.cache 记录模板加载和缓存相关的日志信息。
freemarker.runtime 记录在模板执行期间的和特定分类无关的相关信息。 更重要的是,它会记录模板异常并在模板处理期间抛出 (但它却应该在现行的应用程序中禁用;稍后将会解释)。
freemarker.runtime.attempt 记录在模板执行期间抛出的模板异常日志信息, 但是是开启DEBUG严重级别,并由 attempt/recover 指令捕捉。 请注意,该异常也会被记录到正常的日志记录器中 (比如freemarker.runtime)。
freemarker.servlet 记录来自 FreemarkerServlet 类的消息。
freemarker.jsp 记录FreeMarker JSP 支持的消息。

FreeMarker 会在模板执行期间使用 freemarker.runtime 记录异常,即便异常继续增加,最终由 Template.processEnvironment.process 抛出。 (那些都是从应用程序或框架中调用模板时的API调用。) 良好的应用程序会记录它们抛出的异常,极少数情况下是处理它们而不去记录日志。 但是FreeMarker已经记录了异常,那么就会得到比期望的多一条日志记录。 要修复这个问题(从2.3.22版本开始),可以设置 log_template_exceptions (Configurable.setLogTemplateExceptions(boolean)) 为 false