目录
为什么需要日志
Springboot 如何使用Logback
日志文件输出目录控制
日志级别输出控制
自定义日志配置
日志配置文件创建
logback-spring.xml日志配置文件结构
最佳实践
目标
Pom引入必要的jar
application.properties
logback配置文件
特定类使用特定日志文件输出
方法一:使用LoggerFactory.getLogger()制定logger名
方法二:使用Lombok指定Logger
Reference
前半部分介绍一些基础,如果已经了解,可以直接看最佳实践部分!!!
前半部分介绍一些基础,如果已经了解,可以直接看最佳实践部分!!!
前半部分介绍一些基础,如果已经了解,可以直接看最佳实践部分!!!
为什么需要日志
在日常的项目开发中,遇到一些难以定位的问题通常会使用debug的方式进行结局,但项目上线后就无法继续通过Debug的方式对错误进行定位和分析,那该如何解决该问题呢?毫无疑问“日志”记录就是解决线上错误定位的最有效手段之一。
在java项目中通常会使用第三方的日志框架进行日志记录,当前java有很多优秀的日志框架,如:JUL,log4j,logback,log4j2 ----JCL,slf4j等。
Springboot 如何使用Logback
日志文件输出目录控制
配置application.properties
logging.file,设置文件,可以是绝对路径,也可以是相对路径。如:logging.file=my.log
logging.path,设置目录,会在该目录下创建spring.log文件,并写入日志内容,如:logging.path=/var/log
如果只配置 logging.file,会在项目的当前路径下生成一个 xxx.log 日志文件。
如果只配置 logging.path,在 /var/log文件夹生成一个日志文件为 spring.log
注:二者不能同时使用,如若同时使用,则只有logging.file生效
日志级别输出控制
配置application.properties
logging.level.com.foo=DEBUG:com.foo包下所有class以DEBUG级别输出
logging.level.root=WARN:root日志以WARN级别输出
自定义日志配置
日志配置文件创建
由于日志服务一般都在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 配置的名字,可以在application.properties 配置文件里面通过logging.config属性指定自定义的名字:
logging.config=classpath:logging-config.xml
logback-spring.xml日志配置文件结构
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>logback</contextName>
<property name="log.path" value="/Users/tengjun/Documents/log" />
<!--输出到控制台-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>-->
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!--输出到文件-->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/logback.%d{yyyy-MM-dd}.log</fileNamePattern>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="file" />
</root>
<!-- logback为java中的包 -->
<logger name="com.dudu.controller"/>
<!--logback.LogbackDemo:类的全路径 -->
<logger name="com.dudu.controller.LearnController" level="WARN" additivity="false">
<appender-ref ref="console"/>
</logger>
</configuration>
根节点<configuration> 下面一共有2个属性,3个子节点。
属性一:设置上下文名称<contextName>
每个logger都关联到logger上下文,默认上下文名称为“default”。但可以使用设置成其他名字,用于区分不同应用程序的记录。一旦设置,不能修改,可以通过%contextName来打印日志上下文名称。
1
| <contextName>logback</contextName>
|
属性二:设置变量<property>
用来定义变量值的标签,?有两个属性,name和value;其中name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。
1
| <property name="log.path" value="/Users/tengjun/Documents/log" />
|
子节点一<appender>
appender用来格式化日志输出节点,有俩个属性name和class,class用来指定哪种输出策略,常用就是控制台输出策略和文件输出策略。
#####控制台输出ConsoleAppender:
<!--输出到控制台-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<encoder> 表示对日志进行编码:
%d{HH: mm:ss.SSS} ——日志输出时间%thread ——输出日志的进程名字,这在Web应用以及异步任务处理中很有用%-5level ——日志级别,并且使用5个字符靠左对齐%logger{36} ——日志输出者的名字%msg ——日志消息%n ——平台的换行符
ThresholdFilter为系统定义的拦截器,例如我们用ThresholdFilter来过滤掉ERROR级别以下的日志不输出到文件中。如果不用记得注释掉,不然你控制台会发现没日志~
输出到文件RollingFileAppender
另一种常见的日志输出到文件,随着应用的运行时间越来越长,日志也会增长的越来越多,将他们输出到同一个文件并非一个好办法。RollingFileAppender 用于切分文件日志:
<!--输出到文件-->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/logback.%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
其中重要的是rollingPolicy 的定义,上例中<fileNamePattern>${log.path}/logback.%d{yyyy-MM-dd}.log</fileNamePattern> 定义了日志的切分方式——把每一天的日志归档到一个文件中,<maxHistory>30</maxHistory> 表示只保留最近30天的日志,以防止日志填满整个磁盘空间。同理,可以使用%d{yyyy-MM-dd_HH-mm} 来定义精确到分的日志切分方式。<totalSizeCap>1GB</totalSizeCap> 用来指定日志文件的上限大小,例如设置为1GB的话,那么到了这个值,就会删除旧的日志。
补:如果你想把日志直接放到当前项目下,把${log.path}/ 去掉即可。
子节点二<root>
root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性。
- level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,不能设置为INHERITED或者同义词NULL。
默认是DEBUG。 可以包含零个或多个元素,标识这个appender将会添加到这个logger。
1
2
3
4
| <root level="debug">
<appender-ref ref="console" />
<appender-ref ref="file" />
</root>
|
子节点三<logger>
<logger> 用来设置某一个包或者具体的某一个类的日志打印级别、以及指定<appender> 。<logger> 仅有一个name属性,一个可选的level和一个可选的addtivity属性。
name :用来指定受此logger约束的某一个包或者具体的某一个类。level :用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。如果未设置此属性,那么当前logger将会继承上级的级别。addtivity :是否向上级logger传递打印信息。默认是true。
最佳实践
目标
- 区分Error和Info日志文件
- 设置合理的日志输出格式和日志文件格式
- 设置合理的日志滚动策略(a.单个Appender的日志文件总大小<5MB; b.最久保留10天日志)
- 支持特定类使用特定日志文件输出
Pom引入必要的jar
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.10</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>Spring-boot-log-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Spring-boot-log-demo</name>
<description>Spring-boot-log-demo</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.properties
logging.file.path=log
logback配置文件
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>log-demo</contextName>
<!-- logging.file.path 为application.properties里的配置信息-->
<springProperty scope="context" name="logging.path" source="logging.file.path"/>
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出(配色):%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%yellow(%d{yyyy-MM-dd HH:mm:ss}) %red([%thread]) %highlight(%-5level) %cyan(%logger{50}) - %magenta(%msg) %n
</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!--根据日志级别分离日志,分别输出到不同的文件-->
<appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- error 日志是否在filieInfoLog里重复输出-->
<!-- <filter class="ch.qos.logback.classic.filter.LevelFilter">-->
<!-- <level>ERROR</level>-->
<!-- <onMatch>DENY</onMatch>-->
<!-- <onMismatch>ACCEPT</onMismatch>-->
<!-- </filter>-->
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n
</pattern>
<charset>UTF-8</charset>
</encoder>
<!--滚动策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--按时间保存日志 修改格式可以按小时、按天、月来保存-->
<fileNamePattern>${logging.path}/info.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!--保存时长 10天 %d{yyyy-MM-dd}-->
<MaxHistory>10</MaxHistory>
<!--文件大小-->
<maxFileSize>2MB</maxFileSize>
<!--总大小-->
<totalSizeCap>5MB</totalSizeCap>
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
</appender>
<appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder>
<pattern>
%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n
</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--按时间保存日志 修改格式可以按小时、按天、月来保存-->
<fileNamePattern>${logging.path}/error.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!--保存时长 10天 %d{yyyy-MM-dd}-->
<MaxHistory>10</MaxHistory>
<!--文件大小-->
<maxFileSize>2MB</maxFileSize>
<totalSizeCap>5MB</totalSizeCap>
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
</appender>
<root level="info">
<appender-ref ref="consoleLog"/>
<appender-ref ref="fileInfoLog"/>
<appender-ref ref="fileErrorLog"/>
</root>
<!--特殊功能单独appender 对制定类输出到特定Apperder-->
<appender name="CLASS-APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>
%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n
</pattern>
</encoder>
<!--滚动策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--按时间保存日志 修改格式可以按小时、按天、月来保存-->
<fileNamePattern>${logging.path}/scheduler.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!--保存时长 10天 %d{yyyy-MM-dd}-->
<MaxHistory>10</MaxHistory>
<!--文件大小-->
<maxFileSize>2MB</maxFileSize>
<totalSizeCap>5MB</totalSizeCap>
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
</appender>
<!--这里的name和业务类中的getLogger中的字符串是一样的-->
<logger name="recommend" level="INFO" additivity="true">
<appender-ref ref="CLASS-APPENDER" />
</logger>
</configuration>
特定类使用特定日志文件输出
方法一:使用LoggerFactory.getLogger()制定logger名
Logger logger = LoggerFactory.getLogger("recommend");
针对指定Appender输出,在java文件中需要按下述方式获取对应的logger
@RestController
public class Recommend {
// 重点
Logger logger = LoggerFactory.getLogger("recommend");
@GetMapping("/recommend")
public String index() {
for (int i=0; i<1000; i++){
logger.trace("A TRACE Message");
logger.debug("A DEBUG Message");
logger.info("An INFO Message");
logger.warn("A WARN Message");
logger.error("An ERROR Message");
}
return "Greetings from Spring Boot!";
}
}
方法二:使用Lombok指定Logger
@Slf4j(topic = "recommend")
@RestController
@Slf4j(topic = "recommend")
public class Recommend {
// 重点
Logger logger = LoggerFactory.getLogger("recommend");
@GetMapping("/recommend")
public String index() {
for (int i=0; i<1000; i++){
logger.trace("A TRACE Message");
logger.debug("A DEBUG Message");
logger.info("An INFO Message");
logger.warn("A WARN Message");
logger.error("An ERROR Message");
}
return "Greetings from Spring Boot!";
}
}
Reference
[1]Spring?Boot干货系列:(七)默认日志logback配置解析 | 嘟嘟独立技术
[2]SpringBoot+logback优雅的配置日志!_Mr_chen的博客-CSDN博客_logback日志
[3]?logback 每天生成和大小生成 冲突 TimeBasedRollingPolicy SizeBasedTriggeringPolicy_wujianmin577的专栏-CSDN博客
|