找往期文章包括但不限于本期文章中不懂的知识点:
个人主页:我要学编程程(ಥ_ಥ)-CSDN博客
所属专栏:JavaEE
目录
日志简介
日志是一种记录事件、操作或信息的记录方式。我们在JavaSE阶段学习的异常也算是一种日志的记录,如果程序某个地方出现了啥问题,就可以通过try-catch结构抛出异常信息,这样就能快速定位问题,进一步分析如何解决该问题。又或者执行到某个具体操作时,使用 System.out.println 来打印信息到控制台,这样就能明确知道程序是在执行啥操作,或者该用户是在执行啥操作。但仅限于上述方式是很不现实的,不能很好的满足我们的需求,因此就出现了日志框架。
日志使用
SpringBoot项目在启动时,默认就会输出日志信息到控制台,如下图所示:
上述图中的日志,使用 System.out.println 并不能实现上述效果,而是需要特定的日志框架去实现,这里我们采用的 slf4j,下面就使用该框架来打印类似的日志:
@RestController
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/print")
public void printLog() {
// 通过slf4j包内的Logger类的工厂方法获取logger对象
Logger logger = LoggerFactory.getLogger(HelloController.class);
// 通过logger对象将日志输出到控制台上
logger.info("使用slf4j日志框架,将日志输出到控制台上");
}
}
注意:
1、我们在导包的时候,一定要注意别导错了,得导入 slf4j 包:
2、LoggerFactory.getLogger 需要传递一个参数,标识这个日志的名称。这样可以更清晰的知道是哪个类输出的日志。当有问题时,可以更方便直观的定位到问题类。
当传入Class 对象时,内部自动调用Class.getName方法获取类全限定名作为name,便于在日志中直接显示类;当直接传入字符串时,name 将作为自定义日志标识。待会学习日志格式的时候,再来具体观察效果。
日志框架
Java 日志系统通常由日志门面(日志接口)和日志框架(具体实现)组成。日志门面可以理解为一个接口,日志框架可以理解为是对接口的一个具体实现。上述两者之间的关系,就可以理解为Java中的接口类和具体实现类之间的关系。
slf4j 是一个日志门面,并不是一个具体的日志实现。
门面模式(外观模式)
门面模式,也被称为外观模式,提供了一个统一接口,用来访问子系统中的一群接口,其主要特征是定义了一个高层接口,让子系统更容易被使用。
举个简单的例子,以前计算机最开始问世的时候,如果想要实现某个功能的话,还得在纸带上书写机器语言,然后给计算机读取,计算机再根据读取到的指令才知道接下来要干啥。但是现在不一样了,我们只需要通过图形化界面来操作具体的步骤,计算机就能知道接下来要干嘛。
在上述过程中,图形化界面就相当于是一个门面模式的具体实现,所有的计算机操作,都通过图形化界面操作来实现。
在门面模式中,主要包含两种角色:外观角色(门面角色),是系统对外的统一接口;子系统角色,可以同时有一个或者多个子系统角色的存在。对于子系统角色而言,它本身是不知道有外观角色的存在的,就像上述计算操作,无论是通过机器语言来实现该操作,还是通过图形化界面来操作,本身对于计算这个操作来说,两者都是使用方,即客户端。
接下来,我们就来编写一个具体的门面模式的代码:
interface Graphic {
// 计算
void calc();
// 刷视频
void watchVideo();
// 打游戏
void playGame();
}
class Option implements Graphic {
@Override
public void calc() {
try {
System.out.println("开始计算");
System.out.println("正在计算...");
Thread.sleep(5000); // 休眠五秒钟
System.out.println("计算完毕");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
@Override
public void watchVideo() {
try {
System.out.println("开始刷视频");
System.out.println("正在刷视频...");
Thread.sleep(5000);
System.out.println("视频刷完了");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
@Override
public void playGame() {
try {
System.out.println("开始玩游戏");
System.out.println("正在玩游戏...");
Thread.sleep(5000);
System.out.println("游戏结束");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
class User {
private Graphic graphic = new Option();
public User() {
System.out.println("用户张三买了一台电脑,并已经登录开始使用...");
}
public void runComputer() {
graphic.calc();
graphic.playGame();
graphic.watchVideo();
}
public static void main(String[] args) {
User user = new User();
user.runComputer();
}
}
门面模式的优点:
1、统一接口封装:通过为子系统提供统一的高层次入口,将复杂的内部调用逻辑隐藏,客户端只需要与门面对象交互;
2、减少客户端依赖对象数量:客户端只需关注门面对象,而非子系统;
3、实现了解耦:门面模式隔离了客户端与子系统的直接依赖,子系统的内部修改不会影响客户端代码,增强系统的可维护性和扩展;
4、访问控制与安全性增强:门面对象可限制客户端对子系统的访问权限,仅暴露必要的接口,防止非法的操作。
日志格式的介绍
接下来,我们详细学习日志级别。
日志级别的介绍
日志级别代表着日志信息对应问题的严重新,为了更快地筛选符合目标的日志信息。
如果没有日志级别会怎么样呢?假设今天是放假,正常情况下没有Error级别以上的日志信息,就不需要我们来处理或者说可以在工作日内处理,但如果不存在日志级别,老板发现这里打印了许多日志,误以为是代码哪里出现了问题,赶紧把你从休假的状态叫回来,结果你发现这只是一个小小的警告信息,想一想这时你的心里状态是咋样的,肯定是双眼冒火的看着老板。又或者你是老板在处理事情时,下面的员工不管是重要的事情,还是屁大点事都来通知你,让你去做决定,你肯定忙不停。因此日志级别对于我们程序员来说是非常重要的。
日志级别从高到低依次是:FATAL、ERROR、WARN、INFO、DEBUG、TRACE。
FATAL:致命信息,表示需要立即被处理的系统级错误。
ERROR:错误信息,级别较高的错误日志信息,但仍然不影响系统的继续运行。
WARN:警告信息,不影响使用,但需要注意的问题
INFO:普通信息,用于记录应用程序正常运行时的一些信息,例如系统启动完成、请求处理完成等。
DEBUG:调试信息,需要调试时候的关键信息打印.
TRACE:追踪信息,比DEBUG更细粒度的信息事件(除非有特殊用意,否则请使用DEBUG级别替代)
日志级别的使用
针对不同的日志级别,logger对象都提供了打印方法:
@RestController
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/printByRank")
public void printLogByRank() {
// 传入的logger对象的名字
// Logger logger = LoggerFactory.getLogger("logger");
Logger logger = LoggerFactory.getLogger(HelloController.class);
logger.error("这是ERROR级别的日志");
logger.warn("这是WARN级别的日志");
logger.info("这是INFO级别的日志");
logger.debug("这是DEBUG级别的日志");
logger.trace("这是TRACE级别的日志");
}
}
但从上述图中控制台的输出结果来看,只打印了ERROR、WARN、INFO级别的日志,而对于DEBUG 和 TRACE 级别的日志并未打印,这是因为SpringBoot项目的默认的日志级别配置是输出INFO级别以上的日志,对于级别更低的日志并不会输出。
注意:SpringBoot项目默认的日志框架是logback,而logback中并没有FATAL级别。
配置日志级别
# 配置日志级别
logging:
level:
root: trace
在配置文件中,加上上述配置之后,再来启动程序,观察控制台输出的日志:
日志持久化
上述的所有日志都是输出到控制台上的,但是在实际生产环境中,我们需要将日志保存到本地,以便于后续排查与追溯问题。日志持久化的方式有两种:1、配置日志文件名;2、配置日志的存储目录。同样,日志持久化也是需要修改配置文件的。
第一种方式就是指定日志文件存放的目录,以及对应的文件名
# 配置日志持久化
logging:
file:
name: logger.log # 注意文件的后缀名
当我们再次运行程序时,就会在当前项目的路径生成一个文本文件
除了自动生成到当前项目路径下之外,还可以手动指定创建到哪个磁盘路径下。
我们再来看第二种方式,其是指定日志文件存储的路径,不包括具体的文件名。
# 配置日志持久化
logging:
file:
path: D:\logger\ # 只需要写上路径即可
# 生成的日志文件名,默认是 spring.log
注意:
1、虽然在路径的最后加不加 "\" 的效果都是一样的,但是加上之后,可读性更好。
2、当name和path同时存在时,以name为主。
3、使用name配置日志持久化时,如果在最后不加上文件名的话,配置就不会生效。
日志文件分割
如果日志都放在一个文件中,随着项目的运行,日志文件会越来越大,这时就需要对日志文件进行分割。
logging:
# 配置日志持久化
file:
path: logger/111/
name: logger/222/logger.log
logback:
rollingpolicy:
max-file-size: 10KB # 按照10KB大小进行分割
file-name-pattern: ${LOG_FILE}.%d{yyyy-MM-dd}.%i # 子文件的格式
我们可以从上述图中文件的大小来看,其并不是按照文件大小进行严格划分的,当该行的文件已经超过10KB的话,会将该行的所有日志全部存储完之后,再去新开一个文件来存储,而不是说一满10KB的话,就新开一个文件来存储。
注意:默认当日志文件的大小超过10M时,就会自动进行分割。
更加简单的日志输出
上述所有的日志输出都需要通过日志工厂来获取对应的方法,如果有多个类需要输出日志的话,那么就需要将上述代码重复写,那有没有注解的方式呢?肯定是有的,通过Lombok依赖即可添加对应的注解来输出日志。
@Slf4j // 提供了一个logger对象,直接使用即可
@RestController
@RequestMapping("/hello3")
public class HelloController3 {
@RequestMapping("/print")
public void printLog() {
log.info("1111");
}
}
好啦!本期 初始JavaEE篇 —— SpringBoot 日志 的学习之旅 就到此结束啦!我们下一期再一起学习吧!