一、日志概述
1. 为什么要学习日志
日志对我们来说并不陌生,通过打印日志来发现和定位问题,或者根据日志来分析程序的运行过程。在Spring的学习中, 也经常根据控制台的日志来分析和定位问题,随着项目的复杂度提升, 我们对日志的打印也有了更高的需求, 而不仅仅是定位排查问题
2. 日志的用途
- 系统监控
监控现在几乎是一个成熟系统的标配,我们可以通过日志记录这个系统的运行状态,每一个方法的响应时间,响应状态等,对数据进行分析,设置不同的规则,超过阈值时进行报警。比如统计日志中关键字的数量,并在关键字数量达到一定条件时报警,这也是日志的常见需求之一 - 数据采集
数据采集是一个比较大的范围,采集的数据可以作用在很多方面,比如数据统计,推荐排序等。
数据统计: 统计页面的浏览量(PV),访客量(UV),点击量等,根据这些数据进行数据分析, 优化公司运营策略
推荐排序: 目前推荐排序应用在各个领域,我们经常接触的各行各业很多也都涉及推荐排序,比如购物,广告,新闻等领域。数据采集是推荐排序工作中必须做的一环, 系统通过日志记录用户的浏览历史,停留时长等, 算法人员通过分析这些数据,训练模型,给用户做推荐 - 日志审计
随着互联网的发展,众多企业的关键业务越来越多的运行于网络之上。网络安全越来越受到大家的关注,系统安全也成为了项目中的一个重要环节,安全审计也是系统中非常重要的部分。国家的政策法规、行业标准等都明确对日志审计提出了要求。通过系统日志分析,可以判断一些非法攻击, 非法调用,以及系统处理过程中的安全隐患
二、日志使用
1. 打印日志
打印日志的步骤:
在程序中得到日志对象
使用日志对象输出要打印的内容
1. 在程序中得到日志对象
在程序中获取日志对象需要使用日志工厂 LoggerFactory
java
private static final Logger logger = LoggerFactory.getLogger(LoggerController.class);
LoggerFactory.getLogger 需要传递一个参数,标识这个日志的名称。这样可以更清晰的知道是哪个类输出的日志。当有问题时,可以更方便直观的定位到问题类
注意:Logger 对象是属于 org.slf4j 包下的, 不要导入错包

2. 使用日志对象打印日志
java
@RestController
@RequestMapping("/logger")
public class LoggerController {
private static final Logger logger = LoggerFactory.getLogger(LoggerController.class);
@RequestMapping("/print")
public String print(){
System.out.println("打印日志");
logger.info("打印日志");
return "日志打印成功";
}
}


2. 日志框架
SLF4J不同于其他日志框架,它不是一个真正的日志实现,而是一个抽象层,对日志框架制定的一种规范,标准,接口。所有SLF4J并不能独立使用,需要和具体的日志框架配合使用
1. 门面模式
门面模式,也叫外观模式,是一种结构型设计模式
它的核心思想非常简单:通过为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口使得这一子系统更加容易使用
简单来说,门面模式就像是为一个复杂的系统安装了一个总开关 或统一柜台
场景: 回家, 我们会开各个屋的灯。离开家时,会关闭各个屋的灯
如果家里设置一个总开关,来控制整个屋的灯就会很方便
我们使用门面模式的实现
java
public interface Light {
void on();
void off();
}
java
public class BathroomLight implements Light{
@Override
public void on() {
System.out.println("打开洗手间灯");
}
@Override
public void off() {
System.out.println("关闭洗手间灯");
}
}
public class LivingLight implements Light{
@Override
public void on() {
System.out.println("打开客厅灯");
}
@Override
public void off() {
System.out.println("关闭客厅灯");
}
}
public class BedroomLight implements Light{
@Override
public void on() {
System.out.println("打开卧室灯");
}
@Override
public void off() {
System.out.println("关闭卧室灯");
}
}
java
//灯的门面
public class FacadeLight {
BathroomLight bathroomLight = new BathroomLight();
BedroomLight bedroomLight = new BedroomLight();
LivingLight livingLight = new LivingLight();
void lightOn(){
bathroomLight.on();
bedroomLight.on();
livingLight.on();
}
void lightOff(){
bathroomLight.off();
bedroomLight.off();
livingLight.off();
}
}
java
public class Main {
public static void main(String[] args) {
FacadeLight light = new FacadeLight();
light.lightOff();
}
}

门面模式优点 :
降低复杂度: 客户端不需要了解子系统内部繁琐的细节
松耦合: 客户端只与门面类耦合,子系统的变化不会直接影响到客户端
更好的分层: 门面定义了访问系统的入口点
2. SLF4J 框架
在 SLF4J 出现之前,Java 社区存在多种日志框架(如 Log4j, JUL, Tinylog 等)。如果你开发一个开源库,你很难决定使用哪种日志:
如果你选了 Log4j,但使用你库的用户在项目里用的是 Logback,就会导致项目依赖混乱
SLF4J 解决了这个问题: 它提供了一套统一的接口。作为开发者,你只需要针对 SLF4J 的接口写日志,而底层的具体实现可以在部署时动态更换
动态绑定机制
虽然代码里写的是 LoggerFactory.getLogger(),但在运行时,SLF4J 会去类路径(Classpath)下寻找 StaticLoggerBinder
如果找到了 Logback 的包,日志就由 Logback 输出
如果找到了 Log4j 的桥接包,日志就交给 Log4j 输出。 这种设计体现了门面模式的解耦:客户端代码(你的业务逻辑)与具体实现逻辑(日志怎么落盘)彻底分离
总的来说,SLF4J使你的代码独立于任意一个特定的日志API,这是一个对于开发API的开发者很好的思想
3. 日志格式

从上图可以看到,日志输出内容元素具体如下:
- 时间日期:精确到毫秒
- 日志级别:ERROR, WARN, INFO, DEBUG 或TRACE
- 进程ID
- 线程名
- Logger名(通常使用源代码的类名)
- 日志内容
4. 日志级别
1. 日志级别的分类
日志的级别从高到低依次为: FATAL、ERROR、WARN、INFO、DEBUG、TRACE
• FATAL: 致命信息,表示需要立即被处理的系统级错误
• ERROR: 错误信息, 级别较高的错误日志信息, 但仍然不影响系统的继续运行
• WARN: 警告信息, 不影响使用, 但需要注意的问题
• INFO: 普通信息, 用于记录应用程序正常运行时的一些信息, 例如系统启动完成、请求处理完成等
• DEBUG: 调试信息, 需要调试时候的关键信息打印
• TRACE: 追踪信息, 比DEBUG更细粒度的信息事件(除非有特殊用意,否则请使用DEBUG级别替代)
2. 日志级别的使用
日志级别是开发人员自己设置的,开发人员根据自己的理解来判断该信息的重要程度
针对这些级别,Logger 对象分别提供了对应的方法,来输出日志
java
@RestController
@RequestMapping("/logger")
public class LoggerController {
private static final Logger logger = LoggerFactory.getLogger(LoggerController.class);
@RequestMapping("/print")
public String print(){
System.out.println("打印日志");
logger.error("打印 error 日志");
logger.warn("打印 warn 日志");
logger.info("打印 info 日志");
logger.debug("打印 debug 日志");
logger.trace("打印 trace 日志");
return "日志打印成功";
}
}
SpringBoot 默认的日志框架是Logback,Logback没有 FATAL 级别,它被映射到 ERROR

结果发现, 只打印了info,warn和error级别的日志
这与日志级别的配置有关,日志的输出级别默认是 info级别,所以只会打印大于等于此级别的日志,也就是info,warn和error
5. 日志配置
1. 配置日志级别
日志级别配置只需要在配置文件中设置logging.level 配置项即可


2. 日志持久化
以上的日志都是输出在控制台上的,然而在线上环境中,我们需要把日志保存下来,以便出现问题之后追溯问题,把日志保存下来就叫持久化
日志持久化有两种方式
- 配置日志文件名
- 配置日志的存储目录
配置日志文件的路径和文件名


配置日志文件的保存路径


注意 :
logging.file.name 和 logging.file.path 两个都配置的情况下,只生效其一,以logging.file.name 为准
3. 配置日志文件分割
如果我们的日志都放在一个文件中, 随着项目的运行,日志文件会越来越大,需要对日志文件进行分割



4. 配置日志格式
打印日志的格式,也是支持配置的,支持控制台和日志文件分别设置

设置了颜色, 却没有生效?
需要配置,让idea支持控制台颜色显示
- 打开启动配置, 添加VM options

- 添加VM options -Dspring.output.ansi.enabled=ALWAYS

- 重新启动程序, 就发现控制台支持颜色了



三、更简单的日志输出
每次都使用 LoggerFactory.getLogger(xxx.class) 很繁琐,且每个类都添加一遍,lombok给我们提供了一种更简单的方式
- 添加 lombok 框架支持
- 使用 @slf4j 注解输出日志
1. 添加 lombok 依赖
xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
2. 输出日志
java
@Slf4j
@RestController
@RequestMapping("/logger2")
public class LoggerController2 {
@RequestMapping("/print")
public String print(){
System.out.println("打印日志");
// logger.info("打印日志");
log.error("打印 error 日志");
log.warn("打印 warn 日志");
log.info("打印 info 日志");
log.debug("打印 debug 日志");
log.trace("打印 trace 日志");
return "日志打印成功";
}
}
@Slf4j等同于提供了
private static final Logger log = LoggerFactory.getLogger(LoggerController.class)
四、总结
- 日志是程序中的重要组成部分,使用日志可以快速的发现和定位问题,Spring Boot 内容了日志框架,默认情况下使用的是 info 日志级别将日志输出到控制台的,我们可以通过 lombok 提供的@Slf4j 注解和 log 对象快速的打印自定义日志
- 日志包含6个级别,日志级别越高,收到的日志信息也就越少,我们可以通过配置日志的保存名称或保存目录来将日志持久化
本期内容到此为止,喜欢的话请点个赞,谢谢观看!!!