SpringBoot--学会配置日志
<h1 id="理解springboot-的日志设计">理解SpringBoot 的日志设计</h1><p>在项目中导入spring-boot-starter.jar依赖,它会传递 导入spring-boot-starter-logging.jar依赖,依赖关系如下图:</p>
<p><img src="https://raw.githubusercontent.com/growingbambi/typora/master/ali2_5_1.png"></p>
<p>spring-boot-starter-logging.jar依赖三个jar包:</p>
<ol>
<li>logback-classic.jar:它传递依赖于logback-core.jar和slf4j-api.jar</li>
<li>log4j-to-slf4j.jar:它传递依赖于log4j-api.jar和slf4j-api.jar</li>
<li>jul-to-slf4j.jar:它传递依赖于slf4j-api.jar</li>
</ol>
<p>java的日志框架比较多,常见的包括:SLF4J、Log4j、Log4j2、Logback、common-logging(JCL)、java.util.logging(JUL)、JBoss Logging等,这些日志框架又分:</p>
<ol>
<li>门面类(抽象层):SLF4J、JCL、JBoss Logging</li>
<li>日志实现:Log4j、Log4j2、Logback、common-logging(JCL)</li>
</ol>
<p>SpringBoot默认使用SLF4J+Logback组合,SLF4J作为日志门面(应用程序输出日志时应该面向改API),Logback作为日志实现。</p>
<p>由于SpringBoot要整合大量的第三方框架,这些框架可能使用JCL、Log4j、JUL等。因此SpringBoot提供对应的日志路由,将其他框架生成的日志信息统一路由给SLF4J处理。从上面依赖关系可以看出:</p>
<ol>
<li>log4j-to-slf4j.jar:负责将Log4j日志路由到SLF4J</li>
<li>jul-to-slf4j.jar:负责将JUL日志路由到SLF4J</li>
</ol>
<p>虽然SpringBoot默认采用Logback作为底层日志实现,但通过配置允许将底层日志实现改为其他框架。SpringBoot允许将Logback依赖排除出去,添加其他日志实现(比如log4j)的依赖。</p>
<p>需要注意的是:<strong>当吧SpringBoot应用部署到Web服务器或应用服务器上时,JUL生成的日志不会被路由到SpringBoot应用的日志中</strong>,这是为了避免将服务器或者服务器上的其他应用的日志也路由到SpringBoot的日志中,否则会造成日志混乱。</p>
<h1 id="日志级别与格式">日志级别与格式</h1>
<p>代码示例:控制器类</p>
<pre><code class="language-java">@RestController
public class HelloController
{
Logger logger = LoggerFactory.getLogger(this.getClass());
@GetMapping
public Map<String, Object> hello()
{
logger.trace("-------TRACE级别的日志-------");
logger.debug("-------DEBUG级别的日志-------");
logger.info("-------INFO级别的日志-------");
logger.warn("-------WARN级别的日志-------");
logger.error("-------ERROR级别的日志-------");
return Map.of("hello", "Hello");
}
}
</code></pre>
<p>日志级别主要分为(级别由低到高):</p>
<ol>
<li>all:输出所有日志</li>
<li>trace</li>
<li>debug</li>
<li>info</li>
<li>warn</li>
<li>error</li>
<li>fatal:log4j增加的一种日志级别,代表“致命错误”,比error级别更高。(由于SpringBoot不支持此级别,因此会被自动转换为error级别)</li>
<li>off:关闭所有日志</li>
</ol>
<p>日志系统有一个规则:<strong>当日志的输出方法的级别高于或等于日志的设置级别,该日志才会实际输出。</strong></p>
<p>比如日志级别设为info,当程序使用info()、warn()、error()输出时,日志才会实际输出;使用trace()、debug()输出的日志会被忽略。</p>
<p>因此,日志级别越高,输出日志就越精简,性能开销越小;日志级别越低,输出日志就越详细,性能开销越大。一般项目处于开发、测试、试运行阶段,日志级别设置的低;在项目实际运行阶段,日志级别设置的高。</p>
<p><img src="https://raw.githubusercontent.com/growingbambi/typora/master/ali2_5_3.png"></p>
<p>由上图可知,SpringBoot默认的日志级别是Info。</p>
<p>SpringBoot输出的日志包括如下信息:</p>
<ol>
<li>日期和时间:精确到毫秒</li>
<li>日志级别</li>
<li>进程ID</li>
<li>分隔符:三个减号(---)</li>
<li>线程名:方括号里面的内容(在控制台输出时可能会被截断)</li>
<li>日志名:通常是完整类名(为了便于阅读,包名经常简写)</li>
<li>日志信息</li>
</ol>
<p>设置日志级别的几种方式:</p>
<ol>
<li>通过debug=true或trace=true等属性(可通过配置文件、命令行参数、系统变量、OS环境变量等方式)改变整个SpringBoot核心的日志级别</li>
<li>通过logging.level.<logger-name>=<level>属性(可通过配置文件、命令行参数、系统变量等方式)设置日志级别。其中<logger-name>代表日志名,通常是包名或全限定类名,而Level可以是各种日志级别。</logger-name></level></logger-name></li>
</ol>
<p><strong>需要注意的是,当启用trace或debug模式时,SpringBoot的核心日志(包括嵌入式容器、Hibernate和SpringBoot)被设为对应的级别,但是其他程序组件不会被设为对应级别。</strong></p>
<p>例如:添加命令行参数: --trace 。启动上面代码运行结果如下:</p>
<p><img src="https://raw.githubusercontent.com/growingbambi/typora/master/ali2_5_4.png"></p>
<p>可见,程序组件本身的日志级别没有改变。</p>
<p>那么要设置程序组件的日志级别,要通过logging.level.<logger-name>=<level>属性来设置</level></logger-name></p>
<pre><code class="language-yaml">logging:
level:
# 将org.crazyit.app包及其子包下所有日志级别设为TRACE
org.crazyit.app: trace
</code></pre>
<p>运行结果如下</p>
<p><img src="https://raw.githubusercontent.com/growingbambi/typora/master/ali2_5_5.png"></p>
<p>学会之后解决以下几个问题就很简单了。</p>
<ol>
<li>让Mybatis输出SQL语句(logging.level.<mapper组件所在的包>=debug)</li>
<li>输出Redis的详细执行过程(logging.level.io.lettuce.core=debug)</li>
<li>输出MongoDB的详细执行过程(logging.level.com.mongodb=debug)</li>
</ol>
<p>SpringBoot允许通过spring.output.ansi.enabled属性设置是否用不同颜色来区分不同级别日志,该属性支持以下属性值:</p>
<ol>
<li>always:总是启用</li>
<li>detect:自动检查。如果控制台支持ansi颜色特性,则启用。这是默认值。</li>
<li>never:不启用</li>
</ol>
<p>如果要改变控制台的日志格式,可通过logging.pattern.console属性进行设置。其默认值是:</p>
<p>%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint}<br>
%clr(${LOG_LEVEL_PATTERN:-%5p})<br>
%clr(${PID:- }){magenta}<br>
%clr(---){faint}<br>
%clr([%15.15t]){faint}<br>
%clr(%-40.40logger{39}){cyan}<br>
%clr(😃{faint}<br>
%m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}</p>
<p>上面配置由多个“%clr(输出内容){颜色值} ”片段组成,每个片段代表一个输出元素,其中{颜色值} 用于指定该片段的颜色。此处颜色值支持如下几个值:</p>
<ol>
<li>blue:蓝色</li>
<li>cyan:青色</li>
<li>faint:原色</li>
<li>green:绿色</li>
<li>magenta:紫红色</li>
<li>red:红色</li>
<li>yellow:黄色</li>
</ol>
<p>如果不指定颜色之后,直接使用“%clr(输出内容)”,表面使用默认的颜色。</p>
<p>比如上面设置包含以下片段:</p>
<ol>
<li>%clr(${LOG_LEVEL_PATTERN:-%5p}) :表明以日志级别对应的颜色来输出</li>
<li>%clr(${PID:- }){magenta} :以紫红色输出进程ID</li>
<li>%clr(---){faint} :以原色来输出三个减号(---)</li>
</ol>
<p>假如输出日志不要显示日期、时间,设置时去掉“%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} ”片段即可。</p>
<p><strong>需要注意的是:logging.pattern.console属性仅当使用Logback日志实现时才有效。</strong></p>
<h1 id="输出日志到文件">输出日志到文件</h1>
<p>要将日志输出到文件,设置如下两个属性之一:</p>
<ol>
<li>logging.file:设置日志文件</li>
<li>logging.path:设置日志文件目录。使用默认的spring.log作为文件名。</li>
</ol>
<p><strong>使用logging.file或logging.path属性输出日志到文件</strong></p>
<table>
<thead>
<tr>
<th style="text-align: center">logging.file.name</th>
<th style="text-align: center">logging.file.path</th>
<th style="text-align: center">示例</th>
<th style="text-align: center">描述</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center">无</td>
<td style="text-align: center">无</td>
<td style="text-align: center"></td>
<td style="text-align: center">只输出到控制台</td>
</tr>
<tr>
<td style="text-align: center">指定文件</td>
<td style="text-align: center">无</td>
<td style="text-align: center">my.log</td>
<td style="text-align: center">输出到特定文件,文件路径可以是绝对或相对路径</td>
</tr>
<tr>
<td style="text-align: center">无</td>
<td style="text-align: center">指定目录</td>
<td style="text-align: center">/f:/log</td>
<td style="text-align: center">写入指定路径下的spring.log文件,该路径可以是绝对或相对路径</td>
</tr>
</tbody>
</table>
<p>SpringBoot默认只将info、warn、error三个级别的日志输出到文件。</p>
<p>当日志文件达到10MB时,会自动使用新文件。若要改变这个设置,对于Logback日志实现(SpringBoot默认),可直接使用application.properties(或application.yml)设置;对于其他日志实现,需要对应的日志设置文件来设置。比如log4j,需要使用log4j.xml设置。</p>
<p><strong>Logback的日志设置</strong></p>
<table>
<thead>
<tr>
<th>属性名称</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>logging.logback.rollingpolicy.file-name-pattern</td>
<td>设置对日志归档的文件名模版</td>
</tr>
<tr>
<td>logging.logback.rollingpolicy.clean-history-on-start</td>
<td>应用启动时是否清除日志文档</td>
</tr>
<tr>
<td>logging.logback.rollingpolicy.max-file-size</td>
<td>日志文件归档之前的最大大小</td>
</tr>
<tr>
<td>logging.logback.rollingpolicy.total-size-cap</td>
<td>日志归档被删除之前所能容纳的最大大小</td>
</tr>
<tr>
<td>logging.logback.rollingpolicy.max-history</td>
<td>设置保留多少天的日志归档(默认7天)</td>
</tr>
</tbody>
</table>
<p>代码示例:控制器类org.crazyit.app.controller</p>
<pre><code class="language-java">@RestController
@Slf4j
public class HelloController
{
@GetMapping
public Map<String, Object> hello()
{
log.trace("-------TRACE级别的日志-------");
log.debug("-------DEBUG级别的日志-------");
log.info("-------INFO级别的日志-------");
log.warn("-------WARN级别的日志-------");
log.error("-------ERROR级别的日志-------");
return Map.of("hello", "Hello");
}
}
</code></pre>
<p>上面的控制器类使用@Slf4j注解修饰,里面的方法可直接使用 log.trace()等方法来输出日志,那这个log对象是从哪来的?</p>
<p>因为本例使用Lombok工具,这个工具专门通过各种注解来生成常用的代码,比如以下常用的注解:</p>
<ol>
<li>@Getter:为所有实例变量生成getter方法</li>
<li>@Setter:为所有非final实例变量生成setter方法</li>
<li>@ToString:自动生成toString()方法</li>
<li>@EqualsAndHashCode:自动生成equals()和hashCode()方法</li>
<li>@AllArgsConstructor:自动生成带所有参数的构造器</li>
<li>@NoArgsConstructor:自动生成无参构造器</li>
<li>@Data:自动生成一个数据类,相当于@Getter、@Setter、@ToString、@EqualsAndHashCode、@AllArgsConstructor、@NoArgsConstructor等注解的组合</li>
<li>@Log、@Log4j、@Log4j2、@Slf4j、@CommonsLog、@JBossLog、@Flogger:为对应的日志实现生成一个日志对象</li>
</ol>
<p>为了在应用中使用Lombok,需要做一下两件事:</p>
<ol>
<li>
<p>添加Lombok依赖:</p>
<ol>
<li>
<pre><code class="language-xml"><dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
</code></pre>
</li>
</ol>
</li>
<li>
<p>为IDEA添加Lombok插件,通过主菜单FIle-》Settings-》Plugins-》搜索插件,然后安装即可。</p>
</li>
</ol>
<p>代码示例:application-addition.yml</p>
<pre><code class="language-yaml">logging:
level:
# 将org.crazyit.app包及其子包下所有日志级别设为TRACE
org.crazyit.app: trace
file:
# 指定日志文件的输出目录,在应用的根目录下生成logs文件夹,该文件夹下生成默认文件名为spring.log
# path: logs/
# 指定日志文件,生成在应用的根目录下
name: my.log
</code></pre>
<h1 id="日志组">日志组</h1>
<p>日志组就是将多个包、类组合在一起,起一个名字,以后可直接对改组设置日志级别,对改组设置就相当于同时为改组内的所有包及其子包、类统一设置了日志级别。</p>
<p>代码示例:控制器类,为上面的控制器类再增加这个控制器类org.fkjava.app.controller</p>
<pre><code class="language-java">@RestController
@Slf4j
public class FkController
{
@GetMapping("/fk")
public Map<String, Object> hello()
{
log.trace("-------TRACE级别的日志-------");
log.debug("-------DEBUG级别的日志-------");
log.info("-------INFO级别的日志-------");
log.warn("-------WARN级别的日志-------");
log.error("-------ERROR级别的日志-------");
return Map.of("hello", "Hello");
}
}
</code></pre>
<p>代码示例:application-addition.yml</p>
<pre><code class="language-yaml">logging:
group:
# 将org.crazyit.app和org.fkjava.app两个包定义成fkapp组
fkapp: org.crazyit.app, org.fkjava.app
level:
# 将fkapp组对应的包及其子包的所有日志级别设为TRACE
fkapp: trace
</code></pre>
<h1 id="关闭控制台日志">关闭控制台日志</h1>
<p>如果想改变SpringBoot的底层日志实现(放弃Logback),则需要如下2步:</p>
<ol>
<li>去掉Logback依赖库,添加新日志实现的依赖库</li>
<li>在类加载路径的根路径下为新日志提供对应的配置文件</li>
</ol>
<p>SpringBoot默认从类加载路径的根路径下加载日志框架的配置文件,也可通过logging.config属性来设置新的加载路径。</p>
<p>SpringBoot既可以根据底层依赖库自动选择合适的日志实现,也可通过org.springframework.boot.logging.LoggingSystem属性显式指定日志实现。属性值可以是LoggingSystem实现类的全限定类名(比如Log4J2LoggingSystem, LobbackLoggingSystem, JavaLoggingSystem等类的全限定类名);也可设置为none,也就彻底关闭SpringBoot的日志系统了。</p>
<p>注意:由于日志初始化会在ApplicantContext创建之前完成,因此不能通过SpringBoot的配置文件来配置logging.config、org.springframework.boot.logging.LoggingSystem等日志控制属性,只能通过系统属性来设置。</p>
<p><strong>不同日志系统对应的配置文件</strong></p>
<table>
<thead>
<tr>
<th>日志系统</th>
<th>配置文件</th>
</tr>
</thead>
<tbody>
<tr>
<td>Logback</td>
<td>logback-spring.xml, logback-spring.groovy, logback.xml或logback.groovy</td>
</tr>
<tr>
<td>Log4j2</td>
<td>log4j2-spring.xml或log4j2.xml</td>
</tr>
<tr>
<td>JDK(JUL)</td>
<td>logging,properties</td>
</tr>
</tbody>
</table>
<p>注意:SpringBoot推荐使用带-spring的配置文件,比如Logback日志系统,使用logback-spring.xml更好。此外尽量避免使用JUL日志系统,因此JUL的类加载机制会导致一些问题。</p>
<p><strong>对日志进行定制的属性</strong></p>
<table>
<thead>
<tr>
<th>SpringBoot属性</th>
<th>系统属性</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>logging.exception-conversion-word</td>
<td>LOG_EXCEPTION_CONVERSION_WORD</td>
<td>记录异常的转换字</td>
</tr>
<tr>
<td>logging.file.name</td>
<td>LOG_FILE</td>
<td>指定日志文件名</td>
</tr>
<tr>
<td>logging.file.path</td>
<td>LOG_PATH</td>
<td>指定日志输出路径,使用spring.log作为文件名</td>
</tr>
<tr>
<td>logging.pattern.console</td>
<td>CONSOLE_LOG_PATTERN</td>
<td>控制台日志的格式模板</td>
</tr>
<tr>
<td>logging.pattern.dateformat</td>
<td>LOG_DATEFORMAT_PATTERN</td>
<td>日期格式模板</td>
</tr>
<tr>
<td>loggging.charset.console</td>
<td>CONSOLE_LOG_CHARSET</td>
<td>输出控制台日志的字符集</td>
</tr>
<tr>
<td>logging.pattern.file</td>
<td>FILE_LOG_PATTERN</td>
<td>文件日志的格式模板,仅当日志输出到文件时才有效</td>
</tr>
<tr>
<td>logging.charset.file</td>
<td>FILE_LOG_CHARSET</td>
<td>文件日志所用的字符集,仅当日志输出到文件时才有效</td>
</tr>
<tr>
<td>logging.pattern.level</td>
<td>LOG_LEVEL_PATTERN</td>
<td>指定输出日志级别时使用的格式(默认为%5p)</td>
</tr>
<tr>
<td>PID</td>
<td>PID</td>
<td>当前进程ID</td>
</tr>
</tbody>
</table>
<p>SpringBoot为logback提供了一些通用的配置文件,开发者只要导入这些配置文件,即可使用预定义的配置。这些文件位于org/springframework/boot/logging/logback/路径下,其中常用的有:</p>
<ol>
<li>defaults.xml:提供了转换规则及各种通用配置</li>
<li>console-appender.xml:定义一个ConsoleAppender,将日志输出到控制台</li>
<li>file-appender.xml:定义一个RollingFileAppender,将日志输出到文件</li>
</ol>
<p>代码示例:logback-spring.xml</p>
<pre><code class="language-xml"><?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 导入Logback通用的日志配置 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<!-- 定义日志文件 -->
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/>
<!-- 导入输入到文件的日志配置 -->
<include resource="org/springframework/boot/logging/logback/file-appender.xml" />
<!-- 指定将日志输出到文件 -->
<root level="INFO">
<appender-ref ref="FILE" />
</root>
</configuration>
</code></pre>
<p>在上面的配置文件中可指定如下占位符:</p>
<ol>
<li>${PID}:当前进程ID</li>
<li>${LOG_FILE}:代表是否通过外部配置设置了logging.file.name属性</li>
<li>${LOG_PATH}:代表是否通过外部配置设置了logging.file.path属性</li>
<li>${LOG_EXCEPTION_CONVERSION_WORD}:代表是否通过外部配置设置了logging.exception-conversion-word属性</li>
</ol>
<p>上面配置将日志输出到文件,因此必须在application-addition.yml指定logging.file.name或logging.file.path属性。</p>
<p>代码示例:application-addition.yml</p>
<pre><code class="language-yaml">logging:
level:
# 将org.crazyit.app包及其子包下所有日志级别设为TRACE
org.crazyit.app: trace
file:
# 指定日志文件的输出目录,默认文件名为spring.log
# path: logs/
# 指定日志文件
name: my.log
</code></pre>
<p>此例子不会在控制台输出日志,仅在当前目录的my.log文件中输出日志。</p>
<h1 id="改用log4j2日志实现">改用Log4j2日志实现</h1>
<p>去除Logback依赖库,添加Log4j2依赖库</p>
<pre><code class="language-xml"><!-- Spring Web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 去除spring-boot-starter-logging日志 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
</code></pre>
<p>通过上面配置,项目底层日志框架改成了Log4j2,得益于SpringBoot的日志的抽象机制,上层程序使用的日志没有任何改变。</p>
<p>如果要对Log4j2自定义配置,可通过log4j2.yml或log4j2.json配置。</p>
<h1 id="logback扩展">Logback扩展</h1>
<p>可通过logback-spring.xml对Logback配置扩展功能。<strong>注意:不能使用logback.xml,因为该文件的加载时机太早,SpringBoot的其他基础功能还没来得及加载。</strong></p>
<p>springProfile标签的name属性可指定相关Profile的日志配置</p>
<p>代码示例:logback-spring.xml</p>
<pre><code class="language-xml"><?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 以下配置仅当活动Profile为default、dev和test时有效 -->
<springProfile name="default | dev | test">
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<include resource="org/springframework/boot/logging/logback/file-appender.xml" />
<root level="DEBUG">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
<!-- 指定org.crazyit.app日志的级别是DEBUG -->
<logger name="org.crazyit.app" level="DEBUG"/>
</springProfile>
<!-- 以下配置仅当活动Profile为prod时有效 -->
<springProfile name="prod">
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<property name="LOG_FILE" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<include resource="org/springframework/boot/logging/logback/file-appender.xml" />
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
<!-- 指定org.crazyit.app日志的级别是INFO -->
<logger name="org.crazyit.app" level="INFO"/>
</springProfile>
</configuration>
</code></pre>
<p>此外,logback的配置文件还可读取SpringBoot的配置属性,通过<springProperty.../> 元素来获取,该元素支持以下属性:</p>
<ol>
<li>name:为读取到的属性值指定名字</li>
<li>source:指定读取哪个配置属性。推荐使用“烤串”写法(比如application-dev)</li>
<li>scope:指定存储该配置属性的作用域</li>
<li>defaultValue:当配置属性不存在时,指定默认值</li>
</ol>
<p>比如:</p>
<pre><code class="language-xml"><springProperty scope="context" name="fluentHost" source="myapp.fluentd.host" defaultValue="localhost" />
<appender name="FLUENT" class="ch.qos.logback.more.appenders.DataFluentAppender">
<!-- 使用前面定义的fluentHost属性-->
<remoteHost>${fluentHost}</remoteHost>
</appender>
</code></pre>
</div>
<div id="MySignature" role="contentinfo">
<p>本文来自博客园,作者:NE_STOP,转载请注明原文链接:https://www.cnblogs.com/alineverstop/p/18986983</p><br><br>
来源:https://www.cnblogs.com/alineverstop/p/18986983
頁:
[1]