# slf4j 日志库
# 1. slf4j 和 slf4j-simple
<slf4j.version>1.7.30</slf4j.version>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
</dependency>
slf4j(Simple Logging Facade for Java)是现在最流行的 Java 日志库。它提供了 Java 中所有日志框架的简单抽象:slf4j-api 。
slf4j-simple 是它的一个实现。
提示
简单来说,slf4j-api 和 slf4j-simple 的关系类似于 “接口” 和 “实现类” 的关系。在这里,我们代码中使的 slf4j ,但实际干活的是 slf4j-simple 。
很容易猜到,slf4j-api 的实现并非只有一个,slf4j-simple 只是它最简单的一个实现。slf4j-simple 实现了最基本的功能,而且没有什么额外附加功能和特色功能。
注意
现实项目中不太可能使用 slf4j-simple,我们这里使用它的目的,是通过它去了解 slf4j-api 定义了哪些接口,“约定” 了哪些功能,大体是一个什么样的效果。这样好方便我们未来学习使用 slf4j-api 的其它实现。
# 1.1 基本使用
在 SLF4J 中获得 Logger 对象的方式是:
LoggerFactory.getLogger()
通过指定的名字获得 Logger 对象,如果必要的话,则为这个名字创建一个新的 Logger 对象。Name 一般取本类的名字,比如:
static Logger logger = LoggerFactory.getLogger(App.class);
slf4j 提供为不同级别的日志输出提供了不同的方法,常用的是:error()
、warn()
、info()
、debug()
。
public class App {
private static final Logger log = LoggerFactory.getLogger(App.class);
public static void main(String[] args) {
log.error("error");
log.warn("warn");
log.info("info");
log.debug("debug");
}
}
因为 slf4j-api 背后『真正干活』的是 slf4j-simple,因此显示效果是:
[main] ERROR org.example.App - error
[main] WARN org.example.App - warn
[main] INFO org.example.App - info
注意,上面没有 DEBUG 级别日志的输出,因为它级别太低,被过滤掉了。虽然,可以通过设置来降低显示级别,从而让 DEBUG 级别日志显示出来,但是由于实际项目中并不会使用 slf4j-simple,因此这里我们就不深究了。
如果你使用的是其它的 slf4j-api 的实现,那么显示日志信息的格式会有不同,不过无论使用哪种实现,日志文本信息肯定都是一样的。
# 1.2 {} 占位符
SLF4J 一个非常好的功能是,它提供了占位符(使用 {}
)的概念,以方便与在输出信息中『插入』数据。
logger.info("Hello {} World {}", 100, "Goodbye");
另外,如果通过 slf4j 打印异常的堆栈信息,异常的堆栈信息不需要一个 {}
:
logger.error("错误消息:{}", e.getMessage(), e); // 注意,这里只需要有一个 {}
# 1.3 附:设置 slf4j-simple 的日志级别
slf4j-simple 日志的默认输出级别是 info,这就是为什么,没有 debug 日志输出的原因。
slf4j-simple 没有配置文件,对日志输出级别的设置是通过 VM 的启动设置来定制的。
无论是 Idea 还是 Eclipse,找到 Run/debug Configuration
配置,为其中的 VM options
添加一项:
-Dorg.slf4j.simpleLogger.defaultLogLevel=debug
如果,有其它的配置项,它们之间用空格分隔。
# 2. logback
<logback.version>1.2.3</logback.version>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
整个 Java 日志库领域绕不开的人物是 Ceki Gülcü,大半个日志库领域都与他有关。
# 2.1 日志库及其发展历史
Java 1.4 之前 JDK 中并没有日志相关功能。Apache 基金会的 Log4j 是整个 Java 世界的唯一选择。Ceki Gülcü 是其作者。
2002 发布的 Java 1.4 自带了自己的日志库:jdk-logging,也称为
J.U.L
Log4j 和 jdk-logging 两种日志库选择,导致了日志使用的混乱。所以 Apache 推出了commons-logging 。它只是定义了一套日志接口(也是第一个日志接口),支持运行时动态加载日志组件。它的出现解决了多种日志框架共存的尴尬,也是面向接口编程思想的一种具体体现。
2006 年,Ceki Gülcü(Log4j 的作者)觉得 commons-logging 这套接口设计的不好,容易让开发者写出有性能问题的代码。他离开 Apache 后,又搞出来一套类似 commons-logging 的接口类的日志库:Slf4j 。
在搞出来 Slf4j 之后,Ceki Gülcü 又顺带开发了 Logback,做为 Slf4j 的默认实现。在功能完整度和性能上,Logback 超越了当时所有已有的日志实现框架。
鉴于 logback 的更优秀的设计思路,Apache 基金会重写了 Log4j 库,推出其 2.0 版本。习惯性称为 Log4j2 。
从日志库的发展历史来看,众多的日志相关 Jar 包进行分类,主要分为三类:
接口类:只提供 API 定义,没有提供具体实现。目的是为应用层提供标准化的使用方式。既所谓的面向接口编程。
commons-logging(也称
J.C.L
,java-commons-logging)SLF4J
实现类:具体的日志实现类,提供对日志的收集/管理功能。受不同的需求、不同的历史环境影响,各框架功能上有许多不同。但遵循进化论规律。
Log4j
jdk-logging(也称,
J.U.L
,java-util-logging)Logback
Log4j2
桥接类:多种日志实现框架混用情况下,需要借助桥接类进行日志的转换,最后统一成一种进行输出。
slf4j-jdk14
slf4j-log4j12
log4j-slf4j-impl
logback-classic
slf4j-jcl
jul-to-slf4j
log4j-over-slf4j
icl-over-slf4j
log4j-to-slf4j
# 2.2 Logback
pom.xml
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 由于 logback-classic 依赖于 slf4j-api 和 logback-core,因此 slf4j-api 和 logback-core 的 dependency 可以省略掉 -->
使用 logback 需要在 classpath 下提供一个 logback.xml 配置文件。
该文件最精简的基本格式如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%5p | %-40.40logger{39} : %m%n</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<logger name="包名" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
</logger>
<root level="WARN">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
# Logback 的基本配置说明
<configuration debug="false">
logback 日志库本身也能输出关于它自身的日志信息。debug="false"
表示关闭 logback 自身的 debug 日志信息的输出。
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>...</pattern>
<charset>utf8</charset>
</encoder>
</appender>
这一段 appender
的配置表示这一个日志输出『目的地』为终端控制台的配置。name="xxx"
命名任意,可自定义。class="ch.qos.xxx..."
是『固定』写法。logback 就是靠这里配置的这个类,将日志输出到终端控制台。
<pattern>...</pattern>
中写入的是日志格式配置。例如:
<pattern>%d{yyyy-MM-dd HH:mm:ss} %5p ---- %-40.40logger{39} : %m%n</pattern>
logback 采用类似 C 语言中的 printf 函数的打印格式格式化日志信息。
基本的打印参数有:
占位符 | 说明 |
---|---|
%d 或 %date | 日志生产时间 |
%d{yyyy-MM-dd HH:mm:ss} 2012-11-02 14:34:02 | |
%d{yyyy-MM-dd HH:mm:ss,SSS} 2012-11-02 14:34:02,123 | |
%t 或 %thread | 输出当前线程名称 |
%p 或 %level | 输出日志级别,-5 表示左对齐并且固定输出 5 个字符,如果不足在右边补空格 |
%logger | 输出 logger 的名称。这个名称就是创建 Logger 对象时所传入的字符串,通常就是类的完全限定名 |
%logger{15} 完全限定名超过 15 个字符,开始将包名缩写成单个字母。最右边的部分永远不会被简写 | |
%20.20logger 至少占 20 个字符(多的补空格),右对齐。超过 20 个字符开始从左侧缩写 | |
%-20.20logger 至少占 20 个字符(多的补空格),左对齐。超过 20 个字符开始从左侧缩写 | |
%10.-10logger 至少占 10 个字符(多的补空格),左对齐。超过 20 个字符开始从右侧缩写 | |
%m 或 %msg | 输出日志的内容 |
%n | 换行符 |
这里是一段『配置』,后续会引用这一段配置。
<root level="WARN">
<appender-ref ref="CONSOLE" />
</root>
这一段是在设置日志级别及其日志输出。除了 <logger>
元素『额外』指定的包之外,其它的包都遵守此处的配置。相等于就是『默认』的日志输出。
level="xxx"
表示日志输出级别,志信息的优先级从高到低有 ERROR、WARN、INFO、DEBUG,分别用来指定这条日志信息的重要程度。
<appender-ref ref="CONSOLE" />
表示引用一个名为 CONSOLE
的 <appendder>
配置。这个配置决定了日志信息以什么样的格式输出,输出到哪里。
<logger name="包名" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
</logger>
<logger>
表示一个明确的单独的日志设置。用于专门指定某个包中的日志的输出。如果一个包及符合 <logger>
的设置,又因为它要准守默认的 <root>
的设置,所以使用 additivity="false"
表示它只用遵守 <logger>
的设置,<root>
的设置不起作用。
# 2.3 一个更复杂的样例
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<property name="LOG_PATH" value="${catalina.base}/logs/webapps"/>
<property name="LOG_FILE" value="${LOG_PATH}/spring.log"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %5p | %-40.40logger{39} : %m%n</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %5p | [%15.15t] %-40.40logger{39} : %m%n</pattern>
<charset>utf8</charset>
</encoder>
<file>${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>0</maxHistory>
</rollingPolicy>
</appender>
<!-- 指定类与<looger>的关联关系 -->
<logger name="com.oracle" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE" />
</logger>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</configuration>