文章目录
-
- [📕1. ⽇志概述](#📕1. ⽇志概述)
- [📕2. 日志使用](#📕2. 日志使用)
-
-
- [✏️2.1 打印日志](#✏️2.1 打印日志)
-
-
- [🔖2.1.1 在程序中得到⽇志对象](#🔖2.1.1 在程序中得到⽇志对象)
- [🔖2.1.2 使用日志对象打印日志](#🔖2.1.2 使用日志对象打印日志)
-
- [✏️2.2 日志框架介绍(了解)](#✏️2.2 日志框架介绍(了解))
-
-
- [🔖2.2.1 门面模式(外观模式)](#🔖2.2.1 门面模式(外观模式))
- [🔖2.2.2 SLF4J 框架介绍](#🔖2.2.2 SLF4J 框架介绍)
-
- [✏️2.3 日志格式的说明](#✏️2.3 日志格式的说明)
- [✏️2.4 日志级别](#✏️2.4 日志级别)
-
-
- [🔖2.4.1 日志级别的分类](#🔖2.4.1 日志级别的分类)
- [🔖2.4.2 日志级别的使用](#🔖2.4.2 日志级别的使用)
-
- [✏️2.5 日志配置](#✏️2.5 日志配置)
-
-
- [🔖2.5.1 配置日志级别](#🔖2.5.1 配置日志级别)
- [🔖2.5.2 日志持久化](#🔖2.5.2 日志持久化)
- [🔖2.5.3 配置日志文件分割](#🔖2.5.3 配置日志文件分割)
- [🔖2.5.4 配置日志格式](#🔖2.5.4 配置日志格式)
-
-
- [📕3. 更简单的日志输出](#📕3. 更简单的日志输出)
-
-
- [✏️3.1 添加 lombok 依赖](#✏️3.1 添加 lombok 依赖)
- [✏️3.2 输出日志](#✏️3.2 输出日志)
-
📕1. ⽇志概述
日志对我们来说并不陌生,从JavaSE部分,我们就在使用System.out.print 来打印日志了。通过打印日志来发现和定位问题,或者根据日志来分析程序的运行过程。在Spring的学习中,也经常根据控制台的日志来分析和定位问题。随着项目的复杂度提升,我们对日志的打印也有了更高的需求,而不仅仅是定位排查问题。比如需要记录一些用户的操作记录(一些审计公司会要求),也可能需要使用日志来记录用户的一些喜好, 把日志持久化,后续进行数据分析等。但是System.out.print 不能很好的满足我们的需求,我们就需要使用一些专门日志框架(专业的事情交给专业的人去做)。
⽇志主要是为了发现问题,分析问题,定位问题的,但除此之外,⽇志还有很多⽤途 :
- 系统监控
监控现在几乎是一个成熟系统的标配,我们可以通过日志记录这个系统的运行状态,每一个方法的响应时间,响应状态等,对数据进行分析,设置不同的规则,超过阈值时进行报警。比如统计日志中关键字的数量,并在关键字数量达到一定条件时报警,这也是日志的常见需求之一。
- 数据采集
数据采集是一个比较大的范围,采集的数据可以作用在很多方面,比如数据统计,推荐排序等。
数据统计 : 统计页面的浏览量(PV),访客量(UV),点击量等,根据这些数据进行数据分析,优化公司运营策略。
推荐排序 : 目前推荐排序应用在各个领域,我们经常接触的各行各业很多也都涉及推荐排序,比如购物,广告,新闻等领域。数据采集是推荐排序工作中必须做的一环,系统通过日志记录用户的浏览历史,停留时长等,算法人员通过分析这些数据,训练模型,给用户做推荐。(下图中的数据源,其中一部分就来自于日志记录的数据。)

- 日志审计
随着互联网的发展,众多企业的关键业务越来越多的运行于网络之上。网络安全越来越受到大家的关注,系统安全也成为了项目中的一个重要环节,安全审计也是系统中非常重要的部分。国家的政策法规、 行业标准等都明确对日志审计提出了要求。通过系统日志分析,可以判断一些非法攻击,非法调用,以及系统处理过程中的安全隐患。
比如,大家平时都在做运营系统,其中运营人员在通过界面处理一些数据的时候,如果没有清楚的日志操作记录,一条数据被删除或者修改,你是无法找到是谁操作的,但是如果你做了相应的记录,该数据被谁删除或者修改就会一目了然。
📕2. 日志使用
SpringBoot 内置了日志框架 Slf4j,我们可以直接在程序中调用 Slf4j 来输出日志。
✏️2.1 打印日志
打印日志的步骤 :
- 在程序中得到日志对象.
- 使用日志对象输出要打印的内容.
🔖2.1.1 在程序中得到⽇志对象
在程序中获取日志对象需要使用日志工厂 LoggerFactory,如下代码所示 :
java
private static Logger logger = LoggerFactory.getLogger(LoggerController.class);
LoggerFactory.getLogger 需要传递一个参数,标识这个日志的名称。这样可以更清晰的知道是哪个类输出的日志。当有问题时,可以更方便直观的定位到问题类。
注意 : Logger 对象是属于 org.slf4j 包下的,不要导入错包。
🔖2.1.2 使用日志对象打印日志
日志对象的打印方法有很多种,我们可以先使用 info() 方法来输出日志,如下代码所示 :
java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LoggerController {
private static Logger logger = LoggerFactory.getLogger(LoggerController.class);
@RequestMapping("/logger")
public String logger(){
logger.info("--------------要输出⽇志的内容----------------");
return "打印⽇志";
}
}

✏️2.2 日志框架介绍(了解)
SLF4J不同于其他日志框架,它不是一个真正的日志实现,而是一个抽象层,对日志框架制定的一种规范,标准, 接口。 所有SLF4J并不能独立使用,需要和具体的日志框架配合使用。
🔖2.2.1 门面模式(外观模式)
门面模式(Facade Pattern)又称为外观模式,提供了一个统一的接口,用来访问子系统中的一群接口。其主要特征是定义了一个高层接口,让子系统更容易使用。
原文 : Provide a unified interface to a set of interfaces in a subsystem.Facade defines a higherlevel interface that makes the subsystem easier to use.
解释 : 要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行. 门面模式提供一个高层次的接口,使得子系统更易于使用.

门面模式主要包含2种角色 :
- 外观角色(Facade) : 也称门面角色,系统对外的统一接口.。
- 子系统角色(SubSystem) : 可以同时有一个或多个 SubSystem,每个 SubSytem 都不是一个单独的类,而是一个类的集合。SubSystem 并不知道 Facade 的存在,对于 SubSystem 而言,Facade 只是另一个客户端而已(即 Facade 对 SubSystem 透明)
比如去医院看病,可能要去挂号,门诊,化验, 取药,让患者或患者家属觉得很复杂。如果有提供接待人员, 只让接待人员来处理,就很方便。
门面模式的优点:
- 减少了系统的相互依赖,实现了客户端与子系统的耦合关系,这使得子系统的变化不会影响到调用它的客户端。
- 提高了灵活性,简化了客户端对子系统的使用难度,客户端无需关心子系统的具体实现方式,而只需要和门面对象交互即可。
- 提高了安全性,可以灵活设定访问权限,不在门面对象中开通方法,就无法访问。
🔖2.2.2 SLF4J 框架介绍
SLF4J 就是其他日志框架的门面。SLF4J 可以理解为是提供日志服务的统一API接口,并不涉及到具体的日志逻辑实现。
不引入日志门面
常见的日志框架有log4J, logback等。如果一个项目已经使用了log4j,而你依赖的另一个类库,假如是 Apache Active MQ,它依赖于另外一个日志框架logback,那么你就需要把logback也加载进去。
存在问题:
-
不同⽇志框架的API接⼝和配置⽂件不同,如果多个⽇志框架共存,那么不得不维护多套配置⽂件(这个配置⽂件是指⽤⼾⾃定义的配置⽂件)。
-
如果要更换⽇志框架,应⽤程序将不得不修改代码,并且修改过程中可能会存在⼀些代码冲突。
-
如果引入的第三方框架,使用了多套配置,那就不得不维护多套配置。
引入日志门面
引入门面日志框架之后,应用程序和日志框架(框架的具体实现)之间有了统一的API接口(门面日志框架实现),此时应用程序只需要维护一套日志文件配置,且当底层实现框架改变时,也不需要更改应用程序代码。

SLF4J 就是这个日志门面,总的来说,SLF4J使你的代码独立于任意一个特定的日志API,这是一个对于开发API的开发者很好的思想。
✏️2.3 日志格式的说明
打印的日志分别代表什么呢?

从上图可以看到,日志输出内容元素具体如下 :
- 时间⽇期:精确到毫秒
- ⽇志级别:ERROR,WARN,INFO,DEBUG或TRACE
- 进程ID
- 线程名
- Logger名(通常使⽤源代码的类名)
- ⽇志内容
✏️2.4 日志级别
日志级别代表着日志信息对应问题的严重性,为了更快的筛选符合目标的日志信息。
试想一下这样的场景,假设你是一家 2 万人公司的老板,如果每个员工的日常工作和琐碎的信息都要反馈给你, 那你一定无暇顾及。于是就有了组织架构,而组织架构就会分级,有很多的级别设置。如下图所示 :
有了组织架构之后,就可以逐级别汇报消息了,例如 : 组员汇报给组长, 组长汇报给研发一组,研发一组汇报给 Java 研发,等等依次进行汇报。
日志级别大概是同样的道理,有了日志级别之后就可以过滤自己想看到的信息了,比如只关注error级别的, 就可以根据级别过滤出来error级别的日志信息,节约开发者的信息筛选时间。
🔖2.4.1 日志级别的分类
日志的级别从高到低依次为: FATAL、ERROR、WARN、INFO、DEBUG、TRACE
- FATAL:致命信息,表⽰需要⽴即被处理的系统级错误.
- ERROR:错误信息,级别较⾼的错误⽇志信息,但仍然不影响系统的继续运⾏.
- WARN:警告信息,不影响使⽤,但需要注意的问题
- INFO:普通信息,⽤于记录应⽤程序正常运⾏时的⼀些信息,例如系统启动完成、请求处理完成等.
- DEBUG:调试信息,需要调试时候的关键信息打印.
- TRACE:追踪信息,⽐DEBUG更细粒度的信息事件(除⾮有特殊⽤意,否则请使⽤DEBUG级别替代)
级别越高,收到的消息越少。
⽇志级别通常和测试⼈员的Bug级别没有关系。
⽇志级别是开发⼈员设置的,⽤来给开发⼈员看的。⽇志级别的正确设置,也与开发⼈员的⼯作经验有关。如果开发⼈员把error级别的⽇志设置成了info,就很有可能会影响开发⼈员对项⽬运⾏情况的判断。出现error级别的⽇志信息较多时,可能也没有任何问题。测试的bug级别更多是依据现象和影响范围来判断。
🔖2.4.2 日志级别的使用
日志级别是开发人员自己设置的,开发人员根据自己的理解来判断该信息的重要程度。针对这些级别,Logger 对象分别提供了对应的方法,来输出日志。(类似公司管理,通常由领导来判断什么样的事情需要汇报,什么样的事情不需要汇报。)
java
/**
* 打印不同级别的⽇志
**/
@RequestMapping("/printLog")
public String printLog() {
logger.trace("================= trace ===============");
logger.debug("================= debug ===============");
logger.info("================= info ===============");
logger.warn("================= warn ===============");
logger.error("================= error ===============");
return "打印不同级别的⽇志" ;
}
SpringBoot默认的⽇志框架是Logback,Logback没有 FATAL 级别,它被映射到 ERROR。(出现fatal日志,表示服务已经出现了某种程度的不可用,需要系统管理员紧急介入处理。通常情况下, 一个进程生命周期中应该最多只有一次FATAL记录。)

✏️2.5 日志配置
⽇志框架⽀持我们更灵活的输出⽇志,包括内容,格式等。
🔖2.5.1 配置日志级别
日志级别配置只需要在配置文件中设置"logging.level"配置项即可。
如下所示(以下两个配置,根据项目使用其中之一。) :
yml配置
yaml
logging:
level:
root: debug
Properties配置
puppet
logging.level.root=debug
🔖2.5.2 日志持久化
以上的日志都是输出在控制台上的,然而在线上环境中,我们需要把日志保存下来,以便出现问题之后追溯问题。 把日志保存下来就叫持久化。
日志持久化有两种方式:
- 配置⽇志⽂件名
- 配置⽇志的存储⽬录
配置日志文件的路径和文件名
Properties配置
puppet
logging.file.name=logger/springboot.log
yml配置
yaml
# 设置日志文件的文件名
logging:
file:
name: logger/springboot.log
配置日志文件的保存路径
Properties配置
puppet
logging.file.path=D:/temp
yml配置
yaml
# 设置日志文件的文件名
logging:
file:
path: D:/temp

注意:logging.file.name 和 logging.file.path 两个都配置的情况下, 只生效其一, 以logging.file.name 为准.
🔖2.5.3 配置日志文件分割
如果我们的日志都放在一个文件中,随着项目的运行,日志文件会越来越大,需要对日志文件进行分割。
当然, 日志框架也帮我们考虑到了这一点, 所以如果不进行配置, 就走自动配置 默认日志文件超过10M就进行分割
配置项 | 说明 | 默认值 |
---|---|---|
logging.logback.rollingpolicy.file-name-pattern | 日志分割后的文件名 格式 | ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz |
logging.logback.rollingpolicy.max-file-size | 日志文件超过这个大 小就自动分割 | 10MB |
配置日志文件分割
Properties配置
puppet
logging.logback.rollingpolicy.file-name-pattern=${LOG_FILE}.%d{yyyy-MM-dd}.%i
logging.logback.rollingpolicy.max-file-size=1KB
yml配置
yaml
# 设置日志文件的文件名
logging:
logback:
rollingpolicy:
max-file-size: 1KB
file-name-pattern: ${LOG_FILE}.%d{yyyy-MM-dd}.%i
🔖2.5.4 配置日志格式
目前日志打印的格式是默认的

配置项 | 说明 | 默认值 |
---|---|---|
ole logging.pattern.cons | 控制台日志格式 | %clr(%d{KaTeX parse error: Expected 'EOF', got '}' at position 58: ...H:mm:ss.SSSXXX}}̲){faint} %clr({LOG_LEVEL_PATTERN:- %5p}) %clr( P I D : − ) m a g e n t a {PID:- }){magenta} %clr(---){faint} %clr([%15.15t]) {faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n PID:−)magenta{LOG_EXCEPTION_CONVERSION_WORD:-%wEx} |
logging.pattern.file | 日志文件的日志格式 | %d{{LOG_DATEFORMAT_PATTERN:-yyyy-MM- dd'T'HH:mm:ss.SSSXXX}} {LOG_LEVEL_PATTERN:-%5p} P I D : − − − − [ {PID:- } --- [%t] %-40.40logger{39} : %m%n PID:−−−−[{LOG_EXCEPTION_CONVERSION_WORD:-%wEx} |

📕3. 更简单的日志输出
每次都使用 LoggerFactory.getLogger(xxx.class) 很繁琐, 且每个类都添加一遍, lombok给我们提供了一种更简单的方式.
✏️3.1 添加 lombok 依赖
xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
✏️3.2 输出日志
java
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
public class LogController {
public void log(){
log.info("--------------要输出⽇志的内容----------------");
}
}
lombok提供的@Slf4j 会帮我们提供一个日志对象 log, 我们直接使用就可以.
