前言
SpringBoot对所有内部日志使用通用日志记录,但保留底层日志实现。为Java Util Logging、Log4J2和Logback提供了默认配置。在不同的情况下,日志记录器都预先配置为使用控制台输出,同时还提供可选的文件输出。默认情况下,SpringBoot使用Logback进行日志记录。
日志级别有(从高到低):FATAL(致命),ERROR(错误),WARN(警告),INFO(信息),DEBUG(调试),TRACE(跟踪)或者 OFF(关闭),默认的日志配置在消息写入时将消息回显到控制台。默认情况下,将记录错误级别、警告级别和信息级别的消息。
PS:
Logback does not have a FATAL level. It is mapped to ERROR.
Logback没有FATAL致命级别。它被映射到ERROR错误级别。
详情请戳官方文档:https://docs.spring.io/spring-boot/docs/2.1.5.RELEASE/reference/htmlsingle/#boot-features-logging
本文主要记录Logback日志输出到文件以及实时输出到web页面
输出到文件
我们创建SpringBoot项目时,spring-boot-starter已经包含了spring-boot-starter-logging,不需要再进行引入依赖
标准日志格式
2014-03-05 10:57:51.112 INFO 45469 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/7.0.52
2014-03-05 10:57:51.253 INFO 45469 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2014-03-05 10:57:51.253 INFO 45469 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1358 ms
2014-03-05 10:57:51.698 INFO 45469 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2014-03-05 10:57:51.702 INFO 45469 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
- Date and Time:日期和时间:毫秒精度,易于排序。
- Log Level:日志级别:错误、警告、信息、调试或跟踪。
- Process ID:进程ID。
- —:分隔符,用于区分实际日志消息的开始。
- Thread name:线程名称,括在方括号中(可能会被截断以用于控制台输出)。
- Logger name:日志程序名称,这通常是源类名称(通常缩写)。
- The log message:日志消息。
如何打印日志?
方法1
/**
* 配置内部类
*/
@Controller
@Configuration
class Config {
/**
* 获取日志对象,构造函数传入当前类,查找日志方便定位
*/
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Value("${user.home}")
private String userName;
/**
* 端口
*/
@Value("${server.port}")
private String port;
/**
* 启动成功
*/
@Bean
public ApplicationRunner applicationRunner() {
return applicationArguments -> {
try {
InetAddress ia = InetAddress.getLocalHost();
//获取本机内网IP
log.info("启动成功:" + "http://" + ia.getHostAddress() + ":" + port + "/");
log.info("${user.home} :" + userName);
} catch (UnknownHostException ex) {
ex.printStackTrace();
}
};
}
}
方法2
使用lombok的@Slf4j,帮我们创建Logger对象,效果与方法1一样
/**
* 配置内部类
*/
@Slf4j
@Controller
@Configuration
class Config {
@Value("${user.home}")
private String userName;
/**
* 端口
*/
@Value("${server.port}")
private String port;
/**
* 启动成功
*/
@Bean
public ApplicationRunner applicationRunner() {
return applicationArguments -> {
try {
InetAddress ia = InetAddress.getLocalHost();
//获取本机内网IP
log.info("启动成功:" + "http://" + ia.getHostAddress() + ":" + port + "/");
log.info("${user.home} :" + userName);
} catch (UnknownHostException ex) {
ex.printStackTrace();
}
};
}
}
简单配置
如果不需要进行复杂的日志配置,则在配置文件中进行简单的日志配置即可,默认情况下,SpringBoot日志只记录到控制台,不写日志文件。如果希望在控制台输出之外编写日志文件,则需要进行配置
logging:
file:
path: log # log打印目录,配置name后无效,需要在name配置路径
name: log/info.log # log打印名字
level:
root:
info # 默认为info级别
pattern:
console: '[%-4level] [%d{yyyy-MM-dd HH:mm:ss SSS}] [%thread] [%logger{35}:%line] %msg%n'
file: '[%-4level] [%d{yyyy-MM-dd HH:mm:ss SSS}] [%thread] [%logger{35}:%line] %msg%n'
重新启动项目
打开ims.log
扩展配置
Spring Boot包含许多Logback扩展,可以帮助进行高级配置。您可以在您的logback-spring.xml配置文件中使用这些扩展。如果需要比较复杂的配置,建议使用扩展配置的方式
PS:SpringBoot推荐我们使用带-spring后缀的 logback-spring.xml 扩展配置,因为默认的的logback.xml标准配置,Spring无法完全控制日志初始化。(spring扩展对springProfile节点的支持)
以下是项目常见的完整logback-spring.xml,SpringBoot默认扫描classpath下面的logback.xml、logback-spring.xml,所以不需要再指定spring.logging.config,当然,你指定也没有问题:
logging:
config: classpath:logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 从高到地低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL -->
<!-- 日志输出规则 根据当前ROOT 级别,日志输出时,级别高于root默认的级别时 会输出 -->
<!-- 以下 每个配置的 filter 是过滤掉输出文件里面,会出现高级别文件,依然出现低级别的日志信息,通过filter 过滤只记录本级别的日志 -->
<!-- 属性描述 scan:性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true scanPeriod:设置监测配置文件是否有修改的时间间隔,
如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>d1money-web-ys-ems</contextName>
<!-- 定义日志文件 输入位置 -->
<property name="log_dir" value="logs" />
<!-- 日志最大的历史 30天 -->
<property name="maxHistory" value="90" />
<property name="maxFileSize" value="50MB" />
<!-- ERROR级别日志 -->
<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 RollingFileAppender -->
<appender name="ERROR"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤器,只记录WARN级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 最常用的滚动策略,它根据时间来制定滚动策略.既负责滚动也负责出发滚动 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志输出位置 可相对、和绝对路径 -->
<fileNamePattern>
${log_dir}/app_error.%d{yyyy-MM-dd_HH}.%i.log
</fileNamePattern>
<!-- 可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件假设设置每个月滚动,
且<maxHistory>是6, 则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除 -->
<maxHistory>${maxHistory}</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${maxFileSize}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 按照固定窗口模式生成日志文件,当文件大于20MB时,生成新的日志文件。窗口大小是1到3,当保存了3个归档文件后,将覆盖最早的日志。
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/.log.zip</fileNamePattern> <minIndex>1</minIndex>
<maxIndex>3</maxIndex> </rollingPolicy> -->
<!-- 查看当前活动文件的大小,如果超过指定大小会告知RollingFileAppender 触发当前活动文件滚动 <triggeringPolicy
class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <maxFileSize>5MB</maxFileSize>
</triggeringPolicy> -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<!-- WARN级别日志 appender -->
<appender name="WARN"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤器,只记录WARN级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>
${log_dir}/app_warn.%d{yyyy-MM-dd_HH}.%i.log
</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>${maxHistory}</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${maxFileSize}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<!-- INFO级别日志 appender -->
<appender name="INFO"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤器,只记录INFO级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>
${log_dir}/app_info.%d{yyyy-MM-dd_HH}.%i.log
</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>${maxHistory}</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${maxFileSize}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<!-- DEBUG级别日志 appender -->
<appender name="DEBUG"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤器,只记录DEBUG级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>
${log_dir}/app_debug.%d{yyyy-MM-dd_HH}.%i.log
</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>${maxHistory}</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${maxFileSize}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<!-- TRACE级别日志 appender -->
<appender name="TRACE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 过滤器,只记录ERROR级别的日志 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>TRACE</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 按天回滚 daily -->
<fileNamePattern>
${log_dir}/app_trace.%d{yyyy-MM-dd_HH}.%i.log
</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>${maxHistory}</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>${maxFileSize}</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
</encoder>
</appender>
<logger name="java.sql.PreparedStatement" value="DEBUG" />
<logger name="java.sql.Connection" value="DEBUG" />
<logger name="java.sql.Statement" value="DEBUG" />
<logger name="com.ibatis" value="DEBUG" />
<logger name="com.ibatis.common.jdbc.SimpleDataSource" value="DEBUG" />
<logger name="com.ibatis.common.jdbc.ScriptRunner" level="DEBUG" />
<logger name="com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate"
value="DEBUG" />
<logger name="com.apache.ibatis" level="TRACE" />
<!-- root级别 DEBUG -->
<root level="debug">
<!-- 文件输出 -->
<appender-ref ref="ERROR" />
<appender-ref ref="INFO" />
<appender-ref ref="WARN" />
<appender-ref ref="DEBUG" />
<appender-ref ref="TRACE" />
</root>
</configuration>
启动项目,去到${user.home}当前服务器用户主目录,日志按日期进行产生,如果项目产生的日志文件比较大,还可以按照小时进行.log文件的生成
如果需要按日志级别分别输出到对应的日志文件,在appender标签新增filter标签进行指定:
<!-- 时间滚动输出 level为 【debug / info / warn / error】 日志 -->
<appender name="【DEBUG / INFO / WARN / ERROR】_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 忽略其他配置 -->
<!-- 此日志文件只记录 【debug / info / warn / error】 级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>【debug / info / warn / error】</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>