Spring Boot 日志文件
日志文件是用于记录系统操作事件的记录文件或文件集合,可分为事件日志和消息日志。具有处理历史数据、诊断问题的追踪以及理解系统的活动等重要作用。
事件日志记录系统的执行中发生的事件,以便提供可用于理解系统的活动和诊断问题的跟踪。它们对理解复杂系统的活动至关重要,特别是在用户交互较少的应用程序中。
互联网中继聊天(IRC),即时消息(IM)程序,具有聊天功能的对等文件共享客户端和多人游戏(特别是MMORPG)通常具有自动记录(即保存)文本通信的能力。消息日志几乎是通用的纯文本文件,但是IM和VoIP客户端(其支持文本聊天,例如Skype)可以将它们保存在HTML文件中或以自定义格式以便于阅读和加密。
日志级别
几种常见的日志级别由低到高分为:TRACE < DEBUG < INFO < WARN < ERROR < FATAL
如果项目中的日志级别设置为INFO,那么比它更低级别的日志信息就看不到,即TRACE、DEBUG日志将不会显示。
日志框架
Log4j,Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
Logback,Logback是由log4j创始人设计的又一个开源日志组件。logback当前分成三个模块:logback-core, logback-classic和logback-access。logback-corre是其它两个模块的基础模块。logback相比于log4j性能提升了很多,初始化内存加载也更小了,并且作为Spring Boot 的默认日志框架。
Log4j2,Log4j2是对Log4j的升级版本,参考了logback的一些优秀设计,并且修复了一些问题,因此带来一些重大的提升:1.异常处理,在logback中,Appender中的异常不会被应用感知到,但是在log4j2中,提供了一些异常处理机制;2.性能提升,log4j2相较于log4j和logback都具有明显的性能提升;3.自动重载配置,在生产上可以动态的修改日志的级别而不需要重启应用;4.无垃圾机制,log4j2在大部分情况下,都可以使用其设计的一套无垃圾机制【对象重用、内存缓冲】,避免频繁的日志收集导致的jvm gc。
Spring Boot 日志框架
Spring Boot 默认的日志框架是 logback,原则上使用logback需要添加以下依赖,但是Spring Boot使用其作为默认框架,在引入spring boot依赖的时候就引入了logback依赖了,不用再手动引入。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
Spring Boot 中默认的日志级别是INFO,使用初始化日志配置时启动日志打印如下:
2023-01-12 15:13:49.519 INFO 44380 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication using Java 11.0.16 on user with PID 44380 (E:\java\demo\target\classes started by user in E:\java\demo)
2023-01-12 15:13:49.522 INFO 44380 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to 1 default profile: "default"
2023-01-12 15:13:50.020 INFO 44380 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8888 (http)
2023-01-12 15:13:50.026 INFO 44380 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-01-12 15:13:50.026 INFO 44380 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.69]
2023-01-12 15:13:50.083 INFO 44380 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2023-01-12 15:13:50.083 INFO 44380 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 533 ms
2023-01-12 15:13:50.293 INFO 44380 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8888 (http) with context path ''
2023-01-12 15:13:50.301 INFO 44380 --- [ main] com.example.demo.DemoApplication : Started DemoApplication in 1.064 seconds (JVM running for 1.679)
默认输出的日志元素为:
- 日期时间:精确到毫秒
- 日志级别:INFO
- 进程ID
- 分隔符:- - - 标识实际日志的开始
- 线程名:方括号包裹
- Logger名:通常是源代码的类名
- 日志内容
代码中使用日志
方式一:
也就是最原始的方式,在每一个类里面添加如下代码:
private final Logger logger = LoggerFactory.getLogger(JavaClass.class);
// 使用logger记录日志
logger.info("这里是记录的日志内容");
这种方式显然比较鸡肋,如果每个类中都添加以下岂不是很low
方式二:
引入lombok依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
在类上加入注解 @Slf4j
@Slf4j
@RestController
@RequestMapping("demo")
public class DemoController {
@Autowired
private UserInfo userInfo;
@GetMapping("test")
public String demoTest() {
log.info("访问url: http://localhost:8888/demo/test");
return JSON.toJSONString(userInfo);
}
}
定义日志级别
spring boot 的默认日志级别是INFO,但可以自己在配置文件中修改日志级别:
logging:
level: debug
上面是将所有的日志级别都改成了debug,spring boot还支持package级别的日志级别调整,格式为:logging.level.xxx=xxx
logging:
level:
com.example.demo: error
日志输出到文件中
Spring Boot 中日志默认是输出到控制台的,但是生产环境中这样显示肯定是不行的,因此需要配置日志输出到日志文件中。
logging:
file:
# 日志文件的路径(当前项目路径的logs文件夹下)
path: ./logs
# 日志文件名,默认为spring,log
name:
注意:官方文档说 path 和 name 两个属性不能同时配置,否则不生效,因此只需要配置一个即可。
日志格式
logging:
pattern:
console: "%d{yyyy/MM/dd-HH:mm:ss} [%thread] %-5level %logger- %msg%n"
file: "%d{yyyy/MM/dd-HH:mm:ss} [%thread] %-5level %logger- %msg%n"
- %d{yyyy/MM/dd-HH:mm:ss} --- 日志输出日期时间格式,如 2023-01-12 15:50:00
- %thread --- 输出日志的进程名字,这在Web应用以及异步任务处理中很有用
- %-5level --- 日志级别,并且使用5个字符靠左对齐
- %logger --- 日志输出者的名字
- %msg --- 日志信息
- %n --- 平台的换行符
自定义日志配置
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
当然,如果不想使用spring boot 推荐的名字,也可以在配置文件中指定配置文件的名称即可:
logging.config=classpath:custom-log-file.xml
Spring Boot 官方推荐优先使用带有-spring的文件名作为日志配置,因此只需要在 src/resources 文件夹下创建 logback-spring.xml 文件即可,配置文件内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!--定义日志存放目录-->
<property name="logPath" value="logs"/>
<!--日志输出的格式-->
<property name="PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t-%L] %-5level %logger{36} %L %M - %msg%xEx%n"/>
<contextName>logback</contextName>
<!-- 输出到控制台 -->
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
<!-- 展示格式 layout -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>${PATTERN}</pattern>
</layout>
<!-- 过滤器,只要过滤到指定级别的日志信息才会输出,如果level为ERROR,控制台只会输出ERROR日志 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
</appender>
<!-- 输出到文件 -->
<appender name="fileDEBUGLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 如果只是想要info级别的日志,只是过滤info还是会输出error日志,因为error级别高,使用以下策略会避免输出error日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤error -->
<level>Error</level>
<!-- 匹配到就禁止 -->
<onMatch>DENY</onMatch>
<!-- 没匹配到就允许 -->
<onMismatch>ACCEPT</onMismatch>
</filter>
<!-- 日志名称,如果没有File属性,只会使用FileNamePattern的文件路径规则 -->
<!-- 如果同时又File 和 FileNamePattern,当天日志是File,明天会把今天的日志改为今天的日期,File的日志都是当天的 -->
<File>${logPath}/log_demo.log</File>
<!-- 滚动策略,按照时间滚动 TimeBaseRollingPolicy -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 文件路径,定义了日志的切分方式,把每天的日志归档到一个文件中,以防止日志填满整个磁盘空间 -->
<FileNamePattern>${logPath}/log_demo_%d(yyyy-MM-dd).log</FileNamePattern>
<!-- 只保留最近90天的日志 -->
<maxHistory>90</maxHistory>
<!-- 用来指定日志文件的上限大小,到了这个值,就会删除旧的日志 -->
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<!-- 日志编码输出格式化 -->
<encoder>
<charset>UTF-8</charset>
<pattern>${PATTERN}</pattern>
</encoder>
</appender>
<!-- 输出error日志到指定文件中 -->
<appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 如果只是想要Error级别的日志,需要过滤一下,默认是info级别的-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>Error</level>
</filter>
<File>${logPath}</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${logPath}/error_%d{yyyy-MM-dd}.log</FileNamePattern>
<maxHistory>90</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
</rollingPolicy>
<encoder>
<charset>UTF-8</charset>
<pattern>${PATTERN}</pattern>
</encoder>
</appender>
<!--指定最基础的日志输出级别-->
<root level="DEBUG">
<!--appender将会添加到这个logger-->
<appender-ref ref="consoleLog"/>
<appender-ref ref="fileDEBUGLog"/>
<appender-ref ref="fileErrorLog"/>
</root>
<!--定义指定package的日志级别-->
<logger name="org.springframework" level="DEBUG"/>
<logger name="org.mybatis" level="DEBUG"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<logger name="io.lettuce.*" level="INFO"/>
<logger name="io.netty.*" level="DEBUG"/>
<logger name="com.rabbitmq.*" level="DEBUG"/>
<logger name="org.springframework.amqp.*" level="DEBUG"/>
<logger name="org.springframework.scheduling.*" level="DEBUG"/>
<!--定义com.xx.xx包下的日志信息不上传,直接输出到fileDEBUGLog和fileErrorLog这两个appender中,日志级别为DEBUG-->
<logger name="com.example.demo" additivity="false" level="DEBUG">
<appender-ref ref="fileDEBUGLog"/>
<appender-ref ref="fileErrorLog"/>
</logger>
</configuration>
各配置节点含义:
configuration(根节点)
- scan: 当此属性为true时,配置文件如果发生改变,将会被重新加载,默认为true
- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认时间间隔为1分钟
- debug:当此属性为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false
root
这是一个必须的节点,用来指定基础的日志级别,只有一个level属性,默认值是DEBUG
该节点可以包含零个或多个元素,字节点是 appender-ref, 标记这个 appender 将会添加到这个 logger
contextName
标识一个上下文名称,默认为 default, 一般用不到
property
标记一个上下文变量,属性有 name 和 value,定义变量之后可以使用 ${} 来获取
appender
用来格式化日志输出节点,有两个属性 name 和 class,class 用来指定哪种输出策略,常用就是控制台输出策略和文件输出策略
这个节点很重要,通常的日志文件需要定义三个 appender,分别是控制台输出、常规日志文件输出和异常日志文件输出
该节点有几个重要子节点:
- filter:日志输出拦截器,没有特殊定制一般使用系统自带的即可,但是如果要将日志分开,比如将 ERROR 级别的日志输出到一个文件中,将除了 ERROR 级别的日志输出到另外一个文件中,此时就要拦截 ERROR 级别的日志了
- encoder:和 pattern 节点组合用于具体输出的日志格式和编码格式
- file:节点用来指明日志文件的输出位置,可以是绝对路径也可以是相对路径
- rollingPolicy:日志回滚策略,在这里我们用了 TimeBasedRollingPolicy 基于时间的回滚策略有以下子节点 fileNamePattern(必要节点),可以用来设置指定时间的日志归档
- maxHistory:可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件,例如设置为30的话,30天之后旧的日志就会被删除
- totalSizeCap:可选节点,用来指定日志文件的上限大小,例如设置为3GB的话,那么到了这个值,就会删除旧的日志
logger
可选节点,用来具体指明包的日志输出级别,它将会覆盖 root 的输出级别
该节点有几个重要子节点:
- name:指定包名
- level:可选,日志的级别
- addtivity:可选,默认为 true,将此 logger 的信息向上级传递,将有 root 节点定义日志打印。如果设置为 false,将不会上传,此时需要定义一个 appender-ref 节点才会输出
日志切换
前面介绍的日志框架都是基于日志门面 SLF4j 即简单日志门面(Simple Logging Facade for Java),SLF4j 并不是一个真正的日志实现,而是一个抽象层,它允许你在后面使用任意一个日志实现
使用了 slf4j 后,对于应用程序来说,无论底层的日志框架如何变,应用程序不需要修改任意一行代码,就可以直接上线了