SpringBoot⽇志配置详解
前⾔
记录应⽤系统⽈志主要有三个原因记录操作轨迹、监控系统运⾏状况、回溯系统故障。记录操作⾏为及操作轨迹数据,可以数据化地分析⽤户偏好,有助于优化业务逻辑,为⽤户提供个性化的服务。例如,通过 access.log 记录⽤户的操作频度和跳转链接,有助于分析⽤户的后续⾏为。
全⾯有效的⽇志系统有助于建⽴完善的应⽤监控体系,由此⼯程师可以实时监控系统运⾏状况,及时预警,避免故障发⽣。监控系统运⾏状况,是指对服务器使⽤状态,如内存、 CPU 等使⽤情况,应⽤运⾏情况如响应时间 QPS 等交互状态;应⽤错误信息,如空指针、 SQL 异常等的监控。例如,在CPU 使⽤率⼤于 60%,四核服务器中load ⼤于4时发出报警,提醒⼯程师及时处理,避免发⽣故障。
当系统发⽣线上问题时,完整的现场⽇志有助于⼯程师快速定位问题。例如当系统内存溢出时,如果⽇志系统记录了问题发⽣现场的堆信息,就可以通过这个⽈志分析是什么对象在⼤量产⽣并且没有释放内存,回溯系统故障,从⽽定位问题。
⽇志规范
推荐⽇志⽂件命名⽅式
推荐的⽇志⽂件命名⽅式为appName_logType_logName.log 其中 logType为⽇志类型,推荐分类有 stats monitor visit等, logName 为⽇志描述。这种命名的好处是通过⽂件名就可以知道⽈志⽂件属于什么应⽤,什么类型,什么⽬的,也有利于归类查。例如, mppserver 应⽤中单独监控时区转换异常的⽇志⽂件名定义为mppserver__monitor_timeZoneConvert.log
推荐⽈志⽂件保存时间
代码规约推荐⽈志⽂件⾄少保存15天,可以根据⽇志⽂件的重要程度、⽂件⼤⼩及磁盘空间再⾃⾏延长保存时间。
预先判断⽈志级别
对DEBUG 、INFO 级别的⽇志,必须使⽤条件输出或者使⽤占位符的⽅式打印。该约定综合考虑了程序的运⾏效率和⽇志打印需求。例如在某个配置了打印⽇志级别为WARN 的应⽤中,如果针对 DEBUG 级别的⽇志,仅仅在程序中写出 logger.debug(”Processing trade with id:” + id + ” and symbol:"+ symbol);,那么该⽇志不会被打印但是会执⾏字符串拼接操作,如果 symbol 是对象还会执⾏ toString() ⽅法
⽩⽩浪费了系统资源。如下⽰例代码为正确的打印⽇志⽅式
/
/使明条件判断形式
if (logger.isDebugEnabled()) {
logger.debug ("Processing trade with id:" + id + "and symlbol:" + symbol) ;
//使⽤占位符形式
logger.debug ("Processing trade with id: {} and symbol: {}",id, symbol);
避免⽆效⽇志打印
⽣产环境禁⽌输出 DEBUG ⽈志且有选择地输出 INFO⽇志。使⽤ INFO、WARN 级别来记录业务⾏为信息时,⼀定要控制⽇志输出量,以免磁盘空间不⾜。同时要为⽈志⽂件设置合理的⽣命周期及时清理过期的⽇志。避免重复打印,务必在⽇志配置⽂件中设置 additivity=false
区别对待错误⽇志
WARN、ERROR 都是与错误有关的⽇志级别,但不要⼀发⽣错误就笼统地输出ERROR 级别⽇志。⼀些业务异常是可以通过引导重试就能恢复正常的,例如⽤户输⼊参数错误。在这种情况下,记录⽇志是
为了在⽤户咨询时可以还原现场,如果输出ERROR 级别就表⽰⼀旦出现就需要⼈为介⼊,这显然不合理。所以,ERROR只记录系统逻辑错误、异常或者违反重要的业务规则,其他错误都可以归为 WARN级别。
保证记录内容完整
⽈志记录的内容包括现场上下⽂信息与异常堆栈信息,所以打印时需要注意以下两点:
记录异常时⼀定要输出异常堆栈,例如 ("xxx" +e.getMessage(),e)
⽈志中如果输出对象实例,要确保实例类重写了 toString()⽅法,否则只会输出对象的 hashCode 没有实际意义。
⽇志框架分类与选择
⽇志门⾯(⽇志的抽象层)⽇志实现
JCL(Jakarta Commons Logging)(2014年后不再维护)jboss-logging (不适合企业项⽬开发使⽤)
SLF4J(Simple Logging Facade for java)Log4j
JUL(java.util.logging)(java.util.logging)(担⼼被抢市场,推出的) Log4j2( apache开发的很强⼤,借了log4j的名,但很多框架未适配上) Logback(Log4j同⼀个⼈开发的新框架,做了重⼤升级)
⽇志门⾯
门⾯设计模式是⾯向对象设计模式中的⼀种,⽇志框架采⽤的就是这种模式,类似JDBC 的设计理念。它只提供⼀套接⼝规范,⾃⾝不负责⽇志功能的实现。⽬的是让使⽤者不需要关注底层具体是哪个⽇志
库来负责⽇志打印及具体的使⽤细节等。⽬前⽤得最为⼴泛的⽈志门⾯有两种 slf4j和commons -logging
⽇志库
负责实现⽇志相关功能,主流⽇志库有三个,分别为:log4j、log-jdk(java.util.logging.Logger)、logback。logback是最晚出现的,与log4j同⼀个作者,是log4j的升级版且本⾝实现了slf4j的接⼝。
⽇志适配器
分为:⽇志门⾯适配器(⽇志库适配slf4j),⽇志库适配器(slf4j适配⽇志库)。
⽇志门⾯适配器
⽼⼯程⽤的⽇志库没有实现slf4j接⼝,如log4j;这时候⼯程⾥想使⽤slf4j+log4j的模式,就额外需要⼀个适配器(slf4j+log4j12)来解决接⼝不兼容问题
⽇志库适配器
⽼⼯程直接使⽤⽇志库API完成⽇志打印,要改成业界标准的门⾯模式(如slf4j+logback),但是⽼⼯程代码打印⽇志地⽅太多难以改动,这是就需要⼀个适配器来完成从旧⽇志库的API到slf4j的路由,这样在
不改动原有代码的情况下也能使⽤slf4j来统⼀管理⽇志(如:log4j-over-slf4j),后续⾃由替换具体⽇志库也不成问题。
Spring Boot 采⽤了 slf4j+logback 的组合形式,Spring Boot也提供对JUL、log4j2、Logback提供了默认配置
SpringBoot默认⽇志配置
新建springboot项⽬,引⼊web启动项,其他默认即可
package com.ller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;springboot切换log4j2
/**
* @Author: Luzy
* @Date: 2020-07-17 10:16
* @Description:
*/
@RequestMapping
@RestController
public class LogController {
private static final Logger logger = Logger(LogController.class);
@GetMapping("/log")
public String logTest1(String name) {
/
/ 由低到⾼:trace < debug < info < warn < error
//2. Spring Boot 默认设定的是 info 级别⽇志,(⽇志默认级别也称为root 级别)。可修改默认级别⽇志:=级别名
//3. 可以进⾏调整⽇志级别,设定某个级别后,就只打印设定的这个级别及后⾯⾼级别的⽇志信息。没有指定级别的就⽤SpringBoot 默认规定的级别:root 级别 //4. 可修改指定包的⽇志级别:指定某个包下⾯的所有⽇志级别:logging.level.包名=级别名
logger.info("------------info--------------{}",name);
<("------------error--------------{}",name);
logger.debug("------------debug--------------{}",name);
logger.warn("------------warn--------------{}",name);
return ""+name;
}
}
url 输⼊localhost:8080/log?name=lzy
,控制台输出
修改⽇志默认配置修改⽇志⽂件⽣成路径
logging:
file:
name: demo.log
# path: logs/log_lzy 修改⽇志输出格式
logging:
file:
name: demo.log
pattern:
console: '%clr(%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n)'
file: '%d{yyyy-MM-dd HH:mm:ss.SSS} >>> [%thread] >>> %-5level >>> %logger{50} >>> %msg%n'
# path: logs/log_lzy
注意:如上,yml ⽂件中⾸尾加上单引号可解决识别不了%的问题,properties 不需要加
分析⽇志底层实现
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
spring-boot-starter-web 中引⼊了 spring-boot-starter 启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.1.RELEASE</version>
<scope>compile</scope>
</dependency>
spring-boot-starter 中引⼊了 spring-boot-starter-logging ⽇志启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>2.3.1.RELEASE</version>
<scope>compile</scope>
logging.file.name
logging.file.path ⽰例说明(none)
(none)只在控制台输出指定⽂件名
(none)demo.log 输出到当前项⽬根路径下的 demo.log ⽂件中(none)
指定⽬录logs/log_lzy 输出到当前项⽬所在磁盘根路径下的/logs/log_lzy ⽬录中的 spring.log ⽂件中指定⽂件名
指定⽬录当两个同时指定时,采⽤的是logging.file.name 指定。推荐使⽤logging.file.name 设置即可,因为它可⾃定义⽂件名
</dependency>
spring-boot-starter-logging ⽇志启动器采⽤的是 logback ⽇志框架
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>compile</scope>
</dependency>
总结:SpringBoot中默认⽇志启动器为 spring-boot-starter-logging ,默认采⽤的是 logback ⽇志框架
在 spring-boot-2.3.1.RELEASE.jar! \org\springframework\boot\logging\l 做了⽇志的默认配置
<included>
<!--⽇志格式默认规定-->
<include resource="org/springframework/boot/logging/l" />
<!--⽇志⽂件默认⽣成路径-->
<property name="LOG_FILE"value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${pdir:-/tmp}}}/spring.log}"/>
<!--控制台⽇志信息默认配置-->
<include resource="org/springframework/boot/logging/l" /> <!--⽂件中⽇志信息默认配置-->
<include resource="org/springframework/boot/logging/l" /> <!--⽇志级别默认为: info -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</included>
⽇志⽂件采⽤⽅式为:滚动⽂件追加器
在下⾯类中会读取上⾯xml中配置的信息
如果spring boot 的⽇志功能⽆法满⾜我们的需求(⽐如异步⽇志记录等),我们可以⾃已定义的⽇志配置⽂件⾃定义⽇志配置
⾃定义Logback ⽇志配置
在类路径下,存放对应⽇志框架的⾃定义配置⽂件即可;SpringBoot 就不会使⽤它默认的⽇志配置⽂件了l
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="false" scanPeriod="60 seconds">
<property name="LOG_HOME" value="./logs/logback"/>
<property name="appName" value="lzy-logDemo"/>
<!-- 定义控制台输出 -->
<appender name="stdout" class="ch.ConsoleAppender">
<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.olling.RollingFileAppender">
<!-- 指定⽇志⽂件的名称 -->
<file>${LOG_HOME}/${appName}.log</file>
<rollingPolicy class="ch.olling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<MaxHistory>30</MaxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.olling.SizeAndTimeBasedFNATP"> <MaxFileSize>10MB</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="org.springframework" level="debug" additivity="false"/>
<logger name="com.lzy.logdemo" level="debug"/>
<root level="INFO">
<appender-ref ref="stdout"/>
<appender-ref ref="appLogAppender"/>
</root>Logging System
Customization Logback
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论