这两天在研究 springboot 的日志, 所以记录一下, 做一下总结.
几篇关于日志的文章:
介绍日志:
https://www.cnblogs.com/bigdataZJ/p/springboot-log.html
记录日志:
日志, 通常不会在需求阶段作为一个功能单独提出来, 也不会在产品方案中看到它的细节. 但是, 这丝毫不影响它在任何一个系统中的重要的地位.
为了保证服务的高可用, 发现问题一定要即使, 解决问题一定要迅速, 所以生产环境一旦出现问题, 预警系统就会通过邮件, 短信甚至电话的方式实施多维轰炸模式, 确保相关负责人不错过每一个可能的 bug.
预警系统判断疑似 bug 大部分源于日志. 比如某个微服务接口由于各种原因导致频繁调用出错, 此时调用端会捕获这样的异常并打印 ERROR 级别的日志, 当该错误日志达到一定次数出现的时候, 就会触发报警.
try {
调用某服务
- } catch(Exception e) {
- LOG.error("错误信息", e);
- }
所以日志至关重要, 这篇就来介绍下在 Spring Boot 如何配置日志.
Spring Boot 默认日志系统
Spring Boot 默认使用 LogBack 日志系统, 如果不需要更改为其他日志系统如 Log4j2 等, 则无需多余的配置, LogBack 默认将日志打印到控制台上.
1, 如果要使用 LogBack, 原则上是需要添加 dependency 依赖的
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-logging</artifactId>
- </dependency>
但是因为新建的 Spring Boot 项目一般都会引用 spring-boot-starter 或者
spring-boot-starter-web
, 而这两个起步依赖中都已经包含了对于
spring-boot-starter-logging
的依赖,
所以, 无需额外添加依赖, 配置 logback-spring.xml 就可以了. 以 logback-spring.xml 命名, spring 会自动识别加载.
2, 如果需要切换 Log4j2, 那么在 pom.xml 中需要排除 springboot 自带的 commons-logging, 然后再引入 log4j2 的依赖:
- <!-- 排除 commons-logging-->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-Web</artifactId>
- <exclusions>
- <exclusion>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <!-- 引入 log4j2 -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-log4j2</artifactId>
- </dependency>
然后再引入 log4j.properties 文件就可以了.
- log4j.properties:
- ### set log levels ###
- log4j.rootLogger = debug , stdout , D , E
- ### 输出到控制台 ###
- log4j.appender.stdout = org.apache.log4j.ConsoleAppender
- log4j.appender.stdout.Target = System.out
- log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
- log4j.appender.stdout.layout.ConversionPattern = %d{
- ABSOLUTE
- } ===== %5p %c{
- 1
- }:%L - %m%n
- #### 输出到日志文件 ###
- #log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
- #log4j.appender.D.File = logs/log.log
- #log4j.appender.D.Append = true
- #log4j.appender.D.Threshold = DEBUG ## 输出 DEBUG 级别以上的日志
- #log4j.appender.D.layout = org.apache.log4j.PatternLayout
- #log4j.appender.D.layout.ConversionPattern = %-d{
- yyyy-MM-dd HH:mm:ss
- } [ %t:%r ] - [ %p ] %m%n
- #
- #### 保存异常信息到单独文件 ###
- #log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
- #log4j.appender.D.File = logs/error.log ## 异常日志文件名
- #log4j.appender.D.Append = true
- #log4j.appender.D.Threshold = ERROR ## 只输出 ERROR 级别以上的日志!!!
- #log4j.appender.D.layout = org.apache.log4j.PatternLayout
- #log4j.appender.D.layout.ConversionPattern = %-d{
- yyyy-MM-dd HH:mm:ss
- } [ %t:%r ] - [ %p ] %m%n
日志级别
日志级别由低到高: trace <debug < info < warm < error , 设置的级别越低显示的日志级别的信息越多.
例如: 如果设置的日志级别是 info, 那么此时, 低于 info 级别的 trace,debug 日志不会显示.
springboot 在不对日志进行任何设置的情况下, 默认日志 root 级别是 INFO, 输出的是 INFO 级别以上的日志.
- package com.ll;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.boot.test.context.SpringBootTest;
- import org.springframework.test.context.junit4.SpringRunner;
- @RunWith(SpringRunner.class)
- @SpringBootTest
- public class SpringbootCacheApplicationTests {
- private static final Logger log = LoggerFactory.getLogger(SpringbootCacheApplicationTests.class);
- /**
- * 测试 日志级别:
- * 级别由低到高: trace < debug < info < warm < error 设置的级别越低显示的日志信息越多.
- * 可以调整输出的日志级别, 只会显示高于设置级别的日志.
- */
- @Test
- public void testLog() {
- log.trace("这是 track 日志...");
- log.debug("这是 debug 日志...");
- //spring 默认设置的级别是 info 级别, 没有指定级别的情况下, 会使用 spring 默认的 root 级别(显示的是 info 级别的信息)
- log.info("这是 info 日志...");
- log.warn("这是 warm 日志...");
- log.error("这是 error 日志...");
- }
- }
控制台信息:
只会输出 INFO 级别以上的日志信息, 开发环境中, 要显示打印 sql 语句等 debug 调试信息, 要对日志级别进行设置.
日志的组成部分:
时间日期: 精确到毫秒
日志级别: ERROR, WARN, INFO, DEBUG or TRACE
进程 ID
分隔符:- 标识实际日志的开始
线程名: 方括号括起来(可能会截断控制台输出)
Logger 名: 通常使用源代码的类名
日志内容
日志配置:
日志可以通过两种方式配置:
一, application.properties 或者 application.YAML 文件配置:
这种方式需要把所有的日志配置写在 properties 或者 YAML 文件里面, 配置迁移不方便, 写的感觉也有点乱, 很繁杂, 对 log4j2 的支持也不好. 推荐用第二种, logback 的 xml 配置方便比较好, 配置迁移复制粘贴, 然后改一下里面的配置就好了.
简单列举一下:
logging.level.* = LEVEL
logging.level: 日志级别控制前缀,* 为包名或 Logger 名
LEVEL: 选项 TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF
举例说明:
logging.level.com.ll=DEBUG 表示 com.ll 包下所有 class 以 DEBUG 级别输出(包含 mapper 所在的包, 会打印 sql 语句)
logging.level.root=INFO 表示 root 日志以 INFO 级别输出
logging.path 该属性用来配置日志文件的路径
logging.file 该属性用来配置日志文件名, 如果该属性不配置, 默认文件名为 spring.log
logging.path=/springboot/log 在当前磁盘的根路径下创建 spring 文件夹和里面的 log 文件夹; 如果不配置, 使用 spring.log 作为默认文件
logging.file=E:/springboot/log/spring.log 可以指定完整的路径(logging.path 和 logging.file 配置一个即可)
另外还有日志的打印位置设置:
- logging.pattern.console=%d{
- yyyy/MM/dd-HH:mm:ss
- } [%thread] %-5level %logger- %msg%n
- logging.pattern.file=%d{
- yyyy/MM/dd-HH:mm
- } [%thread] %-5level %logger- %msg%n
- logging.pattern.console
该属性用于定制日志输出格式. 上述配置的编码中, 对应符号的含义如下:
%d{HH:mm:ss.SSS}-- 日志输出时间
%thread-- 输出日志的进程名字, 这在 Web 应用以及异步任务处理中很有用
%-5level-- 日志级别, 并且使用 5 个字符靠左对齐
%logger- -- 日志输出者的名字
%msg-- 日志消息
%n-- 平台的换行符
- appliacation.properties:
- # com.ll 包下所有 class 以 DEBUG 级别输出(包含 mapper 所在的包, 会打印 sql 语句)
- logging.level.com.ll=DEBUG
- # root 日志以 INFO 级别输出
- logging.level.root=INFO
- # 在当前磁盘的根路径下创建 spring 文件夹和里面的 log 文件夹; 如果不配置, 使用 spring.log 作为默认文件
- #logging.path=/springboot/log
- # 可以指定完整的路径(logging.path 和 logging.file 配置一个即可)
- logging.file=E:/springboot/log/spring.log
- # 控制台日志格式
- logging.pattern.console=%d{
- yyyy/MM/dd-HH:mm:ss
- } [%thread] %-5level %logger- %msg%n
- # 文件日志格式
- logging.pattern.file=%d{
- yyyy/MM/dd-HH:mm
- } [%thread] %-5level %logger- %msg%n
二, logback 的 xml 文件配置:
由于日志服务一般都在 ApplicationContext 创建前就初始化了, 它并不是必须通过 Spring 的配置文件控制. 因此通过系统属性和传统的 Spring Boot 外部配置文件依然可以很好的支持日志控制和管理.
根据不同的日志系统, 你可以按如下规则组织配置文件名, 就能被正确加载:
- Logback:logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy
- Log4j:log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xml
- Log4j2:log4j2-spring.xml, log4j2.xml
- JDK (Java Util Logging):logging.properties
Spring Boot 官方推荐优先使用带有 - spring 的文件名作为你的日志配置(如使用 logback-spring.xml, 而不是 logback.xml), 命名为 logback-spring.xml 的日志配置文件, spring boot 可以为它添加一些 spring boot 特有的配置项(下面会提到).
上面是默认的命名规则, 并且放在 src/main/resources 下面即可.
如果你即想完全掌控日志配置, 但又不想用 logback.xml 作为 Logback 配置的名字, 可以通过 logging.config 属性指定自定义的名字: ogging.config=classpath:logging-config.xm
虽然一般并不需要改变配置文件的名字, 但是如果你想针对不同运行时 Profile 使用不同的日志配置, 这个功能会很有用.
logback-spring.xml 配置:
- <?xml version="1.0" encoding="UTF-8"?>
- <!--
- scan: 当此属性设置为 true 时, 配置文件如果发生改变, 将会被重新加载, 默认值为 true.
- scanPeriod: 设置监测配置文件是否有修改的时间间隔, 如果没有给出时间单位, 默认单位是毫秒当 scan 为 true 时, 此属性生效. 默认的时间间隔为 1 分钟.
- debug: 当此属性设置为 true 时, 将打印出 logback 内部日志信息, 实时查看 logback 运行状态. 默认值为 false.
- -->
- <configuration scan="false" scanPeriod="60 seconds" debug="false">
- <!-- 定义日志的根目录 -->
- <property name="LOG_HOME" value="/app/log" />
- <!-- 定义日志文件名称 -->
- <property name="appName" value="atguigu-springboot"></property>
- <!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
- <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
- <!--
- 日志输出格式:
- %d 表示日期时间,
- %thread 表示线程名,
- %-5level: 级别从左显示 5 个字符宽度
- %logger{50} 表示 logger 名字最长 50 个字符, 否则按照句点分割.
- %msg: 日志消息,
- %n 是换行符
- -->
- <layout class="ch.qos.logback.classic.PatternLayout">
- <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
- </layout>
- </appender>
- <!-- 滚动记录文件, 先将日志记录到指定文件, 当符合某个条件时, 将日志记录到其他文件 -->
- <appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
- <!-- 指定日志文件的名称 -->
- <file>${LOG_HOME}/${appName}.log</file>
- <!--
- 当发生滚动时, 决定 RollingFileAppender 的行为, 涉及文件移动和重命名
- TimeBasedRollingPolicy: 最常用的滚动策略, 它根据时间来制定滚动策略, 既负责滚动也负责出发滚动.
- -->
- <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
- <!--
- 滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}: 按天进行日志滚动
- %i: 当文件大小超过 maxFileSize 时, 按照 i 进行文件滚动
- -->
- <fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
- <!--
- 可选节点, 控制保留的归档文件的最大数量, 超出数量就删除旧文件. 假设设置每天滚动,
- 且 maxHistory 是 365, 则只保存最近 365 天的文件, 删除之前的旧文件. 注意, 删除旧文件是,
- 那些为了归档而创建的目录也会被删除.
- -->
- <MaxHistory>365</MaxHistory>
- <!--
- 当日志文件超过 maxFileSize 指定的大小是, 根据上面提到的 %i 进行日志文件滚动 注意此处配置 SizeBasedTriggeringPolicy 是无法实现按文件大小进行滚动的, 必须配置 timeBasedFileNamingAndTriggeringPolicy
- -->
- <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
- <maxFileSize>100MB</maxFileSize>
- </timeBasedFileNamingAndTriggeringPolicy>
- </rollingPolicy>
- <!-- 日志输出格式: -->
- <layout class="ch.qos.logback.classic.PatternLayout">
- <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
- </layout>
- </appender>
- <!--
- logger 主要用于存放日志对象, 也可以定义日志类型, 级别
- name: 表示匹配的 logger 类型前缀, 也就是包的前半部分
- level: 要记录的日志级别, 包括 TRACE <DEBUG < INFO < WARN < ERROR
- additivity: 作用在于 children-logger 是否使用 rootLogger 配置的 appender 进行输出,
- false: 表示只用当前 logger 的 appender-ref,
- true: 表示当前 logger 的 appender-ref 和 rootLogger 的 appender-ref 都有效
- -->
- <!--
- logger 是记录 Logger 对象输出的日志级别的
- sercvice 实现类引入日志对象可以查看方法的报错信息以及打印 sql 语句, public static final Logger logger = LoggerFactory.getLogger(SysUserServiceImpl.class);
- 生产环境:
- 一般把 level 设为 error, 可以记录错误的日志信息, 毕竟主要是要记录错误信息进行错误定位.
- 开发环境:
- 类中引入了 logger 日志对象时, level 级别用 info,debug 都可以, 都有错误信息输出.
- -->
- <!-- hibernate logger -->
- <logger name="com.ll" level="info" />
- <!-- Spring framework logger -->
- <logger name="org.springframework" level="debug" additivity="false"></logger>
- <!--
- root 与 logger 是父子关系, 没有特别定义则默认为 root, 任何一个类只会和一个 logger 对应,
- 要么是定义的 logger, 要么是 root, 判断的关键在于找到这个 logger, 然后判断这个 logger 的 appender 和 level.
- -->
- <!-- 一般用默认的 info 就可以 -->
- <root level="info">
- <!-- 控制台输出日志 -->
- <appender-ref ref="stdout" />
- <!--
- 开发环境:
- 不需要往文件记录日志, 可以把这个 appender-ref ref="appLogAppender" 注释, 上面那个往文件写日志的 appender 也要注释, 不然每天都产生一个空文件;
- 生产环境:
- 需要往文件记录日志, 此时 appender-ref ref="appLogAppender" 就不能注释了, 不然没日志记录到文件, 上面那个往文件写日志的 appender 也要放开.
- -->
- <appender-ref ref="appLogAppender" />
- </root>
- </configuration>
配置完 logback-spring.xml, 接下来需要做的就是在把 Logger 对象引入到需要记录日志的类了.
public static final Logger logger = LoggerFactory.getLogger(SysUserServiceImpl.class);
下面列举一个记录批量保存方法记录错误日志的例子:
- package com.ll.service.impl;
- import java.util.List;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Isolation;
- import org.springframework.transaction.annotation.Propagation;
- import org.springframework.transaction.annotation.Transactional;
- import org.springframework.util.StringUtils;
- import com.ll.bean.SysUser;
- import com.ll.mapper.SysUserMapper;
- import com.ll.service.SysUserService;
- import com.ll.utils.MyException;
- @Service
- public class SysUserServiceImpl implements SysUserService {
- public static final Logger logger = LoggerFactory.getLogger(SysUserServiceImpl.class);
- @Autowired
- SysUserMapper sysUserMapper;
- // 开启事务
- @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,rollbackFor = MyException.class)
- @Override
- public int batchInsertUser(List<SysUser> userList) throws MyException {
- //
- try {
- sysUserMapper.batchInsertUser(userList);
- } catch (Exception e) {
- /** 推荐 printStackTrace()打印堆栈错误信息这个方法保留, 红色错误提示, 开发的时候更方便查看错误信息 */
- e.printStackTrace();
- /**logger.error 方法把异常错误 e 记录进日志文件, 开发环境用 logger.error 方法结合 printStackTrace()打印堆栈错误信息的方法就可以了 */
- logger.error("method:batchInsertUser,error:",e);
- /** 因为 logback-spring.xml 的 logger 的 level 级别设置为 error 时, logger.info 的消息不会打印, 也不会记录进日志文件, 所以不要用 logger.info 方法, 开发环境用 printStackTrace()打印堆栈错误信息即可.*/
- //logger.info("method:batchInsertUser",e);
- throw new MyException("批量新增失败!",e.getCause());
- }
- return 1;
- }
- }
有时候 logger.error 不能完全地打印出网站的错误堆栈信息, 只能打印这个错误是一个什么错误.
为什么?
看 Logger.error 源码
- public void error(String msg, Throwable t);
- public void error(String msg);
如果只传一个参数 e 进去, 那么 e 就被认为是 String 类型 (会自动调 toString() 方法把 Exception 转成 String), 而不是 Exception 类型.
如果想打印堆栈信息, 那么必须传两个或以上参数, 实际上就是为了调用 public void error(String msg, Throwable t);
所以我们的写法可以是:
Logger.error("xxx 出错",e); // 第二个参数是 e
而不是:
Logger.error("xxx 出错:"+e) 或 logger.error(e) 或 logger.error(e.getMessage);
到此, springboot 的 slf4j 简单日志门面, 默认使用的 logback-spring.xml 配置和日志记录就完成了~~~~~~~
来源: https://www.cnblogs.com/javalanger/p/10964603.html