专业编程教程与实战项目分享平台

网站首页 > 技术文章 正文

线上程序出bug,排查起来很麻烦?一个好的日志系统是关键

ins518 2024-11-22 15:45:05 技术文章 7 ℃ 0 评论

前言

前些天接手了一个有些年头的Spring Boot项目,今天好不容易修复了一个bug,中间过程真是特别“酸爽”,觉得有必要记录一下。

情况是这样的,这个bug我在本地开发环境复现不了,又没办法直连线上数据库debug,所以我只能去服务器上翻日志文件,然后发现一个10G+的日志文件nohup.out,所有的运行日志全都在这个文件里,你们知道我当时心里在想什么吗?就特别想和写这项目的前辈好好聊聊人生……

没办法我只能按照程序出问题的大概时间慢慢去排查日志,过程之艰辛就不谈了,说多了都是泪!

突然想起了罗永浩在发布会上的那句话:“原谅他们,因为他们不知道自己在做什么。

好不容易改完bug,我又花了点时间改造了一下老项目的日志模块,实现了日志按级别、按天、按大小切多文件存储、同时错误日志自动保存到数据库,后面集成前端页面后可以很方便的排查问题,最后又花了点时间了解了一下Spring Boot框架默认加载日志框架的机制。

目录

  1. 为什么Spring Boot不需要任何配置就能输出日志?
  2. 如何自定义日志输出?
  3. 输出日志到控制台——高亮显示关键信息
  4. 输出日志到文件——按级别、日期、大小切分
  5. 输出日志到数据库——关键信息自动保存到数据库
  6. 总结

为什么Spring Boot不需要任何配置就能输出日志?

不知道大家有没有注意过这个现象:一个新建的Spring Boot项目,什么都不用配置,启动的时候控制台就会输出一堆日志:

这是怎么回事?根据我的猜想肯定是Spring Boot默认加载了某个日志框架。

观察一下项目的Maven依赖关系可以发现Spring Boot确实默认依赖了spring-boot-starter-logging模块,spring-boot-starter-logging模块又依赖了logbacklog4j等具体的日志框架:

那么问题来了,Spring Boot默认的日志模块是哪一个呢?

看一下自动加载的核心配置文件spring.factories,可以发现Spring Boot默认加载了两个监听器,分别是ClasspathLoggingApplicationListenerLoggingApplicationListener

这两个监听器都实现了GenericApplicationListener接口,而GenericApplicationListener接口又继承自Spring框架的ApplicationListener顶级接口。

ApplicationListener接口有什么用?

熟悉Spring Boot内部机制的人应该知道,在应用的整个启动过程中会经历启动、启动中、初始化、环境准备等几个阶段,在每个阶段系统都会回调ApplicationListener接口中的onApplicationEvent方法,在这个回调方法里我们通常会做一些初始化的工作。

看一下LoggingApplicationListener监听器的实现逻辑:

在收到Spring容器启动事件的回调后,系统开始去加载并初始化日志框架;

继续看一下get方法:

这个SYSTEMS是个Map对象,默认加载了LogbackLoggingSystem、Log4J2LoggingSystem和JavaLoggingSystem三套日志框架:

get方法的逻辑就是循环SYSTEMS找出classpath下已经存在的日志框架,如果存在多个则取第一个即LogbackLoggingSystem,所以Spring Boot默认使用的是Logback日志框架。

如何自定义日志输出?

很多时候系统默认的日志配置并不能满足我们的开发需求,那我们就需要对Logback进行自定义配置,那么Logback的配置文件是啥?

我们还是从源码入手,先来看一下LogbackLoggingSystem的初始化方法:

首先会去尝试加载Logback自己的初始化配置文件,包括logback-test.groovy、logback-test.xml、logback.groovy和logback.xml4个文件:

如果没有加载到标准配置文件,就会去尝试加载Spring的初始化配置文件,包括logback-test-spring.groovy,logback-test-spring.xml,logback-spring.groovy,logback-spring.xml4个文件;

最后如果上面两种方式都没有加载到配置文件,则去加载默认的配置;

通常我们会使用logback-spring.xml作为我们自定义的配置文件名称。

输出日志到控制台——高亮显示关键信息

其实Logback不用配置默认就会输出日志到控制台,但是我发现所有日志都是一个颜色,看起来费劲。怎么办?改!

我们使用ConsoleAppender构造一个appender,然后通过encoder节点可以传入一个自定义的日志输出格式,将关键信息高亮显示:

输出日志到文件——按级别、日期、大小切分

将日志信息输出到文件是非常必要的,出问题时我们可以查找日志文件排查问题。

但是随着应用的运行时间越来越长,日志文件也会变得越来越大,那么将所有日志输出到一个文件的做法就不合适了,我们需要按日志级别、日期、大小来切分日志文件。

我们使用RollingFileAppender构造一个appender,RollingFileAppender的主要作用就是可以滚动记录日志,它有两个重要的策略:RollingPolicyTriggeringPolicy。RollingPolicy决定了日志滚动方式,TriggeringPolicy决定了日志滚动的触发条件。

以这个例子来讲,它是基于时间和文件大小的滚动策略,每天会新生成一个日志文件;或者当日志文件达到指定大小时也会进行滚动生成新的日志文件。

输出日志到数据库——关键信息自动保存到数据库

虽然日志可以输出到文件,但是很多时候我们排查问题时依然感觉是比较麻烦的,可不可以将关键日志自动存到数据库,然后通过一个页面来查看日志呢?

答案当然是可以的!我们使用DBAppender来构造一个appender,然后通过connectionSource节点配置数据库信息:

有人要问了:日志数据会存到哪张表?可以自定义表结构吗?

Logback默认提供了一个logging_event表来存储日志数据:

其中formatted_message字段用来存具体的日志信息,arg0-arg3共4个字段用来存我们的参数:

Logback默认的日志表最多只能存4个自定义参数,很多时候并不能满足我们的业务需求,我们可以进行自定义处理。

那么我们该怎么做呢?没头绪的话那就先看一下默认的DBAppender的实现逻辑吧:

我列了一下DBAppender里面有2个比较重要的点:

一是创建Insert SQL语句;

二是绑定SQL参数并完成数据插入;

既然我们要自定义表结构,那么这两块代码的逻辑肯定要改,新建一个类继承DBAppender并重写它的getInsertSQL和subAppend方法:

getInsertSQL返回我们自定义表的插入语句,subAppend方法里我们重新绑定相应的参数。

总结

记录日志是一件非常有必要的事情,它可以帮助我们快速定位解决问题,所以我们在开发过程中要尽可能多的记录日志。

Spring Boot框架默认使用Logback作为日志框架,我们可以通过自定义配置的方式将日志输出到控制台、文件或者数据库中。


“分享干货,收获快乐”

我是一名程序员,喜欢我的文章欢迎 关注 及 转发,我会经常与大家分享工作当中的实用技巧与经验。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表