一、Slf4j日志框架
1.1 Slf4j日志使用
- SpringBoot环境搭建之后将默认集成slf4j日志框架,通过下面方式获取
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@RestController
public class LogInfoController {
private static final Logger LOGGER = LoggerFactory.getLogger(LogInfoController.class);
}
- 日志记录有5个级别,优先级由低到高
- logger.trace("trace 消息");
- logger.debug("debug 消息");
- logger.info("info 消息");
- logger.warn("warn 消息");
- logger.error("error 消息");
- SpringBoot底层已经实现了框架,直接使用即可
- SpringBoot默认输出info级以上级别的消息,如果是main则是debug及以上日志
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogUserCore {
private static final Logger LOGGER = LoggerFactory.getLogger(LogUserCore.class);
public static void main(String[] args) {
LOGGER.trace("trace日志");
LOGGER.debug("debug日志");
LOGGER.info("info日志,可以使用括号作为占位符,内容【{}】", "info日志");
LOGGER.warn("warn日志");
try {
Integer.parseInt("a");
} catch (NumberFormatException e) {
/// LOGGER.error("error日志,错误信息【{}】,最后面可以直接放异常", e.getMessage(), e);
LOGGER.error("error日志,错误信息【{}】,最后面可以直接放异常", e.getMessage());
}
}
}
- logback整体类图如下
1.2 Logback配置说明
1.2.1 configuration
- configuration是logback的父节点,它只有三个属性
- scan:自动加载判断,当配置文件发生改变时,将会被重新加载,默认为true
- scanPeriod:检测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认为毫秒,当scan=true时这个值生效,默认时间间隔为1分钟,可以配置单位
- debug:当被设置为true时,将打印出logback内部日志信息,实时查看logback运行信息,默认为false
1.2.2 logger
- logger用来设置某一个包或者某一个具体类的日志打印级别以及方式
- 在logger内可以包含零个或者多个appender-ref,然后对应的appender将会被添加到这个logger
- 它的属性如下
- name:指定对应的包路径或者类路径
- level:用来设置日志打印的级别,五个常用打印级别从低至高依次为TRACE、DEBUG、INFO、WARN、ERROR,如果未设置此级别,那么当前logger会继承上级的级别
- additivity:是否向上级log传递打印信息,默认为true
1.2.3 root
- root也是logger元素,但它是根logger,所有logger的最上级,只有一个level属性,它的name固定为ROOT
1.2.4 appender
- appender是configuration的子节点,它的作用是定义日志输出的方式,它有2个必要的属性
- name:指定当前appender的名称
- class:指定appender对应的类,控制日志输出方式
1.2.5 encoder
- encoder主要负责将日志信息转换成字节数组以及将字节数组写到输出流中
- encoder常用转换符有如下
%logger {length}
- 输出日志的logger名,可有一个整形参数,功能是缩短logger名,设置为0表示只输入logger最右边点符号之后的字符串 | Conversion Pattern | Logger name | Result | | --- | --- | --- | | %logger | mainPackage.sub.sample.Bar | mainPackage.sub.sample.Bar | | %logger{0} | mainPackage.sub.sample.Bar | Bar | | %logger{5} | mainPackage.sub.sample.Bar | m.s.s.Bar | | %logger{10} | mainPackage.sub.sample.Bar | m.s.s.Bar | | %logger{15} | mainPackage.sub.sample.Bar | m.s.sample.Bar | | %logger{16} | mainPackage.sub.sample.Bar | m.sub.sample.Bar | | %logger{26} | mainPackage.sub.sample.Bar | mainPackage.sub.sample.Bar |
%d{pattern}
- 输出日志的打印日志,模式语法与java.text.SimpleDateFormat 兼容 | Conversion Pattern | Result | | --- | --- | | %d | 2021-06-20 14:06:49,812 | | %date | 2021-06-20 14:06:49,812 | | %date{ISO8601} | 2021-0620 14:06:49,812 | | %date{HH:mm:ss.SSS} | 15:06:49.812 | | %date{dd MMM yyyy ;HH:mm:ss.SSS} | 20 oct. 2006;14:06:49. |
%msg
- 输出应用程序提供的信息
%level
- 输出日志级别
%thread
- 输出产生日志的线程名
%n
- 输出平台相关的分行符"\n"或者"\r\n"
%ex
- 输出异常信息
1.2.6 filter
- 是appender的子节点,表示对当前给定的日志级别再进行一次过滤,配置方式
<!-- 只打印错误日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
1.3 Logback框架配置
- 新建logback配置文件,增加下面的配置
- 配置文件中也可以针对包指定日志级别,但是通常使用配置文件
logging:
level:
com.codecoord.springboot.practice.log.info: error
- 可以在配置文件中指定日志配置文件位置,然后在日志配置文件中配置日志等策略,配置解释如下
<?xml version="1.0" encoding="UTF-8"?>
<!-- 是否开启debug和扫描周期 -->
<configuration debug="true" scan="true" scanPeriod="1 seconds">
<contextName>logback</contextName>
<!--定义参数,后面可以通过${name}使用-->
<property name="log_pattern" value="%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n%ex"/>
<!-- 定义日志路径 -->
<property name="log_path" value="log"/>
<!-- 最大历史天数为10天 -->
<property name="log_file_max_history" value="10"/>
<!-- 单个日志最大大小 -->
<property name="log_file_max_size" value="1KB"/>
<!-- 最大日志文件大小 -->
<property name="log_file_max_total_size" value="1MB"/>
<!-- ConsoleAppender 用于在屏幕上输出日志 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- 定义了一个过滤器,在指定LEVEL之下的日志输出不会被打印出来 -->
<!-- 这里定义了ERROR,也就是控制台不会输出比ERROR级别小的日志 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<!--定义控制台输出格式-->
<encoder>
<pattern>${log_pattern}</pattern>
</encoder>
</appender>
<!-- 基于文件appender,不支持滚动记录 -->
<appender name="FILE_APPENDER" class="ch.qos.logback.core.FileAppender">
<file>${log_path}/fileAppender.log</file>
<append>true</append>
<!-- 将immediateFlush设置为false以获得更高的日志吞吐量 -->
<immediateFlush>true</immediateFlush>
<encoder>
<pattern>${log_pattern}</pattern>
</encoder>
</appender>
<!-- 基于时间滚动,注意这个基于时间(日)滚动 -->
<appender name="ROLLING_FILE_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log_path}/timeBasedRollingPolicy.log</file>
<!-- true:事件追加到现有文件的末尾。false:任何现有文件都会被截断。默认设置为true -->
<append>true</append>
<!--定义日志滚动的策略,TimeBasedRollingPolicy:不支持%i自动滚动增加序号 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--定义文件滚动时的文件名的格式-->
<fileNamePattern>${log_path}/timeBasedRollingPolicy.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 最大历记录 -->
<maxHistory>60</maxHistory>s
<!-- 该属性在 1.1.6版本后 才开始支持,总文件大小,需要设置maxHistory属性 -->
<totalSizeCap>10GB</totalSizeCap>
<!-- 是否启动时清除历史数据 -->
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder>
<pattern>${log_pattern}</pattern>
</encoder>
</appender>
<!-- 基于时间滚动,根据文件大小滚动,适用于一天会产生多个日志情况,推荐使用 -->
<appender name="ROLLING_FILE_TIME_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log_path}/system/sizeAndTime.log</file>
<append>true</append>
<!--定义日志滚动的策略,SizeAndTimeBasedRollingPolicy:支持%i自动滚动增加序号 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--定义文件滚动时的文件名的格式-->
<fileNamePattern>${log_path}/system/sizeAndTime.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 单个文件最大大小,超过自动轮转,保存60天但是不超过10G -->
<maxFileSize>1KB</maxFileSize>
<!-- 最大历记录,通常和totalSizeCap配合使用 -->
<maxHistory>60</maxHistory>
<!-- 总文件大小,需要设置maxHistory属性 -->
<totalSizeCap>10GB</totalSizeCap>
<!-- 是否启动时清除历史数据 -->
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder>
<pattern>${log_pattern}</pattern>
</encoder>
</appender>
<!-- root是默认的logger,当logger没有配置appender是使用此处配置的输出 -->
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
<!-- 对于类路径以com.codecoord.springboot.practice.log.info开头的Logger,输出级别设置为info -->
<!-- 这个logger没有指定appender,它会继承root节点中定义appender,由于root的appender配置debug级别,此处不输出 -->
<!--<logger name="com.codecoord.springboot.practice.log.info" level="info"/>-->
<!-- additivity为false表示不要使用rootLogger配置的appender进行输出 -->
<!-- 由于设置了additivity="false",所以输出时不会使用rootLogger的appender -->
<!-- 如果没有设置 additivity="false",可能导致一条日志在控制台输出两次的情况 -->
<!--<logger name="com.codecoord.springboot.practice.log.info" level="INFO" additivity="false">
<appender-ref ref="FILE_APPENDER"/>
</logger>-->
<!-- 基于时间滚动日志记录 -->
<logger name="com.codecoord.springboot.practice.log.info" level="INFO" additivity="false">
<appender-ref ref="ROLLING_FILE_TIME_APPENDER"/>
</logger>
<!-- 这个logger本身没有配置appender,配置了additivity="false",所以logger输出日志不会输出到任何地方 -->
<!--<logger name="com.codecoord.springboot.practice.log.error" level="error" additivity="false"/>-->
</configuration>
- 新建几个定时任务,用于定时输出日志,其中一个类输出如下
import java.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@EnableScheduling
@Component
public class LogDebugSchedule {
private static final Logger LOGGER = LoggerFactory.getLogger(LogDebugSchedule.class);
@Scheduled(cron = "0/1 * * * * ?")
public void logDebug() {
LOGGER.debug("logDebug任务执行,当前时间【{}】", LocalDateTime.now());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true" scan="true" scanPeriod="1 seconds">
<contextName>logback</contextName>
<!--定义参数,后面可以通过${name}使用-->
<property name="log_pattern" value="%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n%ex"/>
<!-- 定义日志路径 -->
<property name="log_path" value="log"/>
<!-- 最大历史天数 -->
<property name="log_file_max_history" value="1"/>
<!-- 单个日志最大大小 -->
<property name="log_file_max_size" value="10MB"/>
<!-- 最大日志文件大小 -->
<property name="log_file_max_total_size" value="1MB"/>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>${log_pattern}</pattern>
</encoder>
</appender>
<!-- 基于时间滚动,根据文件大小滚动,适用于一天会产生多个日志情况 -->
<appender name="ROLLING_FILE_TIME_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log_path}/sizeAndTime.log</file>
<!-- 追加记录 -->
<append>true</append>
<!-- 定义日志滚动的策略,SizeAndTimeBasedRollingPolicy:支持%i自动滚动增加序号 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 定义文件滚动时的文件名的格式-->
<fileNamePattern>${log_path}/system/sizeAndTime.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 单个文件最大大小,超过自动轮转,保存60天但是不超过10G -->
<maxFileSize>$log_file_max_size}</maxFileSize>
<!-- 最大历记录,通常和totalSizeCap配合使用 -->
<maxHistory>${log_file_max_history}</maxHistory>
<!-- 总文件大小,需要设置maxHistory属性 -->
<totalSizeCap>${log_file_max_total_size}</totalSizeCap>
<!-- 是否启动时清除历史数据 -->
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder>
<pattern>${log_pattern}</pattern>
</encoder>
</appender>
<!-- 日志采集 -->
<logger name="com.codecoord.springboot.elasticsearch.logstash" level="INFO" additivity="false">
<appender-ref ref="ROLLING_FILE_TIME_APPENDER"/>
</logger>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
1.4 Logback高级配置
- logback除了可以使用核心内部变量外,可以通过MDC来实现自定义参数
- MDC是一个静态类,Logback实现的MDC用ThreadLocal实现,所以用完之后需要清除MDC数据
package org.slf4j;
public class MDC {
//Put a context value as identified by key
//into the current thread's context map.
public static void put(String key, String val);
//Get the context identified by the key parameter.
public static String get(String key);
//Remove the context identified by the key parameter.
public static void remove(String key);
//Clear all entries in the MDC.
public static void clear();
}
- 在代码中通过**MDC.put("key", "value")放入数据,然后在配置文件中通过%X{key}**使用
- 代码示例
import java.time.LocalDateTime;
import java.util.Random;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@EnableScheduling
@Component
public class LogstashLogbackAggregation {
private static final Logger LOGGER = LoggerFactory.getLogger(LogstashLogbackAggregation.class);
private static final Random RANDOM = new Random();
@Scheduled(cron = "0/3 * * * * ?")
public void logbackMdc() {
MDC.put("code", UUID.randomUUID().toString());
MDC.put("createTime", LocalDateTime.now().toString());
MDC.put("content", "logstash日志采集错误" + LocalDateTime.now());
LOGGER.info("logstash日志采集,当前时间【{}】", LocalDateTime.now());
MDC.clear();
System.out.println(String.format("logstash日志采集,当前时间【%s】", LocalDateTime.now()));
}
}
- logback配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="true" scanPeriod="1 seconds">
<contextName>logback</contextName>
<!--定义参数,后面可以通过${name}使用-->
<property name="log_pattern" value="%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n%ex"/>
<!-- 定义日志路径 -->
<property name="log_path" value="log"/>
<!-- 最大历史天数 -->
<property name="log_file_max_history" value="1"/>
<!-- 单个日志最大大小 -->
<property name="log_file_max_size" value="10MB"/>
<!-- 最大日志文件大小 -->
<property name="log_file_max_total_size" value="1GB"/>
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>${log_pattern}</pattern>
</encoder>
</appender>
<!-- 基于时间滚动,根据文件大小滚动,适用于一天会产生多个日志情况 -->
<appender name="ROLLING_FILE_TIME_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log_path}/sizeAndTime.log</file>
<!-- 追加记录 -->
<append>true</append>
<!-- 定义日志滚动的策略,SizeAndTimeBasedRollingPolicy:支持%i自动滚动增加序号 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 定义文件滚动时的文件名的格式-->
<fileNamePattern>${log_path}/system/sizeAndTime.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- 单个文件最大大小,超过自动轮转 -->
<maxFileSize>${log_file_max_size}</maxFileSize>
<!-- 最大历记录,通常和totalSizeCap配合使用 -->
<maxHistory>${log_file_max_history}</maxHistory>
<!-- 总文件大小,需要设置maxHistory属性 -->
<totalSizeCap>${log_file_max_total_size}</totalSizeCap>
<!-- 是否启动时清除历史数据 -->
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder>
<pattern>${log_pattern}</pattern>
</encoder>
</appender>
<!-- MDC -->
<appender name="ROLLING_FILE_TIME_APPENDER_MDC" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log_path}/mdc.log</file>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log_path}/system/mdc.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<maxFileSize>${log_file_max_size}</maxFileSize>
<maxHistory>${log_file_max_history}</maxHistory>
<totalSizeCap>${log_file_max_total_size}</totalSizeCap>
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
<encoder>
<pattern>%d{HH:mm:ss.SSS} 单号【%X{code}】上传时间【%X{createTime}】内容【%X{content}】 %msg%n</pattern>
</encoder>
</appender>
<!-- 日志记录器 -->
<logger name="com.codecoord.springboot.elasticsearch.logstash" level="INFO" additivity="false">
<appender-ref ref="ROLLING_FILE_TIME_APPENDER_MDC"/>
</logger>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
1.5 日志错误处理
Empty watch file list. Disabling
- 由于配置中开启了扫描,但是无法扫描jar包中的信息,所以保存,把configuration的debug移除即可
- 问题配置
<configuration debug="true" scan="true" scanPeriod="1 seconds">
- 调整配置
<configuration scan="true" scanPeriod="30 seconds">