网站首页 > 技术文章 正文
前言
前些天接手了一个有些年头的Spring Boot项目,今天好不容易修复了一个bug,中间过程真是特别“酸爽”,觉得有必要记录一下。
情况是这样的,这个bug我在本地开发环境复现不了,又没办法直连线上数据库debug,所以我只能去服务器上翻日志文件,然后发现一个10G+的日志文件nohup.out,所有的运行日志全都在这个文件里,你们知道我当时心里在想什么吗?就特别想和写这项目的前辈好好聊聊人生……
没办法我只能按照程序出问题的大概时间慢慢去排查日志,过程之艰辛就不谈了,说多了都是泪!
突然想起了罗永浩在发布会上的那句话:“原谅他们,因为他们不知道自己在做什么。”
好不容易改完bug,我又花了点时间改造了一下老项目的日志模块,实现了日志按级别、按天、按大小切多文件存储、同时错误日志自动保存到数据库,后面集成前端页面后可以很方便的排查问题,最后又花了点时间了解了一下Spring Boot框架默认加载日志框架的机制。
目录
- 为什么Spring Boot不需要任何配置就能输出日志?
- 如何自定义日志输出?
- 输出日志到控制台——高亮显示关键信息
- 输出日志到文件——按级别、日期、大小切分
- 输出日志到数据库——关键信息自动保存到数据库
- 总结
为什么Spring Boot不需要任何配置就能输出日志?
不知道大家有没有注意过这个现象:一个新建的Spring Boot项目,什么都不用配置,启动的时候控制台就会输出一堆日志:
这是怎么回事?根据我的猜想肯定是Spring Boot默认加载了某个日志框架。
观察一下项目的Maven依赖关系可以发现Spring Boot确实默认依赖了spring-boot-starter-logging模块,spring-boot-starter-logging模块又依赖了logback、log4j等具体的日志框架:
那么问题来了,Spring Boot默认的日志模块是哪一个呢?
看一下自动加载的核心配置文件spring.factories,可以发现Spring Boot默认加载了两个监听器,分别是ClasspathLoggingApplicationListener和LoggingApplicationListener:
这两个监听器都实现了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的主要作用就是可以滚动记录日志,它有两个重要的策略:RollingPolicy和 TriggeringPolicy。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作为日志框架,我们可以通过自定义配置的方式将日志输出到控制台、文件或者数据库中。
“分享干货,收获快乐”
我是一名程序员,喜欢我的文章欢迎 关注 及 转发,我会经常与大家分享工作当中的实用技巧与经验。
- 上一篇: MySQL慢查询:慢SQL定位、日志分析与优化方案
- 下一篇: 完美日记的微服务实践和优化思路
猜你喜欢
- 2024-11-22 前端容器化实践
- 2024-11-22 npm audit —— 守护Node.js前端代码安全的小助手
- 2024-11-22 程序员私活利器 (Jfinal)——找到一次请求的所有日志
- 2024-11-22 完美日记的微服务实践和优化思路
- 2024-11-22 MySQL慢查询:慢SQL定位、日志分析与优化方案
- 2024-11-22 ELK Stack实用教程:让你的日志数据发挥更大的价值
- 2024-11-22 干货 | 企业如何快速采集分析日志?
- 2024-11-22 Spring Boot 整合 Apache Spark 进行日志分析?
- 2024-11-22 Windows系统服务器系统日志在哪里查看?
- 2024-11-22 websocket方法执行实时日志展示
你 发表评论:
欢迎- 07-10Oracle 与 Google Cloud 携手大幅扩展多云服务
- 07-10分享收藏的 oracle 11.2.0.4各平台的下载地址
- 07-10Oracle 和 Microsoft 推出 Oracle Exadata 数据库服务
- 07-10Oracle Database@Azure 推进到南美等新区域并增加了新服务
- 07-10Oracle宣布推出 Oracle Database@AWS 的有限预览版
- 07-10Oracle与Nextcloud合作,推出主权云上的安全协作平台
- 07-10NodeRED魔改版连接MsSql、PostgreSQL、MySQL、OracleDB存储无忧
- 07-10对于企业数据云备份,“多备份”承诺的是成本更低,管理更高效#36氪开放日深圳站#
- 602℃几个Oracle空值处理函数 oracle处理null值的函数
- 594℃Oracle分析函数之Lag和Lead()使用
- 582℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 579℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 574℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 567℃【数据统计分析】详解Oracle分组函数之CUBE
- 554℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 548℃Oracle有哪些常见的函数? oracle中常用的函数
- 最近发表
-
- Oracle 与 Google Cloud 携手大幅扩展多云服务
- 分享收藏的 oracle 11.2.0.4各平台的下载地址
- Oracle 和 Microsoft 推出 Oracle Exadata 数据库服务
- Oracle Database@Azure 推进到南美等新区域并增加了新服务
- Oracle宣布推出 Oracle Database@AWS 的有限预览版
- Oracle与Nextcloud合作,推出主权云上的安全协作平台
- NodeRED魔改版连接MsSql、PostgreSQL、MySQL、OracleDB存储无忧
- 对于企业数据云备份,“多备份”承诺的是成本更低,管理更高效#36氪开放日深圳站#
- 解读丨《归档文件整理规则》— 电子文件元数据存储
- Data Guard跳归档恢复的实践(dataguard failover)
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端路由 (61)
- 前端数组 (73)
- 前端js面试题 (50)
- 前端定时器 (59)
- 前端获取当前时间 (50)
- Oracle RAC (76)
- oracle恢复 (77)
- oracle 删除表 (52)
- oracle 用户名 (80)
- oracle 工具 (55)
- oracle 内存 (55)
- oracle 导出表 (62)
- oracle约束 (54)
- oracle 中文 (51)
- oracle链接 (54)
- oracle的函数 (57)
- 前端调试 (52)
本文暂时没有评论,来添加一个吧(●'◡'●)