01、项目日志格式
Spring Boot 的默认日志输出类似于以下示例:
2021-12-14 22:40:14.159 INFO 20132 --- [ main] com.kuangstudy.SpringbootApplication : Started SpringbootApplication in 2.466 seconds (JVM running for 3.617)
输出以下项目:
- 日期和时间:毫秒精度且易于排序。
- 日志级别:
ERROR
,WARN
,INFO
,DEBUG
,或TRACE
。 - 进程标识。
- 一个
---
分离器来区分实际日志消息的开始。 - 线程名称:括在方括号中(可能会被截断以用于控制台输出)。
- 记录器名称:这通常是源类名称(通常缩写)。
- 日志消息。
- 换行符
注意:Logback 没有FATAL
级别。它被映射到ERROR
.
02、流行的日志框架
- Java Util Logging
- Logging
- Log4j2
- Logback(默认)
从上图中分析得出 springboot的底层spring-boot-starter-logging可以看出,它依赖的3个日志框架:slf4j(父类)、Logback(实现类)、Log4j2 (实现类)。它们的区别是:
- logback和log4j是日志实现框架,就是实现怎么记录日志的。
- slf4j-api提供了java所有日志框架的简单规范和标准(日志的门面设计模式), 说白了就是一个日志API(没有实现类,里面全是接口),它不能单独使用:故必须结合logback和Log4j2 日志框架来实现。
- springboot的日志搭配。
- spring2.0默认采用slf4j-api + logback的日志搭配,在开发过程中,我们都是采用slf4j的api去记录日志,底层的实现就是根据配置logback和Log4j2 日志框架。
03、Log4j2配置
先来了解一下Log4j2的发展史:
Apache Log4j2 是Log4j的一个升级版本,但是不仅仅是升级,几乎完全进行了重构。旧版本的Log4j,自2015年5月以后就停止对它的更新了。Log4j2是高效的,低延迟的异步日志处理框架,在多线程的场景中,Log4j2的性能是Log4j、LogBack和Logging日志吞吐量的18倍。如何整合如下:
03-01、配置
<!--springboot的web的starter-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 排除掉默认的logging,引入log4j2 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
一般不使用log4j2,一般都是直接引用Lombok,或者直接配置logback.xml文件
03-02、Lombok的日志的定义
支持:@Slf4j (logback) 和 @Log4j2 (log4j2)
推荐使用:@Slf4j
springboot的日志支持
<!--springboot的web的starter-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
我们依赖的web中已经包含了日志logging如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>2.5.7</version>
<scope>compile</scope>
</dependency>
默认情况:springboot默认日志是:logback
传统的定义日志
在每个类中定义日志
private final static Logger log = LoggerFactory.getLogger(AlipayController.class);
private final static Logger log = LoggerFactory.getLogger(LoggerController.class);
使用
log.info("-----info--------");
Lombok的定义日志
package com.kuangstudy.web.log;
import lombok.AllArgsConstructor;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Description:
* Author: yandi Administrator
* Version: 1.0
* Create Date Time: 2021/12/14 15:31.
* Update Date Time:
*
* @see
*/
@RestController
@Slf4j
public class LoggerController {
// private final static Logger log = LoggerFactory.getLogger(LoggerController.class);
@GetMapping("/log1")
public String log1() {
log.info("-----info--------");
return "log1";
}
}
04、LogBack配置(默认)
概述分析
默认情况下,如果您使用“Starters”,则使用 Logback 进行日志记录。还包括适当的 Logback 路由,以确保使用 Java Util Logging、Commons Logging、Log4J 或 SLF4J 的依赖库都能正常工作。
具体操作如下:
package com.kuangstudy.web.log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Description:
* Author: yandi Administrator
* Version: 1.0
* Create Date Time: 2021/12/14 15:31.
* Update Date Time:
*
* @see
*/
@RestController
public class LoggerController {
private final static Logger log = LoggerFactory.getLogger(LoggerController.class);
@GetMapping("/log1")
public String log1() {
log.trace("--------------------trace");
log.debug("--------------------debug");
log.info("--------------------info");
log.warn("--------------------warn");
log.error("--------------------error");
return "log1";
}
}
打印可以看出来只打印了:
2021-12-14 15:33:59.473 INFO 15424 --- [nio-8083-exec-1] com.kuangstudy.web.log.LoggerController : --------------------info
2021-12-14 15:33:59.474 WARN 15424 --- [nio-8083-exec-1] com.kuangstudy.web.log.LoggerController : --------------------warn
2021-12-14 15:33:59.474 ERROR 15424 --- [nio-8083-exec-1] com.kuangstudy.web.log.LoggerController : --------------------error
因为默认情况下:springboot的默认日志级别是:info
# 修改日志级别
logging:
level:
root: info
可以在application.yml配置如下:
# 指定包的日志级别logging: level: com.kuangstudy.web.log: trace
打印如下:
36:38.651 TRACE 4208 --- [nio-8083-exec-1] com.kuangstudy.web.log.LoggerController : --------------------trace2021-12-14 15:36:38.651 DEBUG 4208 --- [nio-8083-exec-1] com.kuangstudy.web.log.LoggerController : --------------------debug2021-12-14 15:36:38.651 INFO 4208 --- [nio-8083-exec-1] com.kuangstudy.web.log.LoggerController : --------------------info2021-12-14 15:36:38.651 WARN 4208 --- [nio-8083-exec-1] com.kuangstudy.web.log.LoggerController : --------------------warn2021-12-14 15:36:38.651 ERROR 4208 --- [nio-8083-exec-1] com.kuangstudy.web.log.LoggerController : --------------------error
总结
-
debug是开发首选,是一种明细的日志级别,可以看到框架加载类的所有过程,如果你要进行源码分析,查看SQL执行的过程,框架的执行的过程,肯定是用debug
# 修改日志级别logging: level: root: debug com.kuang.order: info
-
info: 只会打印常见的日志信息
-
error: 只会打印错误日志信息,一般在生产环境中进行设定。因为项目开发测试完毕,肯定在线上肯定只关注错误,如果你其他也关注,可以单独设定。
# 修改日志级别logging: level: root: error com.kuang.order: info
05、springboot具体配置日志
默认springboot的日志是:logback , 但是只会输出Error、Warn和Info级别的日志信息。可以修改日志的级别来控制:
05-01、日志级别
logging:
level:
# 设置日志的默认级别为 info
root: info
# 设置com.kuantstudy.log包下的日志级别是 debug
com.kuantstudy.log: debug
05-02、日志文件
在实际开发中,特别是在生产环境中,你不可能一直看着控制台,而且日志会非常的大,瞬间就丢失了。因此我们需要把日志存储在指定的目录或者文件中:
指令目录输入日志文件(不推荐)
告诉springboot使用logback生成的日志,除了在控制打印一份,同时往这个目录outputs/logs的中生成一个spring.log文件中生成一份。
一句话:目录的指定,只是告诉spring.log放在哪里。
logging: # 指定日志输出的目录 file: path: outputs/logs
如下:
指定以后,会默认生成 spring.log
文件
指定日志文件输出(推荐)
logging: file: name: G://outputs/logs/springboot.log
06、完整的logback-spring.xml
06-01、application-dev.yml配置
myapp:
logpath: G:/logs/springboot/
06-02、application-prod.yml配置
myapp:
logpath: /logs/springboot/
06-03、logback-spring-xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<springProperty scope="context" name="LOG_PATH" source="myapp.logpath" defaultValue="mylogxxxx"/>
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<!-- 文件日期滚动记录 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--初始文件名-->
<file>${LOG_PATH}/log_file.log</file>
<!--日志是否追加 true-->
<append>true</append>
<!--可以支持并发-->
<prudent>true</prudent>
<!--开始定义日志的回滚策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件每天生成的格式是-->
<fileNamePattern>${LOG_PATH}/log_file_%d{yyyy-mm-dd.HH.MM}.log</fileNamePattern>
<!-- maxHistory日志保留时长,30天或者30分钟,看上面的格式是怎么设置的(现在是分钟)。超过天数后删除日志文件,同时配套目录一起删除。该参数不一定是指天数,也可以是月份数。
具体参考滚动规则fileNamePattern,看是依赖什么进行滚动的-->
<maxHistory>30</maxHistory>
<!-- totalSizeCap最大日志量-->
<totalSizeCap>100MB</totalSizeCap>
</rollingPolicy>
<!--滚动触发规则-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<!--maxFileSize单个文件最大量,如果达到这个最大量。日志有可能会出现报错,新日志无法存入。-->
<maxFileSize>1MB</maxFileSize>
</triggeringPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>【FILE】 %green(%d{yyyy-MM-dd HH:mm:ss:SSS}) [%thread] [%-5level] %logger %M %L - %msg%n</pattern>
</encoder>
<!-- 把error以下级别的日志过滤掉 info < warn <error -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
</appender>
<!--日志异步到数据库,连接数据库,数据库中药创建相应的表 -->
<appender name="DBAPPENDER" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">
<dataSource class="com.zaxxer.hikari.HikariDataSource">
<driverClassName>com.mysql.jdbc.Driver</driverClassName>
<jdbcUrl>jdbc:mysql://localhost:3306/kss-web-db?useUnicode=true&characterEncoding=utf8&useSSL=false</jdbcUrl>
<username>root</username>
<password>mkxiaoer</password>
<poolName>HikariPool-logback</poolName>
</dataSource>
</connectionSource>
</appender>
<!-- 控制台 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<!--格式化输出:%d:表示日期 %thread:表示线程名 %-5level:级别从左显示5个字符宽度 %msg:日志消息 %n:是换行符-->
<pattern>【KSD - CONSOLE】 %clr(%d{yyyy-MM-dd HH:mm:ss:SSS}){yellow} %clr([%thread]){green} %blue([%-5level]) %logger %M %L - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 把error以下级别的日志过滤掉 info < warn <error -->
<!-- <filter class="ch.qos.logback.classic.filter.ThresholdFilter">-->
<!-- <level>ERROR</level>-->
<!-- <level>INFO</level>-->
<!-- </filter>-->
</appender>
<!-- 把日志异步输出到磁盘文件中,避免每次都进行磁盘IO操作,(每次100条写入一次) -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>100</queueSize>
<appender-ref ref="FILE" />
</appender>
<!-- 不同的环境不同的配置 dev -->
<springProfile name="dev">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
<!-- 不同的环境不同的配置 prod -->
<springProfile name="prod">
<root level="ERROR">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ASYNC"/>
<appender-ref ref="DBAPPENDER"/>
</root>
<!-- 指定包的级别 -->
<logger name="com.kuangstudy.order" level="INFO">
<appender-ref ref="FILE"/>
<appender-ref ref="CONSOLE"/>
<appender-ref ref="DBAPPENDER"/>
</logger>
</springProfile>
</configuration>
06-04、日志入库应该创建的表
BEGIN;
DROP TABLE IF EXISTS logging_event_property;
DROP TABLE IF EXISTS logging_event_exception;
DROP TABLE IF EXISTS logging_event;
COMMIT;
BEGIN;
CREATE TABLE logging_event
(
timestmp BIGINT NOT NULL,
formatted_message TEXT NOT NULL,
logger_name VARCHAR(254) NOT NULL,
level_string VARCHAR(254) NOT NULL,
thread_name VARCHAR(254),
reference_flag SMALLINT,
arg0 VARCHAR(254),
arg1 VARCHAR(254),
arg2 VARCHAR(254),
arg3 VARCHAR(254),
caller_filename VARCHAR(254) NOT NULL,
caller_class VARCHAR(254) NOT NULL,
caller_method VARCHAR(254) NOT NULL,
caller_line CHAR(4) NOT NULL,
event_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
);
COMMIT;
BEGIN;
CREATE TABLE logging_event_property
(
event_id BIGINT NOT NULL,
mapped_key VARCHAR(254) NOT NULL,
mapped_value TEXT,
PRIMARY KEY(event_id, mapped_key),
FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
);
COMMIT;
BEGIN;
CREATE TABLE logging_event_exception
(
event_id BIGINT NOT NULL,
i SMALLINT NOT NULL,
trace_line VARCHAR(254) NOT NULL,
PRIMARY KEY(event_id, i),
FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
);
COMMIT;