2.3.2 日志管理
对于Spring来说日志管理框架是非常重要的外部依赖,因为:a)它是唯一的强制性外部依赖,b)每个人都喜欢从他们使用的工具中看到一些输出,c)Spring集成了许多其它工具,它们都选择了日志管理的依赖。应用程序开发者的目标之一是在整个应用程序(包括所有的外部组件)的中心位置统一配置日志管理,这是非常困难的因为现在有很多日志管理框架可供选择。
Spring中强制的日志管理依赖是Jakarta Commons Logging API(JCL)。Spring针对JCL进行编译,同时还使JCL Log对象对扩展Spring Framework的类可见。对用户来说所有版本的Spring使用相同的日志管理框架很重要:这样做便于应用程序迁移,因为Spring保存了向后兼容,即使对于扩展了Spring的应用也能向后兼容。我们让Spring的一个模块明确地依赖于commons-logging(JCL的典型实现),然后让所有其它模块都在编译期依赖于这个模块。如果你想知道哪个模块依赖了commons-logging,可以通过Maven来查看,其实在Spring框架中,其核心模块spring-core依赖了commons-logging。
commons-logging的优点是不需要其它任何东西就可以使应用程序运转起来。它通过一个运行时发现算法用于在classpath中寻找其它日志管理框架并且选择一个恰当的日志框架来使用(或者你也可以指定具体的日志框架)。如果没有发现可用的外部日志框架,Spring默认使用JDK(java.util.logging或JUL)的日志API来输出一份看起来很漂亮的日志。在大多数情况下,Spring应用程序的运行信息正确地输出到控制台上是很重要的。(译者注:日志信息对于程序的开发和调试尤其重要,因此一个良好的应用程序必然要配备一套完善的日志分析和处理系统。)
Log4j 1.2 or 2.x
Log4j 1.2目前已经停止更新。 此外,Log4j 2.3是最新的Java 6兼容版本,较新的Log4j 2.x版本需要Java 7+版本。
许多开发者使用Log4J作为日志管理框架。它是一个高效的并且完备的日志框架,实际上在构建和测试Spring的时候我们就是使用Log4J作为日志处理框架。Spring也提供了一些工具用于配置和初始化Log4J,所以某些模块在编译期可以选择依赖于Log4J。
为了使Log4J代替默认的JCL依赖(commons-logging),开发者只需提供一个配置文件(log4j.properties或log4j.xml)放在classpath根目录下即可。Maven中的配置如下:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
下面是log4j.properties的配置示例,用于打印日志到控制台:
log4j.rootCategory=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2}:%L - %m%n
log4j.category.org.springframework.beans.factory=DEBUG
如果要使用Log4j 2.x,您需要做的就是将Log4j放在类路径上,并提供配置文件(log4j2.xml,log4j2.properties或其他支持的配置格式)。 对于Maven用户,所需的最小依赖关系是:
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jcl</artifactId>
<version>2.6.2</version>
</dependency>
</dependencies>
如果需要使用SLF4J 来代理Log4j,则需要引入如下依赖:
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.6.2</version>
</dependency>
</dependencies>
下面是log4j2.xml的配置,用于将日志信息输出到控制台:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="org.springframework.beans.factory" level="DEBUG"/>
<Root level="error">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
不使用Commons Logging
不幸的是,commons-logging的运行时发现算法虽然对于终端用户很方便,但存在一定的问题。 如果你想避免JCL的标准查找,基本上有两种方法来关闭它:
- 从spring-core模块中去除对commons-logging的依赖(因为这是唯一明确依赖于commons-logging的地方)
- 依赖于一个特定的commons-logging且把其jar包换成一个空jar包(具体做法参考SELF4J FAQ)
如下,在dependencyManagement中添加部分代码就可以排除掉commons-logging了:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.8.RELEASE</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
现在这个应用可能是不完整的,因为在classpath上没有JCL API的实现,所以需要提供一个新的去修复它。下个章节我们将以SLF4J为例子为JCL提供一个替代实现。
使用SLF4J 整合 Log4j or Logback
Java的简单日志门面(SLF4J)是其他日志框架使用的流行API,Spring中的常用日志框架也都使用了SLF4J。 SLF4J通常与Logback结合使用,Logback是SLF4J API的具体实现。
SLF4J提供了绑定到许多常见的日志记录框架的功能,包括Log4j,同时它也提供了相反的功能:用于和其他日志框架的整合。 所以为了在Spring中使用SLF4J,您需要使用SLF4J-JCL整合依赖包替换commons-logging依赖包。配置好SLF4J-JCL整合依赖包后,在Spring中日志记录调用将被转换为对SLF4J API的调用,因此如果你的应用程序中的其他类库也使用到了SLF4J API,那么您只需要在一个位置来统一配置和管理日志了。
常见的选择方案可能是将Spring与SLF4J整合,然后提供从SLF4J到Log4j的显式整合。 这需要提供多个依赖关系(并排除现有的commons-logging):JCL整合包,SLF4j-Log4j整合包以及Log4j提供程序本身。 在Maven中的依赖如下:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.8.RELEASE</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
应用SLF4J时更常见的选择方案是直接整合Logback,这中方案会将减少依赖和配置步骤。 因为Logback直接实现了SLF4J,所以只需要依赖于两个库,即jcl-over-slf4j和logback:
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
</dependencies>
使用JUL (java.util.logging)
Commons Logging的默认实现是java.util.logging,前提是在类路径中没有检测到Log4j。 所以不需要额外的依赖:只要在独立应用程序(在JDK级别使用自定义或默认的JUL设置)或应用程序服务器的“独立应用程序”中使用没有外部依赖关系的日志输出到java.util.logging 日志系统(及其系统范围的JUL设置)。
WebSphere上应用Commons Logging
Spring应用程序一般都运行在容器中,而这个容器本身又提供了JCL的实现。IBM的Websphere Application Server(WAS)是一个典型的例子。这不会引起任何问题,但有以下两种情形需要理解:
在“父类优先加载”的ClassLoader委托模型(WAS中的默认值)中,应用程序将始终选择Commons Logging的服务器版本,委托给WAS日志子系统(实际上基于JUL)。 应用程序提供的CL变体,无论是标准的Commons Logging还是JCL-over-SLF4J桥以及任何本地包含的日志提供程序都将被有效地忽略。
使用“父类后加载”的类加载委托模型(常规Servlet容器中的默认加载方式,WAS上的需要显式配置),将提取应用程序提供的Commons Logging变体,从而可以设置本地包含的日志提供程序,例如。在您的应用程序中可以设置 Log4j或Logback。如果没有本地日志提供程序,默认情况下,常规Commons Logging将委托给JUL,有效地记录到WebSphere的日志记录子系统,这个和“父类优先加载”模型是一致的。
总而言之,我们建议您将“Spring”应用程序部署在“父类后加载”的模式中,因为它能设置本地提日志供程序以及服务器的日志子系统。