---
title: javalib-log
date: 2022-02-17 22:34:30
order: 01
categories:
- Java
- 工具
- 其他
tags:
- Java
- 日志
permalink: /pages/02c18ea3/
---
# 细说 Java 主流日志工具库
> 在项目开发中,为了跟踪代码的运行情况,常常要使用日志来记录信息。
>
> 在 Java 世界,有很多的日志工具库来实现日志功能,避免了我们重复造轮子。
>
> 我们先来逐一了解一下主流日志工具。
## 日志框架
### java.util.logging (JUL)
JDK1.4 开始,通过 `java.util.logging` 提供日志功能。
它能满足基本的日志需要,但是功能没有 Log4j 强大,而且使用范围也没有 Log4j 广泛。
### Log4j
Log4j 是 apache 的一个开源项目,创始人 Ceki Gulcu。
Log4j 应该说是 Java 领域资格最老,应用最广的日志工具。从诞生之日到现在一直广受业界欢迎。
Log4j 是高度可配置的,并可通过在运行时的外部文件配置。它根据记录的优先级别,并提供机制,以指示记录信息到许多的目的地,诸如:数据库,文件,控制台,UNIX 系统日志等。
Log4j 中有三个主要组成部分:
- **loggers** - 负责捕获记录信息。
- **appenders** - 负责发布日志信息,以不同的首选目的地。
- **layouts** - 负责格式化不同风格的日志信息。
[官网地址](http://logging.apache.org/log4j/2.x/)
### Logback
Logback 是由 log4j 创始人 Ceki Gulcu 设计的又一个开源日记组件,目标是替代 log4j。
logback 当前分成三个模块:`logback-core`、`logback-classic` 和 `logback-access`。
- `logback-core` - 是其它两个模块的基础模块。
- `logback-classic` - 是 log4j 的一个 改良版本。此外 `logback-classic` 完整实现 SLF4J API 使你可以很方便地更换成其它日记系统如 log4j 或 JDK14 Logging。
- `logback-access` - 访问模块与 Servlet 容器集成提供通过 Http 来访问日记的功能。
[官网地址](http://logback.qos.ch/)
### Log4j2
[官网地址](http://logging.apache.org/log4j/2.x/)
按照官方的说法,Log4j2 是 Log4j 和 Logback 的替代。
Log4j2 架构:

### Log4j vs Logback vs Log4j2
按照官方的说法,**Log4j2 大大优于 Log4j 和 Logback。**
那么,Log4j2 相比于先问世的 Log4j 和 Logback,它具有哪些优势呢?
1. Log4j2 旨在用作审计日志记录框架。 Log4j 1.x 和 Logback 都会在重新配置时丢失事件。 Log4j 2 不会。在 Logback 中,Appender 中的异常永远不会对应用程序可见。在 Log4j 中,可以将 Appender 配置为允许异常渗透到应用程序。
2. Log4j2 在多线程场景中,[异步 Loggers](https://logging.apache.org/log4j/2.x/manual/async.html) 的吞吐量比 Log4j 1.x 和 Logback 高 10 倍,延迟低几个数量级。
3. Log4j2 对于独立应用程序是无垃圾的,对于稳定状态日志记录期间的 Web 应用程序来说是低垃圾。这减少了垃圾收集器的压力,并且可以提供更好的响应时间性能。
4. Log4j2 使用插件系统,通过添加新的 Appender、Filter、Layout、Lookup 和 Pattern Converter,可以非常轻松地扩展框架,而无需对 Log4j 进行任何更改。
5. 由于插件系统配置更简单。配置中的条目不需要指定类名。
6. 支持[自定义日志等级](https://logging.apache.org/log4j/2.x/manual/customloglevels.html)。
7. 支持 [lambda 表达式](https://logging.apache.org/log4j/2.x/manual/api.html#LambdaSupport)。
8. 支持[消息对象](https://logging.apache.org/log4j/2.x/manual/messages.html)。
9. Log4j 和 Logback 的 Layout 返回的是字符串,而 Log4j2 返回的是二进制数组,这使得它能被各种 Appender 使用。
10. Syslog Appender 支持 TCP 和 UDP 并且支持 BSD 系统日志。
11. Log4j2 利用 Java5 并发特性,尽量小粒度的使用锁,减少锁的开销。
## 日志门面
何谓日志门面?
日志门面是对不同日志框架提供的一个 API 封装,可以在部署的时候不修改任何配置即可接入一种日志实现方案。
### common-logging
common-logging 是 apache 的一个开源项目。也称**Jakarta Commons Logging,缩写 JCL**。
common-logging 的功能是提供日志功能的 API 接口,本身并不提供日志的具体实现(当然,common-logging 内部有一个 Simple logger 的简单实现,但是功能很弱,直接忽略),而是在**运行时**动态的绑定日志实现组件来工作(如 log4j、java.util.loggin)。
[官网地址](http://commons.apache.org/proper/commons-logging/)
### slf4j
全称为 Simple Logging Facade for Java,即 java 简单日志门面。
什么,作者又是 Ceki Gulcu!这位大神写了 Log4j、Logback 和 slf4j,专注日志组件开发五百年,一直只能超越自己。
类似于 Common-Logging,slf4j 是对不同日志框架提供的一个 API 封装,可以在部署的时候不修改任何配置即可接入一种日志实现方案。但是,slf4j 在**编译时**静态绑定真正的 Log 库。使用 SLF4J 时,如果你需要使用某一种日志实现,那么你必须选择正确的 SLF4J 的 jar 包的集合(各种桥接包)。
[官网地址](http://www.slf4j.org/)

### common-logging vs slf4j
slf4j 库类似于 Apache Common-Logging。但是,他在编译时静态绑定真正的日志库。这点似乎很麻烦,其实也不过是导入桥接 jar 包而已。
slf4j 一大亮点是提供了更方便的日志记录方式:
不需要使用`logger.isDebugEnabled()`来解决日志因为字符拼接产生的性能问题。slf4j 的方式是使用`{}`作为字符串替换符,形式如下:
```java
logger.debug("id: {}, name: {} ", id, name);
```
### 总结
综上所述,使用 slf4j + Logback 可谓是目前最理想的日志解决方案了。
接下来,就是如何在项目中实施了。
## 实施日志解决方案
使用日志解决方案基本可分为三步:
1. 引入 jar 包
2. 配置
3. 使用 API
常见的各种日志解决方案的第 2 步和第 3 步基本一样,实施上的差别主要在第 1 步,也就是使用不同的库。
### 引入 jar 包
这里首选推荐使用 slf4j + logback 的组合。
如果你习惯了 common-logging,可以选择 common-logging+log4j。
强烈建议不要直接使用日志实现组件(logback、log4j、java.util.logging),理由前面也说过,就是无法灵活替换日志库。
还有一种情况:你的老项目使用了 common-logging,或是直接使用日志实现组件。如果修改老的代码,工作量太大,需要兼容处理。在下文,都将看到各种应对方法。
**_注:据我所知,当前仍没有方法可以将 slf4j 桥接到 common-logging。如果我孤陋寡闻了,请不吝赐教。_**
#### slf4j 直接绑定日志组件
**slf4j + logback**
添加依赖到 pom.xml 中即可。
_logback-classic-1.0.13.jar_ 会自动将 _slf4j-api-1.7.21.jar_ 和 _logback-core-1.0.13.jar_ 也添加到你的项目中。
```xml
ch.qos.logback
logback-classic
1.0.13
```
**slf4j + log4j**
添加依赖到 pom.xml 中即可。
_slf4j-log4j12-1.7.21.jar_ 会自动将 _slf4j-api-1.7.21.jar_ 和 _log4j-1.2.17.jar_ 也添加到你的项目中。
```xml
org.slf4j
slf4j-log4j12
1.7.21
```
**slf4j + java.util.logging**
添加依赖到 pom.xml 中即可。
_slf4j-jdk14-1.7.21.jar_ 会自动将 _slf4j-api-1.7.21.jar_ 也添加到你的项目中。
```xml
org.slf4j
slf4j-jdk14
1.7.21
```
#### slf4j 兼容非 slf4j 日志组件
在介绍解决方案前,先提一个概念——桥接
**什么是桥接呢**
假如你正在开发应用程序所调用的组件当中已经使用了 common-logging,这时你需要 jcl-over-slf4j.jar 把日志信息输出重定向到 slf4j-api,slf4j-api 再去调用 slf4j 实际依赖的日志组件。这个过程称为桥接。下图是官方的 slf4j 桥接策略图:

从图中应该可以看出,无论你的老项目中使用的是 common-logging 或是直接使用 log4j、java.util.logging,都可以使用对应的桥接 jar 包来解决兼容问题。
**slf4j 兼容 common-logging**
```xml
org.slf4j
jcl-over-slf4j
1.7.12
```
**slf4j 兼容 log4j**
```xml
org.slf4j
log4j-over-slf4j
1.7.12
```
**slf4j 兼容 java.util.logging**
```xml
org.slf4j
jul-to-slf4j
1.7.12
```
#### spring 集成 slf4j
做 java web 开发,基本离不开 spring 框架。很遗憾,spring 使用的日志解决方案是 common-logging + log4j。
所以,你需要一个桥接 jar 包:_logback-ext-spring_。
```xml
ch.qos.logback
logback-classic
1.1.3
org.logback-extensions
logback-ext-spring
0.1.2
org.slf4j
jcl-over-slf4j
1.7.12
```
#### common-logging 绑定日志组件
**common-logging + log4j**
添加依赖到 pom.xml 中即可。
```xml
commons-logging
commons-logging
1.2
log4j
log4j
1.2.17
```
### 使用 API
#### slf4j 用法
使用 slf4j 的 API 很简单。使用`LoggerFactory`初始化一个`Logger`实例,然后调用 Logger 对应的打印等级函数就行了。
```java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class App {
private static final Logger log = LoggerFactory.getLogger(App.class);
public static void main(String[] args) {
String msg = "print log, current level: {}";
log.trace(msg, "trace");
log.debug(msg, "debug");
log.info(msg, "info");
log.warn(msg, "warn");
log.error(msg, "error");
}
}
```
#### common-logging 用法
common-logging 用法和 slf4j 几乎一样,但是支持的打印等级多了一个更高级别的:**fatal**。
此外,common-logging 不支持`{}`替换参数,你只能选择拼接字符串这种方式了。
```java
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class JclTest {
private static final Log log = LogFactory.getLog(JclTest.class);
public static void main(String[] args) {
String msg = "print log, current level: ";
log.trace(msg + "trace");
log.debug(msg + "debug");
log.info(msg + "info");
log.warn(msg + "warn");
log.error(msg + "error");
log.fatal(msg + "fatal");
}
}
```
## log4j2 配置
log4j2 基本配置形式如下:
```xml
;
value
...
...
```
配置示例:
```xml
target/test.log
%d %p %C{1.} [%t] %m%n
```
## logback 配置
### ``
- 作用:`` 是 logback 配置文件的根元素。
- 要点
- 它有 ``、``、`` 三个子元素。

### ``
- 作用:将记录日志的任务委托给名为 appender 的组件。
- 要点
- 可以配置零个或多个。
- 它有 ``、``、``、`` 四个子元素。
- 属性
- name:设置 appender 名称。
- class:设置具体的实例化类。
#### ``
- 作用:设置日志文件路径。
#### ``
- 作用:设置过滤器。
- 要点
- 可以配置零个或多个。
#### ``
- 作用:设置 appender。
- 要点
- 可以配置零个或一个。
- 属性
- class:设置具体的实例化类。
#### ``
- 作用:设置编码。
- 要点
- 可以配置零个或多个。
- 属性
- class:设置具体的实例化类。

### ``
- 作用:设置 logger。
- 要点
- 可以配置零个或多个。
- 属性
- name
- level:设置日志级别。不区分大小写。可选值:TRACE、DEBUG、INFO、WARN、ERROR、ALL、OFF。
- additivity:可选值:true 或 false。
#### ``
- 作用:appender 引用。
- 要点
- 可以配置零个或多个。
### ``
- 作用:设置根 logger。
- 要点
- 只能配置一个。
- 除了 level,不支持任何属性。level 属性和 `` 中的相同。
- 有一个子元素 ``,与 `` 中的相同。
### 完整的 logback.xml 参考示例
在下面的配置文件中,我为自己的项目代码(根目录:org.zp.notes.spring)设置了五种等级:
TRACE、DEBUG、INFO、WARN、ERROR,优先级依次从低到高。
因为关注 spring 框架本身的一些信息,我增加了专门打印 spring WARN 及以上等级的日志。
```xml
%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n
${user.dir}/logs/${DIR_NAME}/all.%d{yyyy-MM-dd}.log
30
30MB
%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n
${user.dir}/logs/${DIR_NAME}/error.%d{yyyy-MM-dd}.log
30
10MB
ERROR
ACCEPT
DENY
%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n
${user.dir}/logs/${DIR_NAME}/warn.%d{yyyy-MM-dd}.log
30
10MB
WARN
ACCEPT
DENY
%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n
${user.dir}/logs/${DIR_NAME}/info.%d{yyyy-MM-dd}.log
30
10MB
INFO
ACCEPT
DENY
%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n
${user.dir}/logs/${DIR_NAME}/debug.%d{yyyy-MM-dd}.log
30
10MB
DEBUG
ACCEPT
DENY
%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n
${user.dir}/logs/${DIR_NAME}/trace.%d{yyyy-MM-dd}.log
30
10MB
TRACE
ACCEPT
DENY
%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n
${user.dir}/logs/${DIR_NAME}/springframework.%d{yyyy-MM-dd}.log
30
10MB
%d{HH:mm:ss.SSS} [%thread] [%-5p] %c{36}.%M - %m%n
```
## log4j 配置
### 完整的 log4j.xml 参考示例
log4j 的配置文件一般有 xml 格式或 properties 格式。这里为了和 logback.xml 做个对比,就不介绍 properties 了,其实也没太大差别。
```xml
```
## 参考
- [slf4 官方文档](http://www.slf4j.org/manual.html)
- [logback 官方文档](http://logback.qos.ch/)
- [log4j 官方文档](http://logging.apache.org/log4j/1.2/)
- [commons-logging 官方文档](http://commons.apache.org/proper/commons-logging/)
-