官方网站:https://logback.qos.ch/index.html、Maven 仓库地址:https://search.maven.org/artifact/ch.qos.logback/logback-classic
Logback日志框架的优势:
Logback主要分为三个模块:
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.32version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-classicartifactId>
<version>1.2.3version>
dependency>
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogbackTest {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(LogbackTest.class);
logger.error("error test");
logger.warn("warn test");
logger.info("info {}", "test");
logger.debug("debug test ");
logger.trace("trace test");
}
}
17:12:38.837 [main] ERROR LogbackTest - error test
17:12:38.840 [main] WARN LogbackTest - warn test
17:12:38.840 [main] INFO LogbackTest - info test
17:12:38.841 [main] DEBUG LogbackTest - debug test
源码分析:其实我们发现即使项目中没有引入slf4j我们这里也是用的slf4j门面进行编程,下面我们查看源码及依赖传递:
1、可以查看到Logback中的Logger继承了org.slf4j.Logger
package ch.qos.logback.classic;
public final class Logger implements org.slf4j.Logger, LocationAwareLogger, AppenderAttachable<ILoggingEvent>, Serializable {}
2、从logback的pom依赖中我们看到slf4-api及logback-core,依赖会进行传递
<
parent>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-parentartifactId>
<version>1.2.3version>
parent>
<artifactId>logback-classicartifactId>
<dependencies>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
<scope>compilescope>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>${slf4j.version}version>
<scope>compilescope>
dependency>
dependencies>
Appender :Logback 将写入日志事件的任务委托给一个名为 Appender 的组件。Appender 必须实现 ch.qos.logback.core.Appender 接口,Appender组件用来定义日志输出格式,日志如何过滤以及日志文件的处理。appender元素可以包含零个或一个layout元素,零个或多个encoder元素以及零个或多个filter元素。
Encoder:一个appender有一个encoder,负责将一个event事件转换成一组byte数组,并将转换后的字节数据输出到文件中。Encoder负责把事件转换为字节数组,并把字节数组写到合适的输出流。因此,encoder可以控制在什么时候、把什么样的字节数组写入到其拥有者维护的输出流中。Encoder接口有两个实现类,LayoutWrappingEncoder与PatternLayoutEncoder。注意:在logback 0.9.19 版之前没有 encoder。在之前的版本里,多数 appender 依靠 layout 来把事件转换成字符串并用java.io.Writer 把字符串输出。在之前的版本里,用户需要在FileAppender里嵌入一个 PatternLayout。
Level:Logback日志级别如下(默认级别:DEBUG):
// ERROR(错误信息) > WARN(警告信息) > INFO(重要信息) > DEBUG(普通信息) > TRACE(追踪信息) DEBUG为默认的打印级别
public static final Integer OFF_INTEGER = 2147483647; // OFF:最高等级的而级别,用于关闭所有的日志记录
public static final Integer ERROR_INTEGER = 40000; // ERROR:记录错误信息,不会影响系统运行。一般不想输出太多日志,则使用该级别即可
public static final Integer WARN_INTEGER = 30000; // WARN:记录警告信息
public static final Integer INFO_INTEGER = 20000; // INFO:记录运行信息
public static final Integer DEBUG_INTEGER = 10000; // DEBUG:(默认)记录调式信息,一般在开发中使用
public static final Integer TRACE_INTEGER = 5000; // TRACE:记录追踪信息,记录程序的流程信息,一般情况不会使用
public static final Integer ALL_INTEGER = -2147483648; // ALL:最低等级,用于打开所有级别的日志记录
LoggerContext:可以在代码中直接获取LoggerContext,通过LoggerContext可以动态修改Logback的配置
import org.sflf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
// 根据包路径或类路径获取日志记录器,"ROOT" 表示根记录器: Logger getLogger(final String name);
loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).setLevel(Level.toLevel("error"));
// 停止logback-classic,也就是停止Logback
loggerContext.stop();
Logback提供了三种种配置文件:logback.groovy、logback-test.xml、logback.xml(位于src/main/resources目录下)如果都不存在则采用默认的配置
对XML日志文件的配置,大多数人第一次接触时有一种望而生畏的感觉,其实如果仔细分析,会发现核心的部分只有三个元素:appender、logger、root
┌── 日志输出格式
┌───appender───┼── 日志过滤
│ └── 日志文件处理
│ ┌── 获取哪些(包)的日志
├────logger────┤ ──┐
│ └── 日志的输出级别 ├ 日志组件(root,logger)
│ ┌── 根节点 ──┘
configuration──┼─────root─────┤
│ └── 只有level属性
│
│─────property──── 定义变量(非必须)
│
└───contextName─── 应用上下文名称(非必须)
模板 1:
<configuration scan="true" scanPeriod="10 seconds">
<contextName>logback-springcontextName>
<property name="logging.path" value="myLogs"/>
<conversionRule conversionWord="clr"
converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debuglevel>
filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}Pattern>
<charset>UTF-8charset>
encoder>
appender>
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logging.path}/web_debug.logfile>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
<charset>UTF-8charset>
encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logging.path}/web-debug-%d{yyyy-MM-dd}.%i.logfileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MBmaxFileSize>
timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>15maxHistory>
rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debuglevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
appender>
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logging.path}/web_info.logfile>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
<charset>UTF-8charset>
encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logging.path}/web-info-%d{yyyy-MM-dd}.%i.logfileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MBmaxFileSize>
timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>15maxHistory>
rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>infolevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
appender>
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logging.path}/web_warn.logfile>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
<charset>UTF-8charset>
encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logging.path}/web-warn-%d{yyyy-MM-dd}.%i.logfileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MBmaxFileSize>
timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>15maxHistory>
rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warnlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
appender>
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logging.path}/web_error.logfile>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%npattern>
<charset>UTF-8charset>
encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${logging.path}/web-error-%d{yyyy-MM-dd}.%i.logfileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MBmaxFileSize>
timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>15maxHistory>
rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERRORlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
appender>
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
root>
configuration>
模板 2:
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<property name="log_dir" value="logs/" />
<property name="maxHistory" value="30"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -%msg%npattern>
encoder>
appender>
<appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERRORlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/error-log.logfileNamePattern>
<maxHistory>${maxHistory}maxHistory>
rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%npattern>
encoder>
appender>
<appender name="WARN" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARNlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/warn-log.log
fileNamePattern>
<maxHistory>${maxHistory}maxHistory>
rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%npattern>
encoder>
appender>
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFOlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/info-log.log
fileNamePattern>
<maxHistory>${maxHistory}maxHistory>
rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%npattern>
encoder>
appender>
<appender name="DruidFILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${log_dir}/settle-query.log-druid-%d{yyyy-MM-dd}FileNamePattern>
<MaxHistory>90MaxHistory>
rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
pattern>
encoder>
appender>
<appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUGlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/debug-log.log
fileNamePattern>
<maxHistory>${maxHistory}maxHistory>
rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%npattern>
encoder>
appender>
<appender name="TRACE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>TRACElevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log_dir}/%d{yyyy-MM-dd}/trace-log.log
fileNamePattern>
<maxHistory>${maxHistory}maxHistory>
rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%npattern>
encoder>
appender>
<logger name="org.springframework" value="WARN" />
<logger name="org.apache" value="WARN" />
<logger name="com.netflix" value="WARN" />
<logger name="me.chanjar" value="WARN" />
<logger name="com.ulisesbocchio" value="WARN" />
<logger name="org.hibernate" value="WARN" />
<logger name="com.github.liuweijw" value="INFO" />
<logger name="c.n.discovery" value="WARN" />
<logger name="o.s.c.s" value="WARN" />
<logger name="c.u.j.encryptor" value="WARN" />
<logger name="o.s.boot" value="WARN" />
<logger name="druid" level="DEBUG">
<appender-ref ref="DruidFILE" />
logger>
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="ERROR" />
<appender-ref ref="INFO" />
root>
configuration>
Logback的配置,需要配置输出源Appender,打日志的Logger(子节点)和Root(根节点)实际上,它输出日志是从子节点开始,子节点如果有输出源直接输出,如果无,判断配置的additivity,是否向上级传递,即是否向root传递,传递则采用Root的输出源,否则不输出日志。
logback.xml配置文件根结点为configuration,内主要包含contextName、property、appender、filter、pattern、logger、root 等结点。基本结构可以描述为1个configuration元素,包含零个或多个appender元素,后跟零个或多个logger元素,后跟最多1个root元素(也可以没有)
根元素configuration有三个属性:debug、 scan、scanPeriod,及一个特殊的属性:packagingData,示例及解释如下:
<configuration debug="true" scan="true" scanPeriod="60 seconds">
configuration>
开启packagingDatas配置:可按如下配置开启包数据(配置文件或者代码形式取其一即可):
<configuration packagingData="true">
configuration>
import org.sflf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.setPackagingDataEnabled(true);
如果开启了,logback 会在输出的堆栈行中显示它是属于哪个 jar 或者哪个类的。此信息由 jar 文件的名称和版本组成,表明堆栈信息来源于此。此机制对于识别软件版本问题非常有用。但是,计算成本相当昂贵,尤其是在经常引发异常的应用程序中。以下演示开启的结果,即多了 [] 括号内的信息。
14:28:48.835 [btpool0-7] INFO c.q.l.demo.prime.PrimeAction - 99 is not a valid value
java.lang.Exception: 99 is invalid
at ch.qos.logback.demo.prime.PrimeAction.execute(PrimeAction.java:28) [classes/:na]
at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431) [struts-1.2.9.jar:1.2.9]
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236) [struts-1.2.9.jar:1.2.9]
at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432) [struts-1.2.9.jar:1.2.9]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) [servlet-api-2.5-6.1.12.jar:6.1.12]
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502) [jetty-6.1.12.jar:6.1.12]
at ch.qos.logback.demo.UserServletFilter.doFilter(UserServletFilter.java:44) [classes/:na]
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115) [jetty-6.1.12.jar:6.1.12]
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:361) [jetty-6.1.12.jar:6.1.12]
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:417) [jetty-6.1.12.jar:6.1.12]
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230) [jetty-6.1.12.jar:6.1.12]
日志上下文名称:contextName,每个日志组件(logger)都会关联到日志上下文,默认上下文名称是:default,用于标识应用,如果多个应用输出到同一个地方,就有必要使用%contextName
来区别。contextName直接在configuration元素下:
<configuration>
<contextName>HelloWorld-logcontextName>
configuration>
1、用来定义变量值的标签,通过property定义的值会被插入到logger上下文中。property标签中有: name、value 两个属性,属性含义如下:
${name}
来使用变量<configuration>
<property name="logName" value="logbackStudy" />
<property resource="logback.properties" />
<property file="D:\\logback.properties" />
configuration>
2、如果是在Spring或SpringBoot项目当中,想让value值是通过配置文件获取,可使用springProperty来定义。这里是获取项目名称和运行的服务器IP
<springProperty scope="context" name="appName" source="spring.application.name" />
<springProperty scope="context" name="ip" source="spring.cloud.client.ipAddress" />
3、变量作用域(scope)
定义的变量是有作用域的,如本地作用域,上下文作用域,系统级作用域。默认是本地作用域。
获取时间戳字符串并格式化:timestamp。他有两个属性key和datePattern
<configuration>
<timestamp key="nowDate" datePattern="yyyyMMdd" />
<timestamp key="nowDateTime" datePattern="yyyyMMdd_HHmmss" />
<contextName>${nowDateTime}contextName>
configuration>
主要用来配置转换器,可以自定义Pattern模板,也可以引入一些其他模板来解析Pattern,比如打印彩色日志,打印IP地址。conversionRule 元素中有两个参数,解释如下:
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<property name="COLOR_LOG_PATTERN" value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%p) %clr(${PID}){magenta} %clr([%.10t]){yellow} %clr([%c{1}]){blue}%clr([%L]){faint}%X{traceId} %m%n"/>
上面%clr
就是要用ColorConverter
来解析,后面{blue}
就是要展示的颜色。
官方文档为:https://logback.qos.ch/manual/appenders.html
Appender 的组件有两个属性:name和class,并且需要强制赋值,解释如下:
Appender种类有很多,常用的种类有:ConsoleAppender(输出到控制台)、FileAppender(输出到文件)、SMTPAppender(输出到邮件)、DBAppender(输出到数据库)、AsyncAppender(异步输出,包装其它具体的appender,不单独使用)等,如下介绍常用的
除了上述Appender外,另外还有SocketAppender(输出到socket)、SMTPAppender(输出到邮件)、DBAppender(输出到数据库)等具体使用查文档
ConsoleAppender 就跟名字显示的一样,是将日志事件附加到控制台,进一步说就是通过 System.out(默认) 或 System.err 来进行输出。ConsoleAppender 通过用户指定的 encoder,格式化日志事件。
属性名 | 类型 | 描述 |
---|---|---|
target | String | System.out 或 System.err。默认为 System.out |
encoder | Encoder | 决定通过哪种方式将事件写入 OutputStreamAppender |
withJansi | boolean | 默认值为 false。设为 true 可以激活 Jansi 在 windows 使用 ANSI 彩色代码。 在 windows 上如果设置为 true,你应该将 org.fusesource.jansi:jansi:1.9 这个 jar 包放到 classpath 下。基于 Unix 实现的操作系统,像 Linux、Max OS X 都默认支持 ANSI 才彩色代码。 |
<configuration>
<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<target>System.outtarget>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%d [%t] [%-5level] %c %M %L %m %npattern>
<charset>UTF-8charset>
encoder>
appender>
<root level="INFO">
<appender-ref ref="consoleAppender"/>
root>
configuration>
FileAppender是OutputStreamAppender的子类,将日志事件输出到文件中。通过file来指定目标文件。如果该文件存在,根据append的值,要么将日志追加到文件中,要么该文件被截断。
属性名 | 类型 | 描述 |
---|---|---|
file | String | 要写入文件的名称。如果文件不存在,则新建 |
encoder | Encoder | 参见 ConsoleAppender 的属性 |
append | boolean | 如果为 true,日志事件会被追加到文件中,否则的话,清空现存文件。默认为 true |
prudent | boolean | 在严格模式下,FileAppender会将日志安全的写入指定文件。即使在不同的 JVM 或者不同的主机上运行 FileAppender 实例。默认值为 false。严格模式可以与 RollingFileAppender 结合使用 |
1、将日志输出到文件:FileAppender。包含file(文件名)、encoder(格式化)元素
<configuration>
<property name="pattern" value="%d [%t] [%-5level] %c %M %L %m %n">property>
<property name="log_file" value="./logs">property>
<timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss" />
<appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
<file>${log_file}/logback-${bySecond}.logfile>
<immediateFlush>trueimmediateFlush>
<encoder>
<pattern>${pattern}pattern>
encoder>
appender>
<root level="INFO">
<appender-ref ref="fileAppender"/>
root>
configuration>
2、输出到html格式的日志文件:appender设置为FileAppender,encoder设置为LayoutWrappingEncoder,layout设置为LayoutWrappingEncoder
<configuration>
<property name="html_pattern" value="%d{yyyy-MM-dd HH:mm:ss}%t%-5level%c%M%L%m%n">property>
<property name="log_file" value="./logs">property>
<appender name="htmlFileAppender" class="ch.qos.logback.core.FileAppender">
<file>${log_file}/logback.htmlfile>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>${html_pattern}pattern>
layout>
encoder>
appender>
<root level="INFO">
<appender-ref ref="htmlFileAppender"/>
root>
configuration>
注意:我们只能设置打印的日志信息内容,不能设置这个网页的打印格式以及样式。但是,当我们打印出logback.html文件后,我们可以人为的修改其中的样式以及格式这个html中包含 HTML+CSS。在实际的开发中,如果日志文件不是很大,我们可以考虑使用html进行日志打印,因为可读性强。
RollingFileAppender是FileAppender的子类,扩展了FileAppender,具有翻转日志文件的功能。当满足某个特定的条件之后,将日志输出到另外一个文件。
属性名 | 类型 | 描述 |
---|---|---|
file | String | 被写入文件名,可以是相对目录或绝对目录,如果上级目录不存在会自动创建,无默认值 |
rollingPolicy | RollingPolicy | 当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名 |
triggeringPolicy | TriggeringPolicy | 告知 RollingFileAppender 合适激活滚动 |
encoder | Encoder | 参见 ConsoleAppender 的属性 |
append | boolean | 如果为 true,日志事件会被追加到文件中,否则的话,清空现存文件。默认为 true |
prudent | boolean | 当为true时,不支持FixedWindowRollingPolicy。支持TimeBasedRollingPolicy,但是有两个限制, 1.不支持也不允许文件压缩,2.不能设置file属性,必须留空 |
RollingPolicy用于配置滚动策略,支持TimeBasedRollingPolicy(基于时间滚动)、SizeAndTimeBasedRollingPolicy(基于大小和时间滚动)、FixedWindowRollingPolicy(固定窗口滚动和大小触发)、其中FixedWindowRollingPolicy和SizeBasedTriggeringPolicy一般同时使用
RollingPolicy滚动策略包括以下几种:
TimeBasedRollingPolicy:它是基于时间来定义轮转策(最常用的轮转策略)例如按天或月。TimeBasedRollingPolicy 既负责轮转的行为,也负责触发轮转。实际上:TimeBasedRollingPolicy 同时实现了 RollingPolicy 与 TriggeringPolicy 接口。
TimeBasedRollingPolicy 的配置需要一个强制的属性 fileNamePattern 以及其它的可选属性:
属性名 | 类型 | 描述 |
---|---|---|
fileNamePattern | String | 该属性的值应该由文件名加上一个 %d 的占位符。%d 应包含 java.text.SimpleDateFormat 中规定的日期格式。如果省略日期格式,那么就默认为 yyyy-MM-dd 。如果 fileNamePattern 以 .gz 或者 .zip 结尾,将会启动这个压缩特性 |
maxHistory | int | 这个可选的属性用来控制最多保留多少数量的归档文件,将会异步删除旧的文件。如单位按月轮转时maxHistory = 6,只会保留6个月的日志。单位根据时间格式维度来区分:按天(yyyy-MM-dd) 按月(yyyy-MM) |
totalSizeCap | int | 在有 maxHistory 的限制下,进一步限制所有日志文件大小之和的上限,超过则从最旧的日志开始删除 |
cleanHistoryOnStart | boolean | 如果设置为 true,那么在 appender 启动的时候,归档文件将会被删除。默认的值为 false。归档文件的删除通常在轮转期间执行。但是,有些应用的存活时间可能等不到轮转触发。对于这种短期应用,可以通过设置该属性为 true,在 appender 启动的时候执行删除操作 |
<configuration>
<appender name="rollingFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/logback.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/logback_%d{yyyy-MM-dd_HH-mm-ss}.logfileNamePattern>
<maxHistory>60maxHistory>
<totalSizeCap>10MBtotalSizeCap>
<cleanHistoryOnStart>truecleanHistoryOnStart>
rollingPolicy>
<encoder>
<pattern>%date %-5level -[%thread] %class.%method/%line : %msg%npattern>
encoder>
appender>
<root level="INFO">
<appender-ref ref="rollingFileAppender"/>
root>
configuration>
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Timer;
import java.util.TimerTask;
public class LogbackTest {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(LogbackTest.class);
// 1秒后启动,每秒跑1次
new Timer().scheduleAtFixedRate(new TimerTask(){
@Override
public void run() {
logger.error("error test");
logger.warn("warn test");
logger.info("info {}", "test");
logger.debug("debug test ");
logger.trace("trace test");
}
}, 1000, 1000);
}
}
执行后可以发现1分钟后生成了61个文件。1个logback.log,60个logback_2022-04-03_12-12-xx.log。这是因为每秒生产一个文件,而maxHistory设置了保留最近一分钟的日志(并且每个日志文件都比较小,60个加起来也没超过totalSizeCap总日志大小的上限)。此时还可以发生另外一种情况,就是把totalSizeCap总文件大小设置比较小,才生成几个文件就达到totalSizeCap总日志大小的上限。
SizeAndTimeBasedRollingPolicy:基于时间+大小进行滚动(既能按时轮转,又能限制每个日志文件的大小)是对于第一种的补充。避免单个日志文件过大。实际查看源码也可以看出SizeAndTimeBasedRollingPolicy继承了TimeBasedRollingPolicy,并增加了一个maxFileSize属性。
属性名 | 类型 | 描述 |
---|---|---|
fileNamePattern | String | 该属性的值应该由文件名加上一个 %d 的占位符。%d 应包含 java.text.SimpleDateFormat 中规定的日期格式。如果省略日期格式,那么就默认为 yyyy-MM-dd 。并且需要包含一个 i% 的占位符,当文件大小超过maxFileSize大小时,则用下标新建文件。如果 fileNamePattern 以 .gz 或者 .zip 结尾,将会启动这个压缩特性 |
maxFileSize | int | 按照文件大小进行拆分,当文件大小达到1MB时就会将日志进行压缩 |
maxHistory | int | 这个可选的属性用来控制最多保留多少数量的归档文件,将会异步删除旧的文件。如单位按月轮转时maxHistory = 6,只会保留6个月的日志。单位根据时间格式维度来区分:按天(yyyy-MM-dd) 按月(yyyy-MM) |
totalSizeCap | int | 在有 maxHistory 的限制下,进一步限制所有日志文件大小之和的上限,超过则从最旧的日志开始删除 |
cleanHistoryOnStart | boolean | 如果设置为 true,那么在 appender 启动的时候,归档文件将会被删除。默认的值为 false。归档文件的删除通常在轮转期间执行。但是,有些应用的存活时间可能等不到轮转触发。对于这种短期应用,可以通过设置该属性为 true,在 appender 启动的时候执行删除操作 |
<configuration>
<appender name="rollingFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/logback.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/logback_%d{yyyy-MM-dd_HH-mm-ss}_%i.log.zipfileNamePattern>
<maxFileSize>1MBmaxFileSize>
<maxHistory>60maxHistory>
<totalSizeCap>200MBtotalSizeCap>
<cleanHistoryOnStart>truecleanHistoryOnStart>
rollingPolicy>
<encoder>
<pattern>%date %-5level -[%thread] %class.%method/%line : %msg%npattern>
encoder>
appender>
<root level="INFO">
<appender-ref ref="rollingFileAppender"/>
root>
configuration>
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Timer;
import java.util.TimerTask;
public class LogbackTest {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(LogbackTest.class);
// 1秒后启动,每秒跑1次
new Timer().scheduleAtFixedRate(new TimerTask(){
@Override
public void run() {
logger.error("error test");
logger.warn("warn test");
logger.info("info {}", "test");
logger.debug("debug test ");
logger.trace("trace test");
}
}, 1000, 1000);
}
}
执行后可以发现会生成文件logback.log与文件logback_2022-04-03_17-42-xx_0.log.zip若干个。若在同一个时间段内生成的单个文件超过了maxFileSize后,%i
就会生效(默认是从0开始)
可以理解成自定义滚动规则,避免使用出现单个文件过大或者日志文件过多的情况。需要同时配置triggeringPolicy用于指定滚动触发规则。对以上的补充。
FixedWindowRollingPolicy 根据固定窗口算法重命名文件,filaNamePattern 表示归档文件的名字。这个属性是必选,并必须包含一个表示整形的占位符 i%
属性名 | 类型 | 描述 |
---|---|---|
fileNamePattern | String | FixedWindowRollingPolicy 在重命名日志文件时将会根据这个属性来命名。它必须包含一个 i% 的占位符,该占位符指明了窗口索引的值应该插入的位置。 例如,当该属性的值为 MyLogFile%i.log ,最小与最大的值分别为 1 和 3 。将会产生的归档文件为 MyLogFile1.log ,MyLogFile2.log ,MyLogFile3.log 。 文件压缩的方式也是通过该属性来指定。例如,设置该属性的值为 MyLogFile%i.log.zip ,那么归档文件将会被压缩成 zip 格式。也可以选择压缩成 gz 格式。 |
minIndex | int | 表示窗口索引的下界 |
maxIndex | int | 表示窗口索引的上界 |
<configuration>
<appender name="rollingFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/logback.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>logs/logback_%i.log.zipfileNamePattern>
<minIndex>1minIndex>
<maxIndex>5maxIndex>
rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>10KBmaxFileSize>
triggeringPolicy>
<encoder>
<pattern>%date %-4relative [%thread] %-5level %logger{35} - %msg%npattern>
encoder>
appender>
<root level="INFO">
<appender-ref ref="rollingFileAppender" />
root>
configuration>
TriggeringPolicy 用于通知何时触发滚动。实现类:SizeBasedTriggeringPolicy
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Timer;
import java.util.TimerTask;
public class LogbackTest {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(LogbackTest.class);
// 1秒后启动,每秒跑1次
new Timer().scheduleAtFixedRate(new TimerTask(){
@Override
public void run() {
logger.error("error test");
logger.warn("warn test");
logger.info("info {}", "test");
logger.debug("debug test ");
logger.trace("trace test");
}
}, 1000, 1000);
}
}
执行代码可以发现:最后生成文件:logback.log、logback_1.log.zip - logback_5.log.zip(zip解压后可以发现文件名:logback_2022-04-03_1754,具体可以参考源码:FixedWindowRollingPolicy)如果fileNamePattern中没有设置以.zip结尾的话,默认名称就是设定名称+Index
适用上面的 SizeAndTimeBasedRollingPolicy 和 TimeBasedRollingPolicy 滚动策略
fileNamePattern 格式 | 说明 |
---|---|
app_%d.log | 每天轮转。不指定%d 日期格式时默认为 yyyy-MM-dd |
app_%d{yyyy-MM}.log | 每个月开始的时候轮转 |
app._%d{yyyy-ww}.log | 每周的第一天(取决于时区) |
app_%d{yyyy-MM-dd_HH}.log | 每小时轮转。如:app_2020-10-24_10.log |
app_%d{yyyy-MM-dd_HH-mm}.log | 每分钟轮转;。如:app_2020-10-24_10-32.log |
app_%d{yyyy-MM-dd_HH-mm, UTC}.log | 每分钟轮转(时间格式是 UTC) |
app/%d{yyyy-MM}/%d.log | 每天轮转。第一个%d 被辅助标记。第二个%d 为主要标记。如:app/2020-10/2020-10-24.log |
重点注意:fileNamePattern中设置的时间格式生产文件名不能使用空格已经冒号等特殊符号(不然不会生产新的滚动文件)
上面的Appender日志输出到文件是同步输出的,即每次输出都会直接写IO到磁盘文件。对于高并发,会产生一定的阻塞,造成不必要的性能损耗。Logback提供了日志异步输出的AsyncAppender。处理方式较简单,添加一个基于异步写日志的appender,并指向原配置的appender即可。AsyncAppender 参数说明:
参数 | 默认值 | 说明 |
---|---|---|
discardingThreshold | 20 | 如果设置discardingThreshold=0,表示 queue 满了,不丢弃,block 线程。默认情况下,当阻塞队列剩余 20% 的容量时,它将丢弃级别跟踪、调试和信息事件,只保留级别警告和错误事件。要保留所有事件,请将 discardingThreshold 设置为0 |
queueSize | 256 | 假设 IO 影响 30s,日志和 qps 比例是1:1,单容器压测值 1500 qps 则可以推算出 queueSize 的值,queueSize 的设置公式:30 * 1500=45000 |
neverBlock | false | 如果为 false(默认值),则追加程序将阻止追加到完整队列,而不是丢失消息。设置为 true 时,附加程序只会丢弃消息,不会阻止您的应用程序 |
includeCallerData | false | 提取呼叫者数据可能相当昂贵。 为了提高性能,默认情况下,当事件添加到事件队列时,不会提取与事件关联的调用者数据。 默认情况下,仅复制线程名称和 MDC 等“廉价”数据。 您可以通过将 includeCallerData 属性设置为 true 来指示此附加程序包含调用方数据。 |
<configuration>
<appender name="fileAppender" class="ch.qos.logback.core.FileAppender">
<file>logs/logback.logfile>
<immediateFlush>falseimmediateFlush>
<encoder>
<pattern>${pattern}pattern>
encoder>
appender>
<appender name="asyncAppender" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0discardingThreshold>
<queueSize>1024queueSize>
<neverBlock>trueneverBlock>
<includeCallerData>trueincludeCallerData>
<appender-ref ref="fileAppender"/>
appender>
<root level="INFO">
<appender-ref ref="asyncAppender" />
root>
configuration>
这里有个需要注意的地方,那就是 AsyncAppender 必须在其引用的 Appender 配置的后面,否则会使配置不生效。
使用SpringBoot + Logback 配置程序异常自动发送邮件:https://mp.weixin.qq.com/s/N4OrKlrtCDeEd95XWGi9YA
现在想更要发生异常自动发送邮件的功能,需要使用到 SMTPAppender 输出日志到邮件组件
1、加入依赖(JavaMail)
<dependency>
<groupId>javax.mailgroupId>
<artifactId>mailartifactId>
<version>1.4.7version>
dependency>
2、修改logback的xml的配置文件
<configuration debug="true">
<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d [%t] [%-5level] %c %M %L %m %npattern>
encoder>
appender>
<property name="smtpHost" value="smtp.qq.com"/>
<property name="smtpPort" value="587"/>
<property name="mailFrom" value="[email protected]"/>
<property name="username" value="[email protected]"/>
<property name="mailTo" value="[email protected],[email protected]"/>
<property name="password" value="fxomimpmgpjgbhxx"/>
<property name="SSL" value="false"/>
<property name="mailSubject" value="Exception information reminder"/>
<appender name="mailAppender" class="ch.qos.logback.classic.net.SMTPAppender">
<smtpHost>${smtpHost}smtpHost>
<smtpPort>${smtpPort}smtpPort>
<username>${username}username>
<password>${password}password>
<SSL>${SSL}SSL>
<asynchronousSending>falseasynchronousSending>
<to>${mailTo}to>
<from>${mailFrom}from>
<subject>${mailSubject}subject>
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<Pattern>%date%level%thread%logger{0}%line%messagePattern>
layout>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERRORlevel>
filter>
appender>
<root level="INFO">
<appender-ref ref="consoleAppender"/>
<appender-ref ref="mailAppender"/>
root>
configuration>
3、测试代码,输出ERROR级别日志,会自动发送到邮件中
package com.xyz;
import ch.qos.logback.classic.Logger;
import org.slf4j.LoggerFactory;
public class LogbackTest {
public static void main(String[] args) {
Logger logger = (Logger)LoggerFactory.getLogger(LogbackTest.class);
String s = null;
try {
s.equals("");
} catch (Exception e) {
logger.error("error message: {}",e);
}
}
}
4、邮件中收到内容如下:
Date | Level | Thread | Logger | LineOfCaller | Message |
---|---|---|---|---|---|
2022-04-06 11:37:18,549 | ERROR | main | LogbackTest | 14 | error message: {} |
java.lang.NullPointerException: null at com.xyz.LogbackTest.main(LogbackTest.java:14) |
GitHub地址:https://github.com/logfellow/logstash-logback-encoder/blob/main/README.md
如果项目中的日志采用的是基于ELK(Elasticsearch、Logstash、Kibana)来进行日志管理。则可以在pom文件中引入logstash-logback-encoder依赖
<dependency>
<groupId>net.logstash.logbackgroupId>
<artifactId>logstash-logback-encoderartifactId>
<version>5.1version>
dependency>
然后在logback-spring.xml中配置对应的appender:
<appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<destination>192.168.0.11:5061destination>
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder">
<customFields>{"project": "springboot-logback-elk"}customFields>
encoder>
appender>
该appender的使用与其他appender的使用无异。主要使用了LogstashTcpSocketAppender类来完成与Logstash的通信。其中destination为Logstash提供的服务地址。customFields为自定义的参数,便于Logstash识别日志是从哪个业务系统传输过来的。
如下是参考一个详细(完整的logback配置示例ELK整合包含生成json日志:https://www.zhangshengrong.com/p/OgN5Dvvxan/)
<appender name="logstash" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/logback.jsonfile>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logs/logback-%d{yyyy-MM-dd}.jsonfileNamePattern>
<maxHistory>7maxHistory>
rollingPolicy>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp>
<timeZone>UTCtimeZone>
timestamp>
<pattern>
<pattern>
{
"ip": "${ip}",
"app": "${appName}",
"level": "%level",
"trace": "%X{X-B3-TraceId:-}",
"span": "%X{X-B3-SpanId:-}",
"parent": "%X{X-B3-ParentSpanId:-}",
"thread": "%thread",
"class": "%logger{40}",
"message": "%message",
"stack_trace": "%exception{10}"
}
pattern>
pattern>
providers>
encoder>
appender>
官网地址:
encoder和layout一样,用来格式化输出。encoder是在logback 0.9.19 版本之后引入的。在这个版本之前,大部分的appender使用layout来格式化输出,但之后FileAppender和他的子类推荐使用encoder来代替layout。常用的是PatternLayoutEncoder(这是默认的encoder)所以我们不用指定,直接使用encoder标签就行。PatternLayoutEncoder的组成和layout一样。如下:
Pattern表达式:
Conversation Word | Effect |
---|---|
d{pattern}、date{pattern} | 日志打印的时间,语法与java.text.SimpleDateFormat兼容。Conversion Pattern Result |
%d | 2006-10-20 14:06:49,812 |
%date | 2006-10-20 14:06:49,812 |
%date{ISO8601} | 2006-10-20 14:06:49,812 |
%date{HH:mm:ss.SSS} | 14:06:49.812 |
%date{dd MMM yyyy ;HH:mm:ss.SSS} | 20 oct. 2006;14:06:49.812 |
%d{dd MMM yyyy ;HH:mm:ss.SSS,Australia/Perth} | 05 4月 2022 ;11:34:13.168 |
Conversation Word | Effect |
---|---|
变量名 | %date 时间打印详情请参考上方 |
%date{pattern, timezone}、%d | 日志打印的时间。pattern与java.text.SimpleDateFormat兼容 pattern不填写默认值是:yyyy-MM-dd hh:mm:ss,SSS。 第二个参数是时区,例如:%date{HH:mm:ss.SSS, Australia/Perth} |
%logger、%lo、%c{length} | 输出当前日志名称。没有length则输出全名。如:com.xyz.LogbackTest |
%class、%C{length} | 输出日志调用所在类,没有length则打印全类名。如:com.xyz.LogbackTest(与%c一样) |
%method、%M | 输出日志所在方法(没有length参数)如:main |
%caller{length} | 日志调用位置,length代表日志深度,如:Caller+0 at com.xyz.LogbackTest.main(LogbackTest.java:9) |
%thread、%t | 输出线程名(没有length参数) |
%level、%le、%p | 输出日志级别(没有length参数) |
%message、%msg、%m | 输出日志记录内容,就是调用logger的方法的时候传入的log字符串 |
%exception、%ex | 输出异常信息。如:logger.error("error...",new Exception("exception")); |
%line、%L | 输出日志的行数。如:9 |
%file、%F | 输出调用logger的java源码文件名,速度不快,避免使用。如:LogbackTest.java |
%n | 换行符 |
% | 输出%号 |
特殊占位符 | |
%X{user} | 表示可以获取外部自定义传入的值, 如:%X{user} =》org.slf4j.MDC.put("user", "xx-yy-zz"); |
宽度设置 | 可以限制上面的ConversationWord的宽度和左右对齐 |
%20logger | 最小宽度20,当小于20时,则左侧留空白。右对齐 |
%-20logger | 最小宽度20,当小于20时,则右侧留空白。左对齐 |
%.30logger | 最大宽度30,超出时从头部开始截断。如:%.2、test=》st |
%.-30logger | 最大宽度30,超出时从末尾开始截断。如:%.-2、test=》te |
%20.30logger | 最小20,最大30,小于20的时候右对齐,大于30的时候开始处截断 |
%-20.30logger | 最小20,最大30,小于20的时候左对齐,大于30的时候开始处截断 |
显示设置 | |
%highlight() | 突出显示,如:%highlight(%-5level) |
%green(%red、%blue、%white) | 字体显示为指定颜色 |
{length} | 可指定长度,如:%logger{36} |
网络访问设置 | 需要依赖logger-access模块 |
%remoteIP、%a | 远程IP |
%localIP、%A | 本地IP |
%clientHost、%h | 远程主机名 |
%localPort | 本地端口 |
%requestMethod、%m | http请求方法 |
%protocol、%H | http请求协议 |
%statusCode、%s | http请求status code |
%requestURL、%r | http请求地址 |
%requestURI、%U | http请求资源地址 |
%queryString、%q | http请求参数 |
%server、%v | 服务器地址 |
%elapsedTime、%D | http请求处理的时间,单位是毫秒 |
%elapsedSeconds、%T | http请求处理的时间,单位是秒 |
%date、%t | 日志记录时间 |
%threadName、%I | 处理请求的线程名 |
%reqAttribute{attributeName} | http请求attribute值 |
%reqCookie{cookie} | http请求cookie值 |
%reqContent | http请求体内容 |
%fullRequest | http完整请求 |
%responseContent | http响应 |
%fullResponse | http完整响应 |
格式化修饰器:可以限制上面的ConversationWord的宽度和左右对齐。比如:
1、用来指定日志的输出格式及编码等其他配置(encoder元素)
<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %lo %C %M %le %t %le %lo : %m%npattern>
<pattern>%date %logger %class %method %highlight(%level) %thread : %msg%npattern>
<pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%npattern>
<charset class="java.nio.charset.Charset">UTF-8charset>
<immediateFlush>falseimmediateFlush>
encoder>
appender>
2、使用特殊占位符%X{}
示例:
<configuration debug="false">
<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d [%X{uuid}] [%X{x}] [%t] [%-5level] %c %M %L %m %npattern>
encoder>
appender>
<root level="INFO">
<appender-ref ref="consoleAppender"/>
root>
configuration>
package com.xyz;
import ch.qos.logback.classic.Logger;
import org.slf4j.LoggerFactory;
import java.util.UUID;
public class LogbackTest {
public static void main(String[] args) {
Logger logger = (Logger)LoggerFactory.getLogger(LogbackTest.class);
org.slf4j.MDC.put("uuid", UUID.randomUUID().toString());
logger.info("info {}", "test");
}
}
2022-04-06 18:46:33,795 [4effd2ef-a4de-4a6c-82c1-c1d522e9aa76] [] [main] [INFO ] com.xyz.LogbackTest main 10 info test
官网地址:https://logback.qos.ch/manual/filters.html
Filter是日志过滤器,是appender里面的子元素。执行一个过滤器会有返回DENY、NEUTRAL、ACCEPT三个枚举值中的一个。appender 有多个过滤器时,按照配置顺序执行。
LevelFilter 基于级别来过滤日志事件。如果事件的级别与配置的级别相等,过滤器会根据配置的 onMatch 与 onMismatch 属性,接受或者拒绝事件
<configuration>
<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFOlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<encoder>
<pattern>%date %-4relative [%thread] %-5level %logger{35} - %msg%npattern>
encoder>
appender>
<root level="INFO">
<appender-ref ref="consoleAppender" />
root>
configuration>
ThresholdFilter 基于给定的临界值来过滤事件。如果事件的级别等于或高于给定的临界值,当调用 decide() 时,ThresholdFilter 将会返回 NEUTRAL。但是事件的级别低于临界值将会被拒绝。简单来说:针对指定级别及以上级别进行日志过滤
<configuration>
<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFOlevel>
filter>
<encoder>
<pattern>%date %-4relative [%thread] %-5level %logger{35} - %msg%npattern>
encoder>
appender>
<root level="INFO">
<appender-ref ref="consoleAppender" />
root>
configuration>
EvaluatorFilter: 求值过滤器,评估、鉴别日志是否符合指定条件。有一下子节点:
:鉴别器,常用的鉴别器是JaninoEventEvaluato,也是默认的鉴别器,它以任意的java布尔值表达式作为求值条件,求值条件在配置文件解释过成功被动态编译,布尔值表达式返回true就表示符合过滤条件。evaluator有个子标签
,用于配置求值条件
:用于配置符合过滤条件的操作
:用于配置不符合过滤条件的操作求值表达式作用于当前日志,logback向求值表达式暴露日志的各种字段(evaluator标签中可以用的对象和值):
Name | Type | Description |
---|---|---|
event | LoggingEvent | 与记录请求相关联的原始记录事件,下面所有变量都来自event,例如,event.getMessage()返回下面"message"相同的字符串 |
message | String | 日志的原始消息,例如,设有logger mylogger,“name"的值是"AUB”,对于 mylogger.info(“Hello {}”,name); "Hello {}"就是原始消息。 |
formattedMessage | String | 日志被各式话的消息,例如,设有logger mylogger,“name"的值是"AUB”,对于 mylogger.info(“Hello {}”,name); "Hello Aub"就是格式化后的消息。 |
logger | String | logger 名。 |
loggerContext | LoggerContextVO | 日志所属的logger上下文。 |
level | int | 级别对应的整数值,所以 level > INFO 是正确的表达式。 |
timeStamp | long | 创建日志的时间戳。 |
marker | Marker | 与日志请求相关联的Marker对象,注意“Marker”有可能为null,所以你要确保它不能是null。 |
mdc | Map | 包含创建日志期间的MDC所有值得map。访问方法是:mdc.get(“myKey”) 。mdc.get()返回的是Object不是String,要想调用String的方法就要强转,例如,((String) mdc.get(“k”)).contains(“val”) .MDC可能为null,调用时注意。 |
throwable | java.lang.Throwable | 如果没有异常与日志关联"throwable" 变量为 null. 不幸的是, “throwable” 不能被序列化。在远程系统上永远为null,对于与位置无关的表达式请使用下面的变量throwableProxy |
throwableProxy | IThrowableProxy | 与日志事件关联的异常代理。如果没有异常与日志事件关联,则变量"throwableProxy" 为 null. 当异常被关联到日志事件时,“throwableProxy” 在远程系统上不会为null |
过滤器还可以来自自带的或表达式直接写在配置文件中。
<dependency>
<groupId>org.codehaus.janinogroupId>
<artifactId>janinoartifactId>
<version>3.1.6version>
dependency>
<configuration>
<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator>
<expression>
// 根据日志消息判断
if(event.getMessage().contains("url")){
return true;
}
if(formattedMessage.contains("url")){
return true;
}
return false;
expression>
evaluator>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<encoder>
<pattern>%date %-4relative [%thread] %-5level %logger{35} - %msg%npattern>
encoder>
appender>
<root level="ALL">
<appender-ref ref="consoleAppender"/>
root>
configuration>
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Timer;
import java.util.TimerTask;
public class LogbackTest {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(LogbackTest.class);
// 1秒后启动,每秒跑一次
new Timer().scheduleAtFixedRate(new TimerTask(){
@Override
public void run() {
logger.error("error test");
logger.warn("warn test");
logger.info("info {}", "url");
logger.debug("debug test ");
logger.trace("trace url");
}
}, 1000, 1000);
}
}
2022-04-03 22:46:31,251 1421 [Timer-0] INFO LogbackTest - info url
2022-04-03 22:46:31,251 1421 [Timer-0] TRACE LogbackTest - trace url
2022-04-03 22:46:32,254 2424 [Timer-0] INFO LogbackTest - info url
2022-04-03 22:46:32,254 2424 [Timer-0] TRACE LogbackTest - trace url
EvaluatorFilter 中还有一个特殊的标签,基本很少用,了解即可。
:匹配器,尽管可以使用String类的matches()方法进行模式匹配,但会导致每次调用过滤器时都会创建一个新的Pattern对象,为了消除这种开销,可以预定义一个或多个matcher对象,定以后就可以在求值表达式中重复引用。
是
的子标签。
中包含两个子标签,一个
,用于定义matcher的名字,求值表达式中使用这个名字来引用matcher;另一个
,用于配置匹配条件<configuration debug="true">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator>
<matcher>
<Name>oddName>
<regex>statement [13579]regex>
matcher>
<expression>odd.matches(formattedMessage)expression>
evaluator>
<OnMismatch>NEUTRALOnMismatch>
<OnMatch>DENYOnMatch>
filter>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger - %msg%npattern>
encoder>
appender>
<root level="DEBUG">
<appender-ref ref="STDOUT" />
root>
configuration>
通过实现ch.qos.logback.core.filter.Filter接口可以自定义过滤器,自定义过滤器类LogbackUrlFilter.class,示例如下:
package com.xyz;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.filter.Filter;
import ch.qos.logback.core.spi.FilterReply;
public class LogbackUrlFilter extends Filter<ILoggingEvent> {
@Override
public FilterReply decide(ILoggingEvent event) {
if(event.getMessage().contains("url")){
return FilterReply.ACCEPT;
}
if(event.getFormattedMessage().contains("url")){
return FilterReply.ACCEPT;
}
return FilterReply.DENY;
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Timer;
import java.util.TimerTask;
public class LogbackTest {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(LogbackTest.class);
// 1秒后启动,每秒跑一次
new Timer().scheduleAtFixedRate(new TimerTask(){
@Override
public void run() {
logger.error("error test");
logger.warn("warn test");
logger.info("info {}", "url");
logger.debug("debug test ");
logger.trace("trace url");
}
}, 1000, 1000);
}
}
2022-04-03 22:46:31,251 1421 [Timer-0] INFO LogbackTest - info url
2022-04-03 22:46:31,251 1421 [Timer-0] TRACE LogbackTest - trace url
2022-04-03 22:46:32,254 2424 [Timer-0] INFO LogbackTest - info url
2022-04-03 22:46:32,254 2424 [Timer-0] TRACE LogbackTest - trace url
通常情况下,日志输出会配置三个,一个控制台输出用于开发阶段;一个INFO及以上级别的日志输出,可追踪相应的生产日志;一个单独ERROR级别的日志输出,方便快速检查出异常日志。都可以通过Appender和Filter来控制。
logger用来设置某一个类或者某个包的日志输出级别、以及关联的appender,这样就可以控制非业务日志不写到对应的文件日志中。
logger标签中包含三个属性:
name:要输出日志的包名或者类名,比如com.xyz。必选项
level:设置日志的级别(不区分大小写:TRACE,DEBUG,INFO,WARN,ERROR,ALL或OFF)如果未设置,则logger会向上继承最近一个非空级别。可选项
additivity:是否将日志向上级传递,默认为 true。可选项
logger通过1个或多个子节点appender-ref来控制日志的输出:
<logger name="org.springframework" level="WARN"/>
<logger name="com" level="debug" />
<logger name="com.xyz" level="info" additivity="true">
<appender-ref ref="consoleAppender" />
logger>
root元素配置根记录器。它是个特殊的logger,是所有logger的根节点,只能指定日志级别及level(默认DEBUG)和appender-ref(输出的appender)
<root level="info">
<appender-ref ref="consoleAppender"/>
<appender-ref ref="fileAppender"/>
root>
<root level="ALL">
<appender-ref ref="fileAppender"/>
root>
<root level="info">
<appender-ref ref="consoleAppender"/>
root>
level属性的值可以是不区分大小写的字符串TRACE,DEBUG,INFO,WARN,ERROR,ALL或OFF之一。root元素可以包含零个或多个appender-ref元素;被引用的每个appender都被添加到根记录器中。
root元素和logger元素的区别?
root是根logger,是一种logger,root和根logger其实是一回事;只不过root中不能有 name 和 additivity 这两个属性
文件包含元素:将配置文件的一部分包含在另一个文件中,可以通过标签include来引入另一个配置文件。如下所示:
<configuration>
<include file="src/main/java/resources/includedConfig.xml"/>
<root level="DEBUG">
<appender-ref ref="includedConsole" />
root>
configuration>
includedConfig.xml文件定义了被引用的内容:
<included>
<appender name="includedConsole" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>"%d - %m%n"pattern>
encoder>
appender>
included>
include标签引入的文件可以为一个文件,一个类路径上的资源,或者一个URL。如下:
<include file="src/main/java/resources/includedConfig.xml"/>
<include resource="includedConfig.xml"/>
<include url="http://xxx.com/includedConfig.xml"/>
如果被引用的文件不存在,logback会打印内部的状态信息。如果包含的文件是可选的,可以通过optional属性设置为true来进制打印显示警告信息。
<include optional="true"/>
如果是基于SpringBoot项目,针对不同环境(profile)有不同的日志输出,比如开发(dev)环境只输出到控制台,生产环境(prod)只输入INFO和ERROR,那则可用到Spring支持的profile机制。对应的元素为springProfile。profile的值与springboot中配置文件的spring.profiles.active值进行对照。
<configuration>
<springProfile name="dev">
<logger name="com.example.demo.controller" level="DEBUG" additivity="false">
<appender-ref ref="consoleLog"/>
logger>
springProfile>
<springProfile name="dev | test">
<logger name="com.example.demo.controller" level="INFO" additivity="false">
<appender-ref ref="consoleLog"/>
logger>
springProfile>
<springProfile name="!production">
<logger name="com.example.demo.controller" level="INFO" additivity="false">
<appender-ref ref="consoleLog"/>
logger>
springProfile>
configuration>
Logback提供有代码方式直接修改日志级别,我们可以获取LoggerContext对象修改日志级别及其他配置。如下是Java代码实现(无配置文件)
package com.xyz;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import org.slf4j.LoggerFactory;
public class LogbackTest {
public static void main(String[] args) {
Logger logger = (Logger)LoggerFactory.getLogger(LogbackTest.class);
System.out.println("============使用默认的日志输出级别DEBUG==============");
printLogs(logger);
System.out.println("============动态设置日志输出级别为ERROR==============");
// 返回正在使用的 ILoggerFactory 实例.
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
// 根据包路径或类路径获取日志记录器,"ROOT" 表示根记录器: Logger getLogger(final String name);
loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).setLevel(Level.toLevel("error"));
printLogs(logger);
System.out.println("============动态设置日志输出级别为WARN==============");
loggerContext.getLogger("com.xyz").setLevel(Level.valueOf("warn"));
printLogs(logger);
}
private static void printLogs(Logger logger) {
logger.error("test error...");
logger.warn("test warn...");
logger.info("test info...");
logger.debug("test debug...");
logger.trace("test trace...");
}
}
============使用默认的日志输出级别DEBUG==============
18:16:51.570 [main] ERROR com.xyz.LogbackTest - test error...
18:16:51.570 [main] WARN com.xyz.LogbackTest - test warn...
18:16:51.570 [main] INFO com.xyz.LogbackTest - test info...
18:16:51.570 [main] DEBUG com.xyz.LogbackTest - test debug...
============动态设置日志输出级别为ERROR==============
18:16:51.570 [main] ERROR com.xyz.LogbackTest - test error...
============动态设置日志输出级别为WARN==============
18:16:51.570 [main] ERROR com.xyz.LogbackTest - test error...
18:16:51.570 [main] WARN com.xyz.LogbackTest - test warn...
为了释放 logback-classic 资源,停止 logback context 是一个好主意。如果停止,会关闭所有 loggers 关联的 appenders,并有序的停止所有活动线程。
import org.sflf4j.LoggerFactory;
import ch.qos.logback.classic.LoggerContext;
// 返回正在使用的 ILoggerFactory 实例.
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.stop(); // stop logback-classic
logback-access模块与Servlet容器(如Tomcat和Jetty)集成,以提供HTTP访问日志功能。我们可以使用logback-access模块来替换tomcat的访问日志
将logback-access.jar与logback-core.jar复制到$TOMCAT_HOME/lib/目录下(可以从本地的maven库直接copy)
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-coreartifactId>
<version>1.2.3version>
dependency>
<dependency>
<groupId>ch.qos.logbackgroupId>
<artifactId>logback-accessartifactId>
<version>1.2.3version>
dependency>
修改$TOMCAT_HOME/conf/server.xml中的Host元素中添加:
<Valve className="ch.qos.logback.access.tomcat.LogbackValve" />
logback默认会在$TOMCAT_HOME/conf下查找文件:logback-access.xml
<configuration>
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener"/>
<property name="LOG_DIR" value="${catalina.base}/logs"/>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_DIR}/access.logfile>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>access.%d{yyyy-MM-dd}.log.zipfileNamePattern>
rollingPolicy>
<encoder>
<pattern>combinedpattern>
encoder> appender>
<appender-ref ref="FILE"/>
configuration>
结果验证:启动tomcat,刷新页面,查看日志:logs/access.log
127.0.0.1 - - [06/四月/2022:10:43:40 +0800] "GET / HTTP/1.1" 200 - "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.55"
127.0.0.1 - - [06/四月/2022:10:43:40 +0800] "GET / HTTP/1.1" 200 - "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.74 Safari/537.36 Edg/99.0.1150.55"
官方配置:https://logback.qos.ch/access.html#configuration
此方式可以用在任何日志实现框架中使用,如:Logback、log4j2 等
上面所讲的日志都是在单个应用系统下记录日志的。一旦进入分布式系统,很可能就会出现日志错乱,对日志追踪和排查造成难题。如果使用像ELK这类框架将日志进行归集统一处理,也需要一个标识,来记录日志的整个请求处理过程。
Slf4j提供了MDC(Mapped Diagnostic Contexts诊断上下文映射),可以让开发人员在诊断上下文中放置信息。通过ThreadLocal实现了线程与线程之间的数据隔离。在输出时可以通过标识符%X{key}来输出MDC中设置的内容。【此方式可以用在任何日志实现框架中使用,如:Logback、log4j2 …】
分布式应用追踪请求实现思路如下:
Web拦截器增加唯一ID »» 增加ID到MDC中 »» 调用其他服务时ID作为Header参数 »» 输出日志添加ID »» 请求结束,清除ID »» 根据相同ID排查日志
下面来看一下具体的实现代码。这里使用的是SpringBoot + Filter,也可以使用HandlerInterceptor拦截器(效果一样)
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.UUID;
import org.slf4j.MDC;
public class CorrelationIdLoggingFilter implements Filter {
private static final String CORRELATION_ID = "correlation_id";
private static final String UU_ID = "uu_id";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String correlationId = req.getHeader(CORRELATION_ID);
MDC.put(CORRELATION_ID, correlationId);
String uuId = UUID.randomUUID().toString();
MDC.put(UU_ID, uuId);
chain.doFilter(request, response);
MDC.remove(CORRELATION_ID);
MDC.remove(UU_ID);
}
}
@Configuration
public class FilterRegistration {
@Bean
public FilterRegistrationBean correlationIdLoggingFilterRegistrationBean() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new CorrelationIdLoggingFilter()); // 可以new也可以Bean注入
registration.setName("correlationIdLoggingFilter"); // 设置过滤器名称
registration.addUrlPatterns("/*"); // 拦截路径
registration.setOrder(-1); // 设置执行顺序,数字越低优先级越高
return registration;
}
}
<property name="log_pattern" value="%d{MM-dd HH:mm:ss.SSS} [%X{uu_id}] [%X{correlational_id}] [%5p] [%40.40c{1.}:%3L] - %m%n"/>
logging.pattern.console=%d{MM-dd HH:mm:ss.SSS} [%X{uu_id}] [%X{correlation_id}] [%5p] [%40.40c{1.}:%3L] - %m%n
启动后测试可以看出:启动时候没有correlation_id与uu_id值,当有请求进来才会生成:
curl -H 'Content-Type:application/json' -H 'correlation_id:xxxxx-yyyyy-zzzzz' http://localhost:8080/logback/logs
04-15 12:16:29.740 [] [] [ INFO] [ o.s.b.w.e.t.TomcatWebServer:220] - Tomcat started on port(s): 8080 (http) with context path ''
04-15 12:16:29.750 [] [] [ INFO] [ c.x.XyzApplication: 61] - Started XyzApplication in 1.504 seconds (JVM running for 2.672)
04-15 12:16:51.659 [] [] [ INFO] [ o.a.c.c.C.[.[.[/]:173] - Initializing Spring DispatcherServlet 'dispatcherServlet'
04-15 12:16:51.659 [] [] [ INFO] [ o.s.w.s.DispatcherServlet:525] - Initializing Servlet 'dispatcherServlet'
04-15 12:16:51.660 [] [] [ INFO] [ o.s.w.s.DispatcherServlet:547] - Completed initialization in 1 ms
04-15 12:16:51.678 [e91e1660-69d2-4fd8-bdcb-304c0cd89c70] [xxxxx-yyyyy-zzzzz] [ERROR] [ c.x.Log4j2Controller: 24] - hello, I am error!
04-15 12:16:51.678 [e91e1660-69d2-4fd8-bdcb-304c0cd89c70] [xxxxx-yyyyy-zzzzz] [ WARN] [ c.x.Log4j2Controller: 25] - hello, I am warn!
04-15 12:16:51.680 [e91e1660-69d2-4fd8-bdcb-304c0cd89c70] [xxxxx-yyyyy-zzzzz] [ INFO] [ c.x.Log4j2Controller: 26] - hello, I am info!
SpringBoot官网文档:https://docs.spring.io/spring-boot/docs/2.5.0/reference/html/features.html#features.logging
spring-boot-starter是SpringBoot启动器,每一个SpringBoot应用都会依赖到它
sping-boot-starter依赖Sping-boot-starter-looging
SpringBoot 底层默认使用 SLF4J+ Logback 方式进行日志记录
SpringBoot 使用中间替换包把其的日志框架都替换成了SLF4J
SpringBoot 能自动适配所有的日志框架,且底层使用SLF4J + Logback方式记录日志,引入其他框架时,只需要把这个框架依赖的日志框架排除掉即可
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-loggingartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-log4j2artifactId>
dependency>
SpringBoot官网配置文件详解:https://docs.spring.io/spring-boot/docs/2.5.0/reference/htmlsingle/#application-properties.core
日志的级别由低到高分别为:trace(跟踪) < debug(调试) < info(信息) < warn(警告) < error(错误)
在配置文件中调整输出的日志级别,日志就只会在这个级别及以后的高级别生效,SpringBoot 默认使用 INFO 级别(Logback 默认是 DEBUG)
SpringBoot 的全局配置文件 “application.properties” 或 “application.yml” 中可以修改日志配置项:
# 默认名logback-spring.xml,如果要设置其他名称则需要如下配置
# logging.config=classpath:logback-spring.xml
# 设置根节点的日志级别输出,root表示整个项目
logging.level.root=INFO
# 指定特定包及类的日志输出级别,未指定就按root设置的级别输出,如果root也未指定则按SpringBoot的默认级别info输出
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate=ERROR
logging.level.com.xyz=DEBUG
# 配置日志输出的文件,这两个选一个配置就可以了,一起配置的话,name的生效.每次启动都是追加日志
# .name=具体文件, 写入指定的日志文件.文件名可以是确切的位置,也可以是相对于当前目录的位置(相对路径为与pom.xml同级)
# .path=具体目录, 写入spring.log文件到指定目录.目录名可以是确切的位置,也可以是相对于位置(文件名spring.log无法更改)
logging.file.name=logs/spring-boot.log
logging.file.path=logs/
# 指定控制台输出的日志格式,例如: %d{yyyy-MM-dd HH:mm:ss} [%X{uu_id}] -- [%thread] %-5level %logger{50} %msg%n
# 1、%d 表示日期时间,
# 2、%thread 表示线程名,
# 3、%‐5level 级别从左显示5个字符宽度
# 4、%logger{50} 表示logger名字最长50个字符,否则按照句点分割。
# 5、%msg 日志消息,
# 6、%n 换行符
# 7、%line 显示日志输出位置的行号,方便寻找位置
# 8、%X 特殊占位符,%X{uu_id}是获取uu_id的值,代码中设置uu_id值:org.slf4j.MDC.put("uuid", "xx-yy-zz");
# 配置日志输出格式, .file是配置输出到文件的日志格式, .console是配置输出到控制台的日志格式
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} %-5level [%thread] %logger{50}:%line %msg%n
logging.pattern.file=%d{yyyy-MM-dd HH:mm} -- [%thread] %-5level %logger{50} %msg%n
# 设置日志记录器组,将相关的记录器组合在一起,然后设置日志记录器组的日志级别为TRACE
logging.group.tomcat=org.apache.catalina, org.apache.coyote, org.apache.tomcat
logging.level.tomcat=TRACE
# SpringBoot包括以下预定义的日志记录组,可以开箱即用
# web: org.springframework.core.codec, org.springframework.http, org.springframework.web
# sql: org.springframework.jdbc.core, org.hibernate.SQL
logging.level.web=INFO
logging.level.sql=DEBUG
SpringBoot全局配置中的日志配置优先级高于第三方日志框架配置文件(application.properties > logback-spring.xml)
logging.file.name | logging.file.path | 示例 | 说明 |
---|---|---|---|
(none) | (none) | 只在控制台输出 | |
指定文件名 | (none) | demo.log | 输出到当前项目根路径下的 demo.log 文件中 |
(none) | 指定目录 | logs/log_lzy | 输出到当前项目所在磁盘根路径下的/logs/log_lzy目录中的 spring.log 文件中 |
指定文件名 | 指定目录 | 当两个同时指定时, name会生效。推荐使用logging.file.name设置,因为它可自定义文件名 |
# 配置日志输出的文件,这两个选一个配置就可以了,一起配置的话,name的生效. 每次启动都是追加日志
# .name=具体文件, 写入指定的日志文件.文件名可以是确切的位置,也可以是相对于当前目录的位置(相对路径为与pom.xml同级)
# .path=具体目录, 写入spring.log文件到指定目录.目录名可以是确切的位置,也可以是相对于位置(文件名spring.log无法更改)
logging.file.name=logs/spring-boot.log
logging.file.path=logs/
- 默认情况:日志文件超过10M时,会新建文件进行递增,如logback.log、logback1.log、logback2.log,使用logging.file.max-size更改大小限制
- 默认情况:日志只记录到控制台,不写入日志文件,如果要在控制台输出之外写入到日志文件,则需要设置 .file 或 .path 属性
- 默认情况,logging.file.* 等配置使用的都是RollingFileAppender
日志输出到文件是非常有用的,比如命令行启动一个 jar 包,但是它一执行就自动报错,然后退出了,还来不急看清错误信息,此时可以修改配置把日志输出到指定文件中,再次启动错误信息就会自动存放进去
SpringBoot Log Levels 日志级别官方文档:https://docs.spring.io/spring-boot/docs/2.5.0/reference/htmlsingle/#features.logging.log-levels
通过配置logging.level.
可以设置所有受支持的日志系统的日志级别,其中 level 是 ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF 之一
可以使用 logging.level.root 配置根日志记录器:
# 未特别指定的仍然按 Spring Boot 的默认的 logging.level.root 设置输出.
logging.level.root=WARN # 根节点日志级别,即整个应用的日志级别设置,默认为 info
logging.level.org.springframework.web=DEBUG # 单独某个包的日志级别设置,spring web 包日志输出级别
logging.level.org.hibernate=ERROR # 单独某个包的日志级别设置,hibernate 日志输出级别
logging.level.com.xzy=info # 自己项目中指定包下的日志输出级别
SpringBoot Log Groups 日志记录器组官方文档:https://docs.spring.io/spring-boot/docs/2.5.0/reference/htmlsingle/#features.logging.log-groups
将相关的记录器组合在一起,以便可以同时对它们进行配置,这通常是很有用的。Spring Boot 允许在 Spring 环境中定义日志组。例如下面通过将 “tomcat” 组添加到 application.properties 中来定义它,定义后,可以用一行更改组中所有记录器的级别
logging.group.tomcat=org.apache.catalina, org.apache.coyote, org.apache.tomcat
logging.level.tomcat=TRACE
SpringBoot 包括以下预定义的日志记录组,可以开箱即用:
Name | Loggers |
---|---|
web | org.springframework.core.codec, org.springframework.http, org.springframework.web |
sql | org.springframework.jdbc.core, org.hibernate.SQL |
logging.level.web=INFO
logging.level.sql=DEBUG
使用Mybatis的时候,SQL语句是DEBUG下才会打印,而这里我们只配置了INFO,所以想要查看SQL语句的话,有以下两种操作:
1、第一种把
改成
这样就会打印SQL,不过这样日志那边会出现很多其他消息
2、第二种就是单独给dao下目录配置debug模式,代码如下,这样配置sql语句会打印,其他还是正常INFO级别:
application.properties 全局配置
logging.level.sql=DEBUG
logging.level.org.mybatis=debug
logging.level.com.xyz.*.dao=debug
logback-spring.xml 自定义配置文件配置
<configuration debug="true" scan="true" scanPeriod="60 seconds">
<logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE" />
<logger name="org.hibernate.type.descriptor.sql.BasicExtractor" level="DEBUG" />
<logger name="org.hibernate.SQL" level="DEBUG" />
<logger name="org.hibernate.engine.QueryParameters" level="DEBUG" />
<logger name="org.hibernate.engine.query.HQLQueryPlan" level="DEBUG" />
<logger name="com.apache.ibatis" level="TRACE"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
configuration>
可以在启动的时候通过命令行参数指定日志输出级别(java -jar xxx.jar --level),相当于 logging.level.root 配置的根日志记录器。
java -jar .java-se-1.0-SNAPSHOT.jar --debug
可以直接在 SpringBoot 的全局配置文件中修改 SLF4J 的默认配置,也可以使用 SLF4J 实现框架的自己的配置文件。直接放置再类路径下即可
官网参考链接,根据日志记录系统的不同,各自的配置文件文件也不同:
Logging System(日志系统) | Customization(日志文件名称) |
---|---|
Logback | logback-spring.xml,logback-spring.groovy,logback.xml or logback.groovy |
Log4j2 | log4j2-spring.xml,log4j2.xml |
JDK (Java Util Logging) | logging.properties |
因为 SpringBoot 底层默认采用 SLF4J+Logback 的日志组合,也就是说如果在 src/main/resources 目录下放置其中任一类型的配置文件,SpringBoot便会自动进行使用。而SpringBoot官方推荐优先使用带有-spring的文件名配置(如有logback-spring.xml,则不会使用logback.xml)。若需要对配置文件名进行修改,或者希望把放到其它目录下,可以在application中通过logging.config属性来指定,如:
logging.config=classpath:dev/logback-spring.xml
具体配置文件设置详情可参考上面的教程:【配置文件结构详解】
这是转载其他人整理的百分号属性参数说明大全:
********************************************************************************************************************
参数 说明 举例 输出显示媒介
********************************************************************************************************************
%c 列出logger名字空间的全称,如果加上{<层数>}, 假设当前logger的命名空间是"a.b.c"
则表示列出从最内层算起的指定层数的名字空间
%c a.b.c
%c{2} b.c
%20c (若名字空间长度小于20,则左边用空格填充)
%-20c (若名字空间长度小于20,则右边用空格填充)
%.30c (若名字空间长度超过30,截去多余字符)
%20.30c (若名字空间长度小于20,则左边用空格填充;
若名字空间长度超过30,截去多余字符)
%-20.30c (若名字空间长度小于20,则右边用空格填充;
若名字空间长度超过30,截去多余字符)
********************************************************************************************************************
%C 列出调用logger的类的全名(包含包路径) 假设当前类是"org.apache.xyz.SomeClass"
%C org.apache.xyz.SomeClass
%C{1} SomeClass
%class
********************************************************************************************************************
%d 显示日志记录时间,{<日期格式>}使用ISO8601定义的日期格式
%d{yyyy/MM/dd HH:mm:ss,SSS} 2005/10/12 22:23:30,117
%d{ABSOLUTE} 22:23:30,117
%d{DATE} 12 Oct 2005 22:23:30,117
%d{ISO8601} 2005-10-12 22:23:30,117
********************************************************************************************************************
%F 显示调用logger的源文件名 %F MyClass.java
********************************************************************************************************************
%l 显示日志事件的发生位置,包含包路径、方法名、
源文件名,以及在代码中的行数 %l com.a.b.MyClass.main(MyClass.java:168)
********************************************************************************************************************
%L 显示调用logger的代码行 %L 129
%line %line 129
********************************************************************************************************************
%level 显示该条日志的优先级 %level INFO
%p %p INFO
********************************************************************************************************************
%m 显示输出消息 %m This is a message for debug.
%message %message This is a message for debug.
********************************************************************************************************************
%M 显示调用logger的方法名 %M main
********************************************************************************************************************
%n 当前平台下的换行符 %n Windows平台下表示rn,UNIX平台下表示n
********************************************************************************************************************
%p 显示该条日志的优先级 %p INFO
%level %level INFO
********************************************************************************************************************
%r 显示从程序启动时到记录该条日志时已经经过的毫秒数 %r 1215
********************************************************************************************************************
%t 输出产生该日志事件的线程名 %t http-nio-8080-exec-10
%thread %thread http-nio-8080-exec-10
********************************************************************************************************************
%x 按NDC(Nested Diagnostic Context,线程堆栈)顺序输出日志 假设某程序调用顺序是MyApp调用com.foo.Bar
%c %x - %m%n MyApp - Call com.foo.Bar.
com.foo.Bar - Log in Bar
MyApp - Return to MyApp.
********************************************************************************************************************
%X 按MDC(Mapped Diagnostic Context,线程映射表)
输出日志。通常用于多个客户端连接同一台服务器,
方便服务器区分是那个客户端访问留下来的日志。 %X{5} (记录代号为5的客户端的日志)
********************************************************************************************************************
%% 显示一个百分号 %% %
********************************************************************************************************************
修改 logback.xml 配置文件,定时监控配置变化情况。
<configuration scan="true" scanPeriod="60 seconds" debug="false"/>
使用Controller接口动态修改日志输出级别,想设置什么级别,只需传参调接口即可,无论是设置root级别,还是指定包或类,不用重启服务能立马生效
package com.example.xyz;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LogbackController {
private static final Logger log = (Logger) LoggerFactory.getLogger(LogbackController.class);
/**
* 打印日志测试: http://localhost:8080/logback/logs
*/
@GetMapping("logback/logs")
public void logs() {
log.error("hello, I am error!");
log.warn("hello, I am warn!");
log.info("hello, I am {}!", "info");
log.debug("hello, I am debug!");
log.trace("hello, I am trace!");
}
/**
* logback 动态切换指定包或者类的日志输出级别,立即生效.
* http:localhost:8080/logback/updateLevel
* http:localhost:8080/logback/updateLevel?level=DEBUG
* http:localhost:8080/logback/updateLevel?level=DEBUG&clazz=com.xyz
*
* @param level :日志级别,可选值有:ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF,默认为 debug。不区分大小写.
* @param clazz :指定的包或者类路径,为空时默认设置全局(root)日志级别。路径不存在时,照样会设置这个路径,不会影响全局级别,这一点与 log4j2 不同.
* 比如 org.springframework。
* @return map :返回指定路径的当前日志级别,root 表示全局级别
*/
@GetMapping(value = "/logback/updateLevel")
public String setLevel(String level, String clazz) {
try {
// 返回正在使用的 ILoggerFactory 实例.
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
/**
* Logger getLogger(final String name):根据包路径或类路径获取日志记录器,"ROOT" 表示根记录器
* Level toLevel(String sArg):转换为日志级别,如果转换失败,则默认为 debug
* synchronized void setLevel(Level newLevel):为日志记录器设置日志输出级别,立即生效.
*/
if (clazz == null || "".equals(clazz.trim())) {
// 设置根日志记录器对象并设置日志输出级别
loggerContext.getLogger(Logger.ROOT_LOGGER_NAME).setLevel(Level.toLevel(level));
} else {
// 设置指定日志记录器并设置日志输出级别,路径不存在也没有关系,会返回它的日志记录器,然后设置输出级别
loggerContext.getLogger(clazz).setLevel(Level.toLevel(level));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
return "fail";
}
return "success";
}
}
使用SpringBoot Actuator监控管理日志的优点:
1、引入pring Boot Actuator依赖
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
2、开启日志访问端点: /loggers
management:
endpoint:
health:
show-details: ALWAYS # 展示节点的详细信息
endpoints:
web:
exposure:
include: info,health,logfile,loggers # 指定公开的访问端点
3、查看级别
4、修改日志级别