2.3.2 日志
2.3.2 日志
日志是Spring非常重要的一个依赖,这是因为:1)它是唯一的强制性外部依赖;2)每个人都喜欢看到他们使用的工具输出一些信息;而且3)Spring集成了许多其他工具,它们都选择了各自的日志依赖。在一个集中的位置为整个应用程序甚至外部组件统一配置日志信息,往往是应用程序开发者追求的一个目标。这比想象的更加困难,因为有太多的日志框架可以选择。
Spring中强制使用的日志依赖是Jakarta Commons Logging API(JCL)。我们编译了JCL,而且使JCL的日志对象对于扩展Spring Framework的类可见。所有版本的Spring都使用相同的日志库对于用户来说很重要:迁移很简单,因为即使扩展了Spring的程序也保留了向后兼容性。我们是这样做的,使Spring中的某个模块显式的依赖于commons-logging
(JCL的标准实现),然后让所有其他的模块在编译时依赖它。举例来说,如果您使用Maven,并且想知道从哪里获得了对commons-logging
的依赖,那么依赖就是来自Spring,准确来说来自核心模块spring-core
。
commons-logging
的好处是您不需要其他任何东西就能使您的程序工作。它有一个运行时发现算法,在众所周知的类路径中找寻其他的日志框架,并从中挑选一个它认为合适的来使用(或者如果需要,您可以告诉它用哪一个)。如果没有可用的,就使用JDK提供的相当不错的日志(java.util.logging,简称JUL)。重要的是,在大多数情况下,您会发现您的Spring应用程序已经可以工作,并且日志愉快地打印到了控制台上。
使用Log4J 1.2或2.x
Log4j 1.x已经寿终正寝,而且Log4j 2.3是最后一个Java 6兼容的版本,更新的Log4j 2.x版本需要Java 7+。
很多人由于配置和管理的目的使用Log4j。Log4j是高效且完善的,实际上我们构建和测试Spring时在运行时使用的也是它。Spring还提供了一些用于配置和初始化Log4j的工具,所以在一些模块中Log4j是可选的编译期依赖。
要使Log4j 1.2和默认的JCL依赖(commons-logging
)一起工作,您只需要把Log4j放到类路径中,并为其提供一个配置文件(在类路径的根目录中的log4j.properties
或log4j.xml
)。所以对于Maven用户来说,这是您的依赖声明:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.12.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与JCL配合使用,您只需将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,例如对于默认使用SLF4J的其他库,那么还需要以下依赖:
<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
API中的运行时发现算法是有问题的。如果您想避免JCL的标准查找,基本上有两种方法可以将其关闭:
- 从
spring-core
模块中排除该依赖(因为它是唯一显式依赖于commons-logging
的模块) - 依赖一个特殊的
commons-logging
,其使用空的jar包替代该库(更多细节请见SLF4J FAQ)
要排除commons-logging
,将以下内容添加到您的dependencyManagement
段中:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.12.RELEASE</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
现在,程序已经无法运行了,因为类路径中没有了JCP API的实现,所以,要修复它必须提供一个新的实现。在下一节中,我们向您展示如何使用SLF4J提供JCL的替代实现。
结合Log4j或Logback使用SLF4J
Java的简单日志外观(SLF4J,Simple Logging Facade for Java)是常常与Spring一起使用的其他库常用的API。SLF4J通常与Logback一起使用,它是SLF4J API的本地实现。
SLF4J可以绑定到许多常见的日志框架,包括Log4j。同时,SLF4J还可以充当其他日志框架和其本身之间的桥梁。所以要在Spring中使用SLF4J,您需要使用SLF4J-JCL桥替代commons-logging
依赖。一旦您这样做了,Spring内部的日志调用都将会被转换为对SLF4J API的调用,所以如果您程序中的其他库调用了该API,那么您就有一个单一的地方配置和管理日志了。
一个常见的选择可能是桥接Spring到SLF4J,然后将SLF4J显式绑定到Log4J。您需要提供多个依赖(并且排除已有的commons-logging
):JCL桥、绑定到Log4J的SLF4J以及Log4J本身。在Maven中您会这样做:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.12.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设置)或应用程序服务器的日志系统(及其系统范围内的JUL设置)中,为了将日志输出到java.util.logging
使用没有外部依赖的Spring。
WebSphere上的Commons Logging
Spring程序可能运行在本身提供JCL实现的容器上,例如IBM的Websphere应用服务器(WAS)。这本身不会导致问题,但会导致需要理解的两种不同情况:
在“父类优先”的ClassLoader委托模型(WAS默认模型)中,程序总是会取得服务器提供的Commons Logging版本,委托给WAS的日志子系统(实际上基于JUL)。程序提供的JCL变体,无论是标准的Commons Logging还是JCL-over-SLF4J桥,都会与任何本地包含的日志提供程序一起被忽略。
在“父类置后”的委托模型(常规Servlet容器的默认设置,但在WAS上需要显式配置)下,将选取程序提供的Commons Logging变体,使您能够设置程序中使用的本地包含的日志提供程序。在没有本地日志提供程序的情况下,常规的Commons Logging默认会委托给JUL,有效地记录到WebSphere的日志子系统中,就像在“父类优先”的场景中那样。
总之,我们建议在“父类置后”模型中部署Spring应用程序,因为它天生就允许本地提供程序以及服务器的日志子系统。