一)日志的介绍:
1)日志是程序中的重要组成部分,它是在程序运行当中输出的一些提示或者是异常信息,我们可以通过日志来观察程序执行的情况,如果出现BUG就可以根据日志来排查和发现问题中的BUG,SpringBoot在进行启动的时候,就会存着着默认的日志输出,之所以有上面的输出,是因为SpringBoot存在着默认的日志框架
2)SpringBoot中内置了SLF4J和logback两个日志框架,用户层面并不是直接操作具体的日志对象而是使用SLF4J给用户提供的API来供用户进行logback操作具体的日志对象来实现日志
3)SLF4J这类日志框架是使用门面模式来实现的,SLF4J其实本质上和JDBC很像,我们都知道JDBC操作数据库,一套JDBC代码就可以操作很多数据库,可以是MYSQL,Oracle等等,这个JDBC就类似于代理一样,来进行代理操作数据库,其实程序员本身并不需要关心各种数据库的实现,只是需要关注JDBC给我们提供的API即可;
4)类似的SLF4J中也并不是真正的完成了日志框架的实现,它只是说是一个门面或者是一个代理,在SLF4J中还是会去调用logback这样的日志实现框架,此时就不需要关心日志实现的细节,这一层面是感知不到的,这就是门面模式带来的好处;
5)还有一个好处就是如果日志实现出现了漏洞,只是需要更换对应日志实现的框架即可,而SLF4J可以匹配相应的日志框架,虽然此时日志实现的框架发生了改变,但是我们所写的代码仍然不受影响,这样就可以使得系统的依赖性降低,利于系统的更新和维护;
二)日志的作用:
1)快速的排查和定位问题,直接看报错信息;
2)进行记录用户登录的信息记录业务功能日志方便分析用户是正常登录还是暴力破解用户;
假设我们在这个登录程序中没有写反暴力破解的机制,比如说用户输入密码六次之后锁定三分钟,输入10次之后锁定10分钟,假设用户在极短的时间内连续输入多次密码,我们就可以进行记录下来,可以是不是看一下日志,从而分析用户的行为,从而给你的系统提供一个更加安全稳定的工作环境了,发现用户不正常的行为,发现一天一个人登录10W次,我们查看登录日志,发现登录日志暴增,正常情况下登录日志2000条,但是有一天发现暴增10000条,我们就可以设置黑名单,发现是暴力破解;
3)记录系统的操作日志帮助恢复数据和定位责任人
误删数据之后之前记录删除日志的信息和时间,人工进行恢复,日志已经记录了操作之前的数据和操作之后的数据,非常重要的流程以及操作频率比较低的记录操作记录下来;
某个人不小心把信息删除了,想推拖责任,就可以通过日志找到操作人;
4)记录每一个方法的执行时间
方便后期的优化和分析,某一个模块方法的执行时间,最后把所有模块执行时间列出来,看看那个模块执行时间长,可以定位到类和方法,从而做出针对性性能优化,可以通过AOP拦截,我们可以针对每一个方法的执行时间进行排序,比如说前五条进行优化;
5)还有一个具体的应用:比如说教务系统有两个功能,写作业功能和查看论坛功能,咱们的写作业功能使用Java代码写的,但是咱们的查看论坛功能使用PHP写的,所以说当用户进行注册教务系统的时候,当我们给作业系统添加一条用户注册,同时也会在论坛里面同一时间添加一条相同的用户记录;这样的目的就是实现一次注册,同时在两个系统注册,相当于在程序中使用极低的成本来实现用户数据的同步
5.1)假设现在有一个新的系统注册教务系统,先给用户写作业系统添加一条记录,但是此时插入查看论坛系统失败了,系统就会一直死等,空转CPU资源,第二步注册一直过不去,这是客户端就会看到服务器繁忙,用户体验就会非常不好,(因为当我们进行注册作业教务系统的时候,会注册一条相同的记录给论坛系统);
5.2)这是该怎们进行解决呢?我们设置无论论坛是否注册成功,都会给用户返回成功,如果论坛真的注册失败了,设置超时时间,超过论坛注册超时时间,我们就记录一下日志,进行报警,等待论坛恢复正常了之后,把日志给后台的管理人员,让管理员手动把用户注册作业功能成功,但是论坛失败的用户手动插入同步到查看论坛系统,手动进行数据补偿,这样就最低成本的解决了问题;
1)SpringBoot中内置了日志框架,像是SpringBoot内置了测试框架,Jackon;
2)默认情况下,输出的日志并不是开发者进行定义和打印的,而是由系统打印的,我们是不2.1)可以直接使用System.out.println来进行打印的,但是系统正常的日志有时间点的,发生时间,日志级别,哪个类里面,关键信息都很全;
2.2)System是在不同的环境下的行为都是一致的,一直进行日志的输出的,但是有的情况下,我们是要屏蔽日志的输出的,但是正常的日志是开发环境尽可能打印多的日志,而在生产环境里面尽量打印一些比较关键的日志,还知道哪个类的那些代码,打印日志更全,比如说时间啥的,上线屏蔽掉一些没用的日志,我们可以通过日志框架设置日志级别来屏蔽一些日志的输出;
3)日志是打印在控制台上面的,没有被永久保存下来保存的,再次重启程序,上一个错误日志是没有被保存下来的;
fatal是当程序被迫终止后才会打印此日志
三)SpringBoot中日志的使用:
对于SpringBoot控制台产生的日志,各个信息如下,信息中的包名有的部分是简写,根据日志信息,程序员就可以知道日志是发生在什么时间什么地点,哪一个线程,哪一个类以及具体的日志信息
3.1)自定义日志的输出:
3.1.1)先从类中获取到日志对象
这个日志对象来自于日志框架SLF4J,假设在类中设置自定义的日志的输出,那么日志对象创建代码如下:
Logger logger= LoggerFactory.getLogger(UserController.class);
a)每一个类都对应着一个日志对象,都是可以通过日志工厂LoggerFactory来进行获取的,导包的时候要注意Logger对象是org.SLF4J包下面的,不要导错
b)getLogger()方法里面一般传入的是当前类的类型信息,这里面的参数用于定位日志的归属类,来方便日志进行输出,输出的日志信息中有了日志的定位才能更方便的,更直观的定位到日志类
3.1.2)使用日志对象提供的方法来实现自定义日志的打印:
日志对象提供的方法有很多,可以进行设定不同级别的日志输出
@Controller public class JavaController { //1.先获取到日志对象 private static Logger logger= LoggerFactory.getLogger(JavaController.class);//里面传入的参数表示我们在哪个类中打印日志; @RequestMapping("/java100") @ResponseBody//表示给浏览器返回一个接口而不是页面 public String run(String name) { //2.调用这个方法就可以成功的向控制台打印日志,使用日志框架提供的方法进行打印 logger.info("我们已经成功地执行了run方法"); return name+"是我"; } }
观察预期结果代码是写了5个日志级别的日志输出的,但是为什么在这里面只有三个结果呢,这是因为在SpringBoot项目下,默认的日志级别是info,低于info日志级别的都不会输出
3.1.3)日志级别讲解:
1)日志级别可以筛选出一些重要的信息,比如说设置日志级别是error,那么就可以看到程序的报错日志了,对于普通的调试日志和业务日志就可以忽略了,从而帮助开发者节省信息筛选的时间;
2)日志级别可以控制在不同的环境下面,一个程序是否需要打印日志,比如说开发环境我们需要很详细的信息,而生产环境下为了保证性能和安全性就可以输出更少的日志,而日志的级别就可以实现此需求,简单来说就是过滤信息,把业务不需要的日志给屏蔽掉
1)trace:轻微痕迹,他是最轻量的(级别日志)
2)debug:调试日志,需要调试的关键信息展示
3)info:普通信息,普通的打印信息
4)warn:警告,不影响使用,但是需要注意的信息
5)error:错误日志,级别较高的错误日志信息
6)fatal:致命的日志,由于代码异常导致程序退出的执行的事件,不支持程序进行打印,程序直接崩溃,是由系统直接输出的;
7)日志的级别就是为了筛选信息和过滤信息的,是不能看比他级别底的日志,比如说我现在设置日志级别是info,那么我所看到的日志的级别的信息只能是info,warn,error,fatal,不能看到info以下的级别信息,当我进行设置日志级别是warn只能看到error和fatal输出的日志信息,自定义输出的日志是非常完善和详细的,和系统日志输出的内容是相同的
日志级别:日志级别越低,打印的日志是越多的,是为了筛选符合目标的日志信息,是为了控制相同的代码在不同的生产环境有不同的行为,我们只需要在代码不变的情况下,在配置文件里面把代码直接进行修改就好了,就可以过滤掉一些不符合级别的日志了,日志缩小了,IO次数小了,程序执行效率就变高了
如果说咱们在生产环境,就可以提高日志的级别了,想要看到所有的日志级别,就要设置默认级别是trace,因为当前默认的日志输出界别是info,程序只会输出等于当前级别或者是大于当前级别的信息,所以说比info小的级别最终我们是看不到的越往下收到的消息越少,比如说设置了warn就只能收到warn,error和fatal级别的日志了
1)门面模式:真正的进行操作之前有一个代理对象,所有的类进行操作的时候,操作的肯定是代理对象而不是最终的一个类,就类似于JDBC,不管你底层连接的是MYSQL数据库还是Oracle数据库还别的数据库统统不管,最上层就是JDBC,就算框架变了,我的最上面的写法就是保持不变的,保持一致性;
2)所以说不管最后最底层的日志实现框架使用的是log4j,1/2,JUL,还是lobback,最终的最上层的写法都是固定的,获取到的日志对象打印日志的代码是永远不会发生改变的,其实最后真正打印日志不是SLF4J,SLF4J只是驱动了某一个日志实现进行打印的,可能是logback,也有可能是log 4j 1/2,假设后面的logback出现了巨大的漏洞,底层换成log 4j 1/2就可以了,用户看到的就是代理操作的对象就是slf4j,底层实现的就是底层类(logback等等)
3)门面模式又叫外观模式,提供了一个统一的接口,提供了一个统一的接口,用来访问子系统中的一群接口其主要特征是定义了一个高层接口,让子系统更容易使用,属于结构性模式
我们的SLF4J只是一个代理而已,真正决定日志打印还是日志品质以及漏洞的不是SLF4J,SLF4J只是一个中间人,而是决定最终使用的JUL还是logback,用户只是可以感知到的操作的是SLF4J;
SLF4J和lombok都是常用的日志框架,都被内置到Spring里面
他的一个过滤规则:比yml文件里面的日志默认级别低的日志就会被忽略掉,但是正常情况下日志的默认过滤级别是info
3.1.4)设置日志级别:
日志级别是可以通过配置文件来进行设置的,日志级别包含着两类,一类是全局日志级别,一类是局部日志级别,全局日志级别可以影响全局日志的输出,而局部日志级别只是会影响一个局部的日志输出,并且局部日志的级别大于全局日志级别的配置
1)设置全局日志级别:
在properties里面进行设置:logging.level.root=trace 在yml里面进行设置: logging: level: root:trace
logging.level.root=trace logging: level: root: trace
当运行上面我们的自定义日志代码以后发现,所有的自定义日志信息都会输出,并且会伴随着程序进行启动相比有更多的日志信息输出,毕竟这里面设置的是全局的配置文件,会影响全局;
2)设置局部日志级别:
最终结果是当我们在浏览器上面访问URL的时候,UserController会自动打印日志信息,况且会把trace以上的日志级别的信息全部进行输出,因为我们已经设置了UserController类里面的日志级别是trace了
c)设置全局级别和局部日志级别:
logging: level: root: info com: example: demos: UserController: warn UserService: error
root这里面设置的是全局级别,项目中的所有的日志级别都是info,和root同级下面的可以设置具体的日志级别这里就是com.example.demos.UserController和UserService设置不同的级别
3.2)日志的持久化:
前面的介绍都是将日志输出在控制台上的,然而在生产环境下往往需要将日志保存下来,以便于后续出现问题时去追溯原因,将日志保存下来的过程就称之为持久化
实现日志持久化的方式就是将日志信息保存到磁盘,同样可以通过设置配置文件实现
以上的SpringBoot的日志都是输出在控制台上面的,但是如果说我们想要在生产环境的情况下直接保存下来到磁盘的某一个位置,以便后面出现问题以后去追溯问题,我们把日志保存下来的这个过程就叫做日志持久化,它一共有两种实现方式:
1)在配置文件里面设置日志的保存路径,当设置了保存路径之后,那么日志就会自动地进行持久化操作,日志文件名字就是SpringBoot自动创建文件的名字(path);
2)在配置文件里面设置日志保存的名称,日志会自动地进行持久化(name),本质上也是一个路径
1)设置日志的保存路径:
设置日志的保存路径:spring boot会按照自己的格式生成日志文件并到对应的我们所指定的目录,下面的结果就是说在D盘下面的loggs目录会生成一个spring.log的文件
错误的使用方式:
此时我们要注意一个问题:在配置文件里面写日志持久化路径的时候:D:\Data\---->"\D"会被SpringBoot认为是一个特殊的字符,而非目录,所以说日志持久化不会成功 那么如何才可以正确保存呢? 1)logging.file.path:D:/Data/ 2)logging.file.path:D:\\Data\\
默认情况下springboot会有一个最大的日志大小限制,如果日志的文件大于默认的最大日志大小,那么SpringBoot会重新启动一个日志文件进行保存,我们可以在yml文件里面进行设置:max-size属性,默认情况下是20KB,自动进行分割
logging.level.com.example.demo.AL=trace//设置demo包底下的AL包里面的类日志级别全部是trace logging.file.path=D:\
程序部署到linux服务器上面,我们还是可以通过设置配置文件的方式来保存的日志
2)进行设置日志的保存文件名称:springboot会按照进行设置的文件来进行保存日志
logging: file: max-size: 2MB name: D:/loggs/SpringBoot.log#这样就可以修改文件名了
之前已经保存的日志是没有被覆盖掉的,而是从文件尾进行追加,重启项目之后,不是直接进行覆盖的
logging.file.name=D://SpringMVC//SpringBoot.log 要指定文件的全路径+文件名
四)更简单的输出日志:
这么写是很繁琐的,况且我们如果需要在要把每一个类打印日志对象,那么这个代码就需要在每一个类里面都要进行添加一遍上述代码,这当然也是很麻烦的,我们还有一些更为简化的日志输出方式,使用lombok来进行更简单的输出日志;
Logger ger=LoggerFactory.getLogger(类名.class)
更加简单的打印自定义日志而不依靠日志对象:通过注解
1)添加lombock框架支持,也是一个准备工作
1.1)创建项目的时候已经把lombok添加进来了;
1.2)使用插件完成SpringBoot外部框架的注入,下载EditStarters插件,这个插件是在SpringBoot中快速添加框架依赖的插件,右键点击generate,之后弹出界面的版本号是不可以进行修改的,我们在search里面直接进行搜索你要添加的依赖,进行双击就可以了,selected下面是当前项目中存在的所有依赖;
2)使用@slf4j注解输出日志
我们接下来使用更简单的方式来进行获取日志类并进行打印完整日志,lombok注解
添加lombok框架,通过@slf4j注解来进行获取到日志对象,这样默认就会得到一个log对象
logging: file: max-size: 2MB name: D:/loggs/Spring.log level: root: info #整个项目的日志级别是info
@Service @Slf4j//引入日志框架 public class UserService { @PostConstruct public void PostConstruct() { log.trace("我是日志"); log.error("我是UserService下的日志"); } }
1)相当于我们加上了@SLF4J之后就代替了
private static Logger logger= LoggerFactory.getLogger(JavaController.class);
2)注意:使用@SLF4J注解之后,我们在程序中直接使用log对象就可以进行输出日志,况且只有使用log对象才可以进行输出,自动提供log对象
3)加上Controller修饰的类里面使用路由地址才可以被浏览器访问到,咱们在Controller类里面调用Service层
lombok的原理以及常见注解:不影响程序执行的性能
在项目进行编译之前,lombock就可以把注解生成我想要的代码,比如说Getter和Setter,直接变成Setter方法和Getter方法
1)lombok是在编译时期搞事情的:.java代码+lombok,编译生成字节码,在JVM中进行加载运行,咱们不需要手动写Setter和Getter方法的
2)因为lombok会自动地在编译时期将我们的@Setter和@Getter注解进行引入,进一步说就是将注解替换成对应的字节码,本质上JVM是来进行识别.class文件也就是target目录;
3)查看target中的对应程序生成的.class文件就可以了,idea会自动将字节码转化成肉眼可见的JAVA源代码了,但是实际上target目录实际上存储的时候并不是以JAVA源代码来进行存储的,是以字节码来进行存储的,只不过是IDEA怕程序员看不懂,才来进行适配的,我们查看这个字节码发现,编译时期的注解都没有了,相应替换的是对应的注解对应的字节码;
3)SLF4J生成的变量就是log;
@Getter @Setter @ToString
@EqualsAndHashCode,自动添加equals和hashcode方法
@NoArgsConstructor,自动添加无参构造方法
@AllArgsConstructor,自动添加全属性构造方法,顺序按照属性的定义顺序
@NonNull,属性不可以为空
@Sif4j:添加一个为log的日志
要注意使用 @Slf4j 注解,在程序中使用 log 对象即可输⼊⽇志,并且只能使⽤ log 对象才能输出,这是 lombok 提供的对象名;代码中输入 log 后就会有相关方法的提醒(前提是安装了 lombok 插件)
使用 Lombok 提供给我们的这些注释,可以很好的帮助我们消除项目中大量冗余的代码,可以使得我们的 Java 类可以看起来非常的干净整洁