<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>日志处理 | Next-gen Tech Edu</title><link>https://ng-tech.icu/books/spring-notes/01.%E5%9F%BA%E7%A1%80%E4%BD%BF%E7%94%A8/4.%E8%AF%B7%E6%B1%82%E4%B8%8E%E5%93%8D%E5%BA%94/%E6%97%A5%E5%BF%97%E5%A4%84%E7%90%86/</link><atom:link href="https://ng-tech.icu/books/spring-notes/01.%E5%9F%BA%E7%A1%80%E4%BD%BF%E7%94%A8/4.%E8%AF%B7%E6%B1%82%E4%B8%8E%E5%93%8D%E5%BA%94/%E6%97%A5%E5%BF%97%E5%A4%84%E7%90%86/index.xml" rel="self" type="application/rss+xml"/><description>日志处理</description><generator>Wowchemy (https://wowchemy.com)</generator><language>zh</language><image><url>https://ng-tech.icu/media/sharing.png</url><title>日志处理</title><link>https://ng-tech.icu/books/spring-notes/01.%E5%9F%BA%E7%A1%80%E4%BD%BF%E7%94%A8/4.%E8%AF%B7%E6%B1%82%E4%B8%8E%E5%93%8D%E5%BA%94/%E6%97%A5%E5%BF%97%E5%A4%84%E7%90%86/</link></image><item><title>Log4j2</title><link>https://ng-tech.icu/books/spring-notes/01.%E5%9F%BA%E7%A1%80%E4%BD%BF%E7%94%A8/4.%E8%AF%B7%E6%B1%82%E4%B8%8E%E5%93%8D%E5%BA%94/%E6%97%A5%E5%BF%97%E5%A4%84%E7%90%86/log4j2/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://ng-tech.icu/books/spring-notes/01.%E5%9F%BA%E7%A1%80%E4%BD%BF%E7%94%A8/4.%E8%AF%B7%E6%B1%82%E4%B8%8E%E5%93%8D%E5%BA%94/%E6%97%A5%E5%BF%97%E5%A4%84%E7%90%86/log4j2/</guid><description>&lt;h1 id="log4j2">Log4j2&lt;/h1>
&lt;p>Log4j 2 包含了基于 LMAX 分离库的下一代的异步日志系统，在多线程环境下，异步日志系统比 Log4j 1.x 和 Logback 提高了 10 倍性能提升(吞吐量和延迟率)，各种框架的对比可以看到如下所示：&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="http://cdn3.infoqstatic.com/statics_s2_20160105-0313u5/resource/news/2014/08/apache-log4j2/zh/resources/0805000.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>&lt;/p>
&lt;p>Log4j 2 是 Log4j 的升级版本，该版本比起其前任来说有着显著的改进，包含很多在 Logback 中的改进以及 Logback 架构中存在的问题。这是 Log4j 2 的首次发行的版本，值得关注的改进包括：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>API 分离 – Log4j 的 API 和其实现进行分类(注：我讨厌这样，本来一个 jar 包搞定的，要变成好几个，跟 slf4j 似的的)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>为日志审计而设计，与 Log4j 1.x 和 Logback 不同的是 Log4j 2 将不会在重新配置期间丢失事件，支持消息可方便进行审计&lt;/p>
&lt;/li>
&lt;li>
&lt;p>性能方面的提升，在关键领域比 Log4j 1.x 的性能提升不少，大部分情况下性能跟 Logback 差不多&lt;/p>
&lt;/li>
&lt;li>
&lt;p>支持多 APIs，支持 SLF4J 和 Commons Logging API&lt;/p>
&lt;/li>
&lt;li>
&lt;p>自动配置重载，支持 XML 和 JSON 格式的配置&lt;/p>
&lt;/li>
&lt;li>
&lt;p>插件体系架构，所有可配置的组件都是通过 Log4j 插件进行定义，包括 Appender, Layout, Pattern Converter, 等等&lt;/p>
&lt;/li>
&lt;li>
&lt;p>配置属性支持&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="http://logging.apache.org/log4j/2.x/images/Log4jClasses.jpg" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>&lt;/p>
&lt;h1 id="配置文件">配置文件&lt;/h1>
&lt;p>如果需要使用 Log4j2，首先需要在他的依赖文件里引入如下依赖：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.apache.logging.log4j&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>log4j-api&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;version&amp;gt;&lt;/span>2.4.1&lt;span class="nt">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.apache.logging.log4j&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>log4j-core&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;version&amp;gt;&lt;/span>2.4.1&lt;span class="nt">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/dependencies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;groupId&amp;gt;&lt;/span>org.apache.logging.log4j&lt;span class="nt">&amp;lt;/groupId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;artifactId&amp;gt;&lt;/span>log4j-slf4j-impl&lt;span class="nt">&amp;lt;/artifactId&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;version&amp;gt;&lt;/span>2.0-beta9&lt;span class="nt">&amp;lt;/version&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/dependency&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>而后在代码文件中，可以使用 Slf4j，也可以直接使用 Log4j2，如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">org.slf4j.Logger&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">org.slf4j.LoggerFactory&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">private&lt;/span> &lt;span class="kd">static&lt;/span> &lt;span class="kd">final&lt;/span> &lt;span class="n">Logger&lt;/span> &lt;span class="n">log&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LoggerFactory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getLogger&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">Test&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果是直接使用的 log4j2，则只要用 LogManager 的 getLogger 函数获取一个 logger，就可以使用 logger 记录日志，代码如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">org.apache.logging.log4j.Logger&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">org.apache.logging.log4j.LogManager&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">HelloLog4j&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="kd">static&lt;/span> &lt;span class="n">Logger&lt;/span> &lt;span class="n">logger&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LogManager&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getLogger&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;HelloLog4j&amp;#34;&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kd">static&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">[]&lt;/span> &lt;span class="n">args&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MyApplication&lt;/span> &lt;span class="n">myApplication&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">MyApplication&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">entry&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;Hello, World!&amp;#34;&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">myApplication&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">doIt&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">error&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;Hello, World!&amp;#34;&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">exit&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>需要注意的是，log4j 2.0 与以往的 1.x 有一个明显的不同，其配置文件只能采用.xml, .json 或者 .jsn。在默认情况下，系统选择 configuration 文件的优先级如下：&lt;/p>
&lt;ul>
&lt;li>classpath 下名为 log4j-test.json 或者 log4j-test.jsn 文件&lt;/li>
&lt;li>classpath 下名为 log4j2-test.xml&lt;/li>
&lt;li>classpath 下名为 log4j.json 或者 log4j.jsn 文件&lt;/li>
&lt;li>classpath 下名为 log4j2.xml&lt;/li>
&lt;/ul>
&lt;h1 id="logger-配置">Logger 配置&lt;/h1>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- status=debug 可以查看log4j的装配过程 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;configuration&lt;/span> &lt;span class="na">status=&lt;/span>&lt;span class="s">&amp;#34;off&amp;#34;&lt;/span> &lt;span class="na">monitorInterval=&lt;/span>&lt;span class="s">&amp;#34;1800&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;properties&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;property&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;LOG_HOME&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>/log/fish&lt;span class="nt">&amp;lt;/property&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 日志备份目录 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;property&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;BACKUP_HOME&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>{LOG_HOME}/backup&lt;span class="nt">&amp;lt;/property&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;property&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;STAT_NAME&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>stat&lt;span class="nt">&amp;lt;/property&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;property&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;SERVER_NAME&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>global&lt;span class="nt">&amp;lt;/property&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/properties&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appenders&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 定义控制台输出 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Console&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;Console&amp;#34;&lt;/span> &lt;span class="na">target=&lt;/span>&lt;span class="s">&amp;#34;SYSTEM_OUT&amp;#34;&lt;/span> &lt;span class="na">follow=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PatternLayout&lt;/span> &lt;span class="na">pattern=&lt;/span>&lt;span class="s">&amp;#34;%date{yyyy-MM-dd HH:mm:ss.SSS} %level [%thread][%file:%line] - %msg%n&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/Console&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 程序员调试日志 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;RollingRandomAccessFile&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;DevLog&amp;#34;&lt;/span> &lt;span class="na">fileName=&lt;/span>&lt;span class="s">&amp;#34;${LOG_HOME}/${SERVER_NAME}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">filePattern=&lt;/span>&lt;span class="s">&amp;#34;${LOG_HOME}/${SERVER_NAME}.%d{yyyy-MM-dd-HH}.log&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PatternLayout&lt;/span> &lt;span class="na">pattern=&lt;/span>&lt;span class="s">&amp;#34;%date{yyyy-MM-dd HH:mm:ss.SSS} %level [%thread][%file:%line] - %msg%n&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Policies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;TimeBasedTriggeringPolicy&lt;/span> &lt;span class="na">interval=&lt;/span>&lt;span class="s">&amp;#34;1&amp;#34;&lt;/span> &lt;span class="na">modulate=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/Policies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/RollingRandomAccessFile&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 游戏产品数据分析日志 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;RollingRandomAccessFile&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;ProductLog&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">fileName=&lt;/span>&lt;span class="s">&amp;#34;${LOG_HOME}/${SERVER_NAME}_${STAT_NAME}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">filePattern=&lt;/span>&lt;span class="s">&amp;#34;${LOG_HOME}/${SERVER_NAME}_${STAT_NAME}.%d{yyyy-MM-dd-HH}.log&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PatternLayout&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">pattern=&lt;/span>&lt;span class="s">&amp;#34;%date{yyyy-MM-dd HH:mm:ss.SSS} %level [%thread][%file:%line] - %msg%n&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Policies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;TimeBasedTriggeringPolicy&lt;/span> &lt;span class="na">interval=&lt;/span>&lt;span class="s">&amp;#34;1&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">modulate=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/Policies&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/RollingRandomAccessFile&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/appenders&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;loggers&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 3rdparty Loggers --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;org.springframework.core&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;info&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;org.springframework.beans&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;info&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;org.springframework.context&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;info&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;org.springframework.web&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;info&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;org.jboss.netty&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;warn&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;org.apache.http&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;warn&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;com.mchange.v2&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;warn&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- Game Stat logger --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;com.u9.global.service.log&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;info&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">additivity=&lt;/span>&lt;span class="s">&amp;#34;false&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;ProductLog&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- Root Logger --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;root&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;DEBUG&amp;#34;&lt;/span> &lt;span class="na">includeLocation=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;DevLog&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;Console&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/root&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/loggers&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>loggers 标签，用于定义 logger 的 lever 和所采用的 appender，其中 appender-ref 必须为先前定义的 appenders 的名称，例如，此处为 Console。那么 log 就会以 appender 所定义的输出格式来输出 log。root 标签为 log 的默认输出形式，如果一个类的 log 没有在 loggers 中明确指定其输出 lever 与格式，那么就会采用 root 中定义的格式。例如以下定义：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;configuration&lt;/span> &lt;span class="na">status=&lt;/span>&lt;span class="s">&amp;#34;OFF&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appenders&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Console&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;Console&amp;#34;&lt;/span> &lt;span class="na">target=&lt;/span>&lt;span class="s">&amp;#34;SYSTEM_OUT&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PatternLayout&lt;/span> &lt;span class="na">pattern=&lt;/span>&lt;span class="s">&amp;#34;%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/Console&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/appenders&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;loggers&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;com.relin.HelloLog4j&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;error&amp;#34;&lt;/span> &lt;span class="na">additivity=&lt;/span>&lt;span class="s">&amp;#34;false&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;Console&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;root&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;trace&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;Console&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/root&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/loggers&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>此时，HelloLog4j 则会在 error 级别上输出 log，而其他类则会在 trace 级别上输出 log。需要注意的是 additivity 选项，如果设置为 true(默认值)则 HelloLog4j 的 log 会被打印两次，第二次打印是由于 HelloLog4j 同时也满足 root 里面定义的 trace。在 log4j2 中可以配置不同的 Logger 输出到不同的文件中，如果有时候需要按照不同的级别输出到不同的文件中，则直接在 logger 的 AppenderRef 中定义不同的 level 指向。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;loggers&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;com.mvc.login&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;info&amp;#34;&lt;/span> &lt;span class="na">additivity=&lt;/span>&lt;span class="s">&amp;#34;false&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appenderRef&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;LoginController&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;error&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appenderRef&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;InfoController&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;info&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/loggers&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Logback</title><link>https://ng-tech.icu/books/spring-notes/01.%E5%9F%BA%E7%A1%80%E4%BD%BF%E7%94%A8/4.%E8%AF%B7%E6%B1%82%E4%B8%8E%E5%93%8D%E5%BA%94/%E6%97%A5%E5%BF%97%E5%A4%84%E7%90%86/logback/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://ng-tech.icu/books/spring-notes/01.%E5%9F%BA%E7%A1%80%E4%BD%BF%E7%94%A8/4.%E8%AF%B7%E6%B1%82%E4%B8%8E%E5%93%8D%E5%BA%94/%E6%97%A5%E5%BF%97%E5%A4%84%E7%90%86/logback/</guid><description>&lt;h1 id="基于-logback-的-java-日志处理">基于 Logback 的 Java 日志处理&lt;/h1>
&lt;p>Logback 是一个日志框架，它与 Log4j 可以说是同出一源，都出自 Ceki Gülcü 之手。Slf4j 的全称是 Simple Logging Facade for Java，即简单日志门面；实现了日志框架一些通用的接口，结合日志框架一起使用，最终日志的格式、记录级别、输出方式等都是通过绑定具体的日志框架实现的。&lt;/p>
&lt;p>Logback 主要由 logback-core, logback-classic, logback-access 三个模块组成，logback-core 是其它模块的基础设施，提供了一些关键的通用机制。logback-classic 的地位和作用等同于 Log4J，它也被认为是 Log4J 的一个改进版，并且它实现了简单日志门面 Slf4j；而 logback-access 主要作为一个与 Servlet 容器交互的模块，比如说 tomcat 或者 jetty，提供一些与 HTTP 访问相关的功能。&lt;/p>
&lt;h1 id="基础配置">基础配置&lt;/h1>
&lt;p>我们可以通过 logback-spring.xml 来配置 Logback，基础配置如下：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;configuration&lt;/span> &lt;span class="na">scan=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span> &lt;span class="na">scanPeriod=&lt;/span>&lt;span class="s">&amp;#34;60 seconds&amp;#34;&lt;/span> &lt;span class="na">debug=&lt;/span>&lt;span class="s">&amp;#34;false&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 引入 Spring Boot 基础配置 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;include&lt;/span> &lt;span class="na">resource=&lt;/span>&lt;span class="s">&amp;#34;org/springframework/boot/logging/logback/defaults.xml&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 定义变量 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;property&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;APP_NAME&amp;#34;&lt;/span> &lt;span class="na">value=&lt;/span>&lt;span class="s">&amp;#34;test&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;property&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;LOG_PATH&amp;#34;&lt;/span> &lt;span class="na">value=&lt;/span>&lt;span class="s">&amp;#34;${user.home}/${APP_NAME}/logs&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;property&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;LOG_FILE&amp;#34;&lt;/span> &lt;span class="na">value=&lt;/span>&lt;span class="s">&amp;#34;${LOG_PATH}/application.log&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 日志输出级别变量 @see application-xxx.properties--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;springProperty&lt;/span> &lt;span class="na">scope=&lt;/span>&lt;span class="s">&amp;#34;context&amp;#34;&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;logLevel&amp;#34;&lt;/span> &lt;span class="na">source=&lt;/span>&lt;span class="s">&amp;#34;log.level&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // xxxx
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/appender&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // xxxx
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;root&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;${logLevel}&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> // xxxx
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/root&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>property 用来定义变量值的标签，property 标签有两个属性，name 和 value；其中 name 的值是变量的名称，value 的值时变量定义的值。通过 property 定义的值会被插入到 logger 上下文中。定义变量后，可以使 &lt;code>${name}&lt;/code> 来使用变量。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;included&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;conversionRule&lt;/span> &lt;span class="na">conversionWord=&lt;/span>&lt;span class="s">&amp;#34;clr&amp;#34;&lt;/span> &lt;span class="na">converterClass=&lt;/span>&lt;span class="s">&amp;#34;org.springframework.boot.logging.logback.ColorConverter&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;conversionRule&lt;/span> &lt;span class="na">conversionWord=&lt;/span>&lt;span class="s">&amp;#34;wex&amp;#34;&lt;/span> &lt;span class="na">converterClass=&lt;/span>&lt;span class="s">&amp;#34;org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;conversionRule&lt;/span> &lt;span class="na">conversionWord=&lt;/span>&lt;span class="s">&amp;#34;wEx&amp;#34;&lt;/span> &lt;span class="na">converterClass=&lt;/span>&lt;span class="s">&amp;#34;org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;property&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;CONSOLE_LOG_PATTERN&amp;#34;&lt;/span> &lt;span class="na">value=&lt;/span>&lt;span class="s">&amp;#34;${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}}&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;property&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;FILE_LOG_PATTERN&amp;#34;&lt;/span> &lt;span class="na">value=&lt;/span>&lt;span class="s">&amp;#34;${FILE_LOG_PATTERN:-%d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;DEBUG_LEVEL_REMAPPER&amp;#34;&lt;/span> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;org.springframework.boot.logging.logback.LevelRemappingAppender&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;destinationLogger&amp;gt;&lt;/span>org.springframework.boot&lt;span class="nt">&amp;lt;/destinationLogger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/appender&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;org.apache.catalina.startup.DigesterFactory&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;ERROR&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;org.springframework.boot.actuate.endpoint.jmx&amp;#34;&lt;/span> &lt;span class="na">additivity=&lt;/span>&lt;span class="s">&amp;#34;false&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;DEBUG_LEVEL_REMAPPER&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;org.thymeleaf&amp;#34;&lt;/span> &lt;span class="na">additivity=&lt;/span>&lt;span class="s">&amp;#34;false&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;DEBUG_LEVEL_REMAPPER&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/included&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后在 Java 代码中可以通过 Slf4j 来获取到日志记录器对象：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 通过 Slf4j LoggerFactory 获取 logger 对象
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kd">private&lt;/span> &lt;span class="kd">static&lt;/span> &lt;span class="kd">final&lt;/span> &lt;span class="n">Logger&lt;/span> &lt;span class="n">logger&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LoggerFactory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getLogger&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">DemoTest&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// 通过 Slf4j 注解注入
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nd">@Slf4j&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">logger&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;info&amp;#34;&lt;/span>&lt;span class="o">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="appender">Appender&lt;/h1>
&lt;p>Appender 是一个日志打印的组件，这里组件里面定义了打印过滤的条件、打印输出方式、滚动策略、编码方式、打印格式等等。但是它仅仅是一个打印组件，如果我们不使用一个 logger 或者 root 的 appender-ref 指定某个具体的 appender 时，它就没有什么意义。上文定义的 root 本质上是根 logger,只不过 root 中不能有 name 和 additivity 属性，是有一个 level。&lt;/p>
&lt;p>Appender 主要包含以下三类：&lt;/p>
&lt;ul>
&lt;li>ConsoleAppender：把日志添加到控制台&lt;/li>
&lt;li>FileAppender：把日志添加到文件&lt;/li>
&lt;li>RollingFileAppender：滚动记录文件，先将日志记录到指定文件，当符合某个条件时，将日志记录到其他文件。它是 FileAppender 的子类&lt;/li>
&lt;/ul>
&lt;h2 id="consoleappender">ConsoleAppender&lt;/h2>
&lt;p>最常用的日志输出就是输出到 Console 中，我们可以定义 Console 专用的 Appender:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;appender&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;console&amp;#34;&lt;/span> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;ch.qos.logback.core.ConsoleAppender&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;encoder&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;pattern&amp;gt;&lt;/span>logback: %d {HH:mm:ss.SSS} %logger{36} - %M - %msg%n&lt;span class="nt">&amp;lt;/pattern&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!--&amp;lt;pattern&amp;gt;%d{yyyy-MM-dd HH:mm:ss.SSS}|%level|%class|%thread|%method|%line|%msg%n&amp;lt;/pattern&amp;gt;--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 使用 Spring Boot 预定义的 Pattern --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!--&amp;lt;pattern&amp;gt;${CONSOLE_LOG_PATTERN}&amp;lt;/pattern&amp;gt;--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/encoder&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/appender&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;root&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;${logLevel}&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;console&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/root&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="rollingfileappender">RollingFileAppender&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;appender&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;APPLICATION&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;ch.qos.logback.core.rolling.RollingFileAppender&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;file&amp;gt;&lt;/span>${LOG_FILE}&lt;span class="nt">&amp;lt;/file&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;encoder&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;pattern&amp;gt;&lt;/span>${FILE_LOG_PATTERN}&lt;span class="nt">&amp;lt;/pattern&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/encoder&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;rollingPolicy&lt;/span> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;fileNamePattern&amp;gt;&lt;/span>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log&lt;span class="nt">&amp;lt;/fileNamePattern&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;maxHistory&amp;gt;&lt;/span>7&lt;span class="nt">&amp;lt;/maxHistory&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;maxFileSize&amp;gt;&lt;/span>50MB&lt;span class="nt">&amp;lt;/maxFileSize&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;totalSizeCap&amp;gt;&lt;/span>20GB&lt;span class="nt">&amp;lt;/totalSizeCap&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/rollingPolicy&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/appender&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其中 rollingPolicy 子标签用来描述滚动策略，仅在 RollingFileAppender 中才需要配置。而该标签中常用的滚动策略是 TimeBasedRollingPolicy，它根据时间来制定滚动策略，既负责滚动也负责触发滚动。其他还有，FixedWindowRollingPolicy 根据固定窗口算法重命名文件的滚动策略。&lt;/p>
&lt;h2 id="filter">Filter&lt;/h2>
&lt;p>在真实场景下，我们可能会需要将不同级别的日志输出到不同的文件中，在不引入新的 Logger 的情况下，我们可以通过 Filter 来进行过滤：&lt;/p>
&lt;p>filter 其实是 appender 里面的子元素。它作为过滤器存在，执行一个过滤器会有返回 DENY，NEUTRAL，ACCEPT 三个枚举值中的一个。&lt;/p>
&lt;ul>
&lt;li>DENY：日志将立即被抛弃不再经过其他过滤器&lt;/li>
&lt;li>NEUTRAL：有序列表里的下个过滤器过接着处理日志&lt;/li>
&lt;li>ACCEPT：日志会被立即处理，不再经过剩余过滤器&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;appender&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;fileInfoLog&amp;#34;&lt;/span> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;ch.qos.logback.core.rolling.RollingFileAppender&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;filter&lt;/span> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;ch.qos.logback.classic.filter.LevelFilter&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!--要拦截的日志级别--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;level&amp;gt;&lt;/span>ERROR&lt;span class="nt">&amp;lt;/level&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!--如果匹配，则禁止--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;onMatch&amp;gt;&lt;/span>DENY&lt;span class="nt">&amp;lt;/onMatch&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!--如果不匹配，则允许记录--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;onMismatch&amp;gt;&lt;/span>ACCEPT&lt;span class="nt">&amp;lt;/onMismatch&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/filter&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;encoder&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;pattern&amp;gt;&lt;/span>%d -- %msg%n&lt;span class="nt">&amp;lt;/pattern&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/encoder&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!--滚动策略--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;rollingPolicy&lt;/span> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;ch.qos.logback.core.rolling.TimeBasedRollingPolicy&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!--路径--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;fileNamePattern&amp;gt;&lt;/span>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log&lt;span class="nt">&amp;lt;/fileNamePattern&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/rollingPolicy&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/appender&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;appender&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;fileErrorLog&amp;#34;&lt;/span> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;ch.qos.logback.core.rolling.RollingFileAppender&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!--添加 范围 过滤--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;filter&lt;/span> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;ch.qos.logback.classic.filter.ThresholdFilter&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;level&amp;gt;&lt;/span>ERROR&lt;span class="nt">&amp;lt;/level&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/filter&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;encoder&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;pattern&amp;gt;&lt;/span>%d -- %msg%n&lt;span class="nt">&amp;lt;/pattern&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/encoder&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;rollingPolicy&lt;/span> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;ch.qos.logback.core.rolling.TimeBasedRollingPolicy&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;fileNamePattern&amp;gt;&lt;/span>${LOG_FILE}.%d{yyyy-MM-dd}.%i.log&lt;span class="nt">&amp;lt;/fileNamePattern&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/rollingPolicy&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/appender&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;root&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;info&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;fileInfoLog&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;fileErrorLog&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/root&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>其他的常用的 Filter 还有 ThresholdFilter 与 LevelFilter：&lt;/p>
&lt;ul>
&lt;li>
&lt;p>ThresholdFilter: 临界值过滤器，过滤掉低于指定临界值的日志。当日志级别等于或高于临界值时，过滤器返回 NEUTRAL；当日志级别低于临界值时，日志会被拒绝。&lt;/p>
&lt;/li>
&lt;li>
&lt;p>LevelFilter: 级别过滤器，根据日志级别进行过滤。如果日志级别等于配置级别，过滤器会根据 onMath(用于配置符合过滤条件的操作) 和 onMismatch(用于配置不符合过滤条件的操作)接收或拒绝日志。&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h1 id="logger">Logger&lt;/h1>
&lt;p>我们可以通过自定义 Logger，来关联不同的包或者 Appender:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;wx.spring.boot.controller&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;${logging.level}&amp;#34;&lt;/span> &lt;span class="na">additivity=&lt;/span>&lt;span class="s">&amp;#34;false&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;console&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>上面的这个配置文件描述的是：wx.spring.boot.controller 这个包下的 &lt;code>${logging.level}&lt;/code> 级别的日志将会使用 console 来打印；logger 有三个属性和一个子标签：&lt;/p>
&lt;ul>
&lt;li>name: 用来指定受此 logger 约束的某一个包或者具体的某一个类。&lt;/li>
&lt;li>level: 用来设置打印级别（TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF），还有一个值 INHERITED 或者同义词 NULL，代表强制执行上级的级别。如果没有设置此属性，那么当前 logger 将会继承上级的级别。&lt;/li>
&lt;li>addtivity: 用来描述是否向上级 logger 传递打印信息。默认是 true。&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- fileControllerLog--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;appender&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;fileControllerLog&amp;#34;&lt;/span> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;ch.qos.logback.core.rolling.RollingFileAppender&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;file&amp;gt;&lt;/span>${APP_LOG_FILE}&lt;span class="nt">&amp;lt;/file&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;filter&lt;/span> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;ch.qos.logback.classic.filter.ThresholdFilter&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;level&amp;gt;&lt;/span>${logging.level}&lt;span class="nt">&amp;lt;/level&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/filter&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;encoder&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;pattern&amp;gt;&lt;/span>%d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN:-%5p} %t %logger{0} %m%n&lt;span class="nt">&amp;lt;/pattern&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;charset&amp;gt;&lt;/span>utf8&lt;span class="nt">&amp;lt;/charset&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/encoder&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;rollingPolicy&lt;/span> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;ch.qos.logback.core.rolling.TimeBasedRollingPolicy&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;fileNamePattern&amp;gt;&lt;/span>${APP_LOG_FILE}.log.%d{yyyy-MM-dd}&lt;span class="nt">&amp;lt;/fileNamePattern&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;maxHistory&amp;gt;&lt;/span>10&lt;span class="nt">&amp;lt;/maxHistory&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/rollingPolicy&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/appender&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!--此logger约束将.controller 包下的日志输出到 fileControllerLog--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;wx.spring.boot.controller&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;${logging.level}&amp;#34;&lt;/span> &lt;span class="na">additivity=&lt;/span>&lt;span class="s">&amp;#34;false&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;fileControllerLog&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;fileErrorLog&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!--此logger约束将.service 包下的日志输出到 fileServiceLog--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;wx.spring.boot.service&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;${logging.level}&amp;#34;&lt;/span> &lt;span class="na">additivity=&lt;/span>&lt;span class="s">&amp;#34;false&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;fileServiceLog&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;fileErrorLog&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们也可以将日志维度固定到某个具体的类：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!--这里指定到了具体的某一个类--&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;wx.spring.boot.task.TestLogTask&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;${logging.level}&amp;#34;&lt;/span> &lt;span class="na">additivity=&lt;/span>&lt;span class="s">&amp;#34;true&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;SCHEDULERTASKLOCK-APPENDER&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;fileErrorLog&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>或者自定义 logger 名称：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;dependency&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;info&amp;#34;&lt;/span> &lt;span class="na">additivity=&lt;/span>&lt;span class="s">&amp;#34;false&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;dependencyAppender&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后在代码中显式获取：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">private&lt;/span> &lt;span class="kd">static&lt;/span> &lt;span class="n">Logger&lt;/span> &lt;span class="n">logger&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LoggerFactory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getLogger&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;dependency&amp;#34;&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果我们希望打印出 MyBatis 的 SQL 语句，则在 logback-spring.xml 中添加如下配置：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl"> &lt;span class="c">&amp;lt;!-- 将sql语句输出到具体的日志文件中 --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;wx.dao&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;${logging.sql.level}&amp;#34;&lt;/span> &lt;span class="na">additivity=&lt;/span>&lt;span class="s">&amp;#34;false&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;sqlAppender&amp;#34;&lt;/span>&lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/logger&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="links">Links&lt;/h1>
&lt;ul>
&lt;li>&lt;a href="https://mp.weixin.qq.com/s/IAGPxOfCz9a2C0VrnyT2eg" target="_blank" rel="noopener">https://mp.weixin.qq.com/s/IAGPxOfCz9a2C0VrnyT2eg&lt;/a> Logback 配置文件这么写，TPS 提高 10 倍&lt;/li>
&lt;/ul></description></item><item><title>MDC</title><link>https://ng-tech.icu/books/spring-notes/01.%E5%9F%BA%E7%A1%80%E4%BD%BF%E7%94%A8/4.%E8%AF%B7%E6%B1%82%E4%B8%8E%E5%93%8D%E5%BA%94/%E6%97%A5%E5%BF%97%E5%A4%84%E7%90%86/mdc/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://ng-tech.icu/books/spring-notes/01.%E5%9F%BA%E7%A1%80%E4%BD%BF%E7%94%A8/4.%E8%AF%B7%E6%B1%82%E4%B8%8E%E5%93%8D%E5%BA%94/%E6%97%A5%E5%BF%97%E5%A4%84%E7%90%86/mdc/</guid><description>&lt;h1 id="mdc">MDC&lt;/h1>
&lt;p>MDC（Mapped Diagnostic Context，映射调试上下文）是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。某些应用程序采用多线程的方式来处理多个用户的请求。在一个用户的使用过程中，可能有多个不同的线程来进行处理。典型的例子是 Web 应用服务器。当用户访问某个页面时，应用服务器可能会创建一个新的线程来处理该请求，也可能从线程池中复用已有的线程。在一个用户的会话存续期间，可能有多个线程处理过该用户的请求。这使得比较难以区分不同用户所对应的日志。当需要追踪某个用户在系统中的相关日志记录时，就会变得很麻烦。&lt;/p>
&lt;p>一种解决的办法是采用自定义的日志格式，把用户的信息采用某种方式编码在日志记录中。这种方式的问题在于要求在每个使用日志记录器的类中，都可以访问到用户相关的信息。这样才可能在记录日志时使用。这样的条件通常是比较难以满足的。MDC 的作用是解决这个问题。MDC 可以看成是一个与当前线程绑定的哈希表，可以往其中添加键值对。MDC 中包含的内容可以被同一线程中执行的代码所访问。当前线程的子线程会继承其父线程中的 MDC 的内容。当需要记录日志时，只需要从 MDC 中获取所需的信息即可。MDC 的内容则由程序在适当的时候保存进去。对于一个 Web 应用来说，通常是在请求被处理的最开始保存这些数据。&lt;/p>
&lt;h1 id="使用案例">使用案例&lt;/h1>
&lt;p>让我们从一个例子开始。假设我们要写一个转账的软件。我们设置了一个 Transfer 类来表示一些基本信息：一个独特的转账 ID 和发送者的名字。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">Transfer&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="n">String&lt;/span> &lt;span class="n">transactionId&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="n">String&lt;/span> &lt;span class="n">sender&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="n">Long&lt;/span> &lt;span class="n">amount&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="nf">Transfer&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">String&lt;/span> &lt;span class="n">transactionId&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">String&lt;/span> &lt;span class="n">sender&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">amount&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">transactionId&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">transactionId&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">sender&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sender&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">amount&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">amount&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>为了执行转移，我们需要使用一个由简单 API 支持的服务。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span> &lt;span class="kd">abstract&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">TransferService&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kt">boolean&lt;/span> &lt;span class="nf">transfer&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="n">amount&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// connects to the remote service to actually transfer money
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">abstract&lt;/span> &lt;span class="kd">protected&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">beforeTransfer&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="n">amount&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">abstract&lt;/span> &lt;span class="kd">protected&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">afterTransfer&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="n">amount&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="kt">boolean&lt;/span> &lt;span class="n">outcome&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>beforeTransfer() 和 afterTransfer() 方法可以被重写，以便在传输完成之前和之后运行自定义代码。我们将利用 beforeTransfer() 和 afterTransfer() 来记录一些关于传输的信息。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">org.apache.log4j.Logger&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">com.baeldung.mdc.TransferService&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">Log4JTransferService&lt;/span> &lt;span class="kd">extends&lt;/span> &lt;span class="n">TransferService&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="n">Logger&lt;/span> &lt;span class="n">logger&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Logger&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getLogger&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">Log4JTransferService&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">protected&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">beforeTransfer&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="n">amount&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;Preparing to transfer &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">amount&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s">&amp;#34;$.&amp;#34;&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">protected&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">afterTransfer&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="n">amount&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="kt">boolean&lt;/span> &lt;span class="n">outcome&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="o">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;Has transfer of &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">amount&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s">&amp;#34;$ completed successfully ? &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">outcome&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s">&amp;#34;.&amp;#34;&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>这里需要注意的主要问题是，当创建日志信息时，不可能访问 Transfer 对象；只有金额可以访问，因此不可能记录交易 ID 或发件人。让我们设置通常的 log4j.properties 文件，以便在控制台记录。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yml" data-lang="yml">&lt;span class="line">&lt;span class="cl">&lt;span class="l">log4j.appender.consoleAppender=org.apache.log4j.ConsoleAppender&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="l">log4j.appender.consoleAppender.layout=org.apache.log4j.PatternLayout&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="l">log4j.appender.consoleAppender.layout.ConversionPattern=%-4r [%t] %5p %c %x - %m%n&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="l">log4j.rootLogger = TRACE, consoleAppender&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>最后让我们设置一个小程序，它能够通过 ExecutorService 同时运行多个传输。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">TransferDemo&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kd">static&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">[]&lt;/span> &lt;span class="n">args&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ExecutorService&lt;/span> &lt;span class="n">executor&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Executors&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">newFixedThreadPool&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TransactionFactory&lt;/span> &lt;span class="n">transactionFactory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">TransactionFactory&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="o">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="o">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="o">;&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">++)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Transfer&lt;/span> &lt;span class="n">tx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">transactionFactory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">newInstance&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Runnable&lt;/span> &lt;span class="n">task&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Log4JRunnable&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">tx&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">executor&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">submit&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">task&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">executor&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">shutdown&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们注意到，为了使用 ExecutorService，我们需要将 Log4JTransferService 的执行包装在一个适配器中，因为 executor.submit()期望有一个 Runnable。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">Log4JRunnable&lt;/span> &lt;span class="kd">implements&lt;/span> &lt;span class="n">Runnable&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="n">Transfer&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="nf">Log4JRunnable&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">Transfer&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">tx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">run&lt;/span>&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">log4jBusinessService&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">transfer&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">tx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getAmount&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>当我们运行同时管理多笔转账的演示程序时，我们很快发现，日志并不像我们希望的那样有用。跟踪每笔转账的执行情况是很复杂的，因为被记录的唯一有用的信息是转账的金额和执行该特定转账的线程的名称。更重要的是，我们不可能区分由同一个线程执行的相同金额的两个不同交易，因为相关的日志行看起来基本相同。&lt;/p>
&lt;h2 id="log4j">Log4j&lt;/h2>
&lt;p>Log4j 中的 MDC 允许我们在一个类似于地图的结构中填入一些信息，这些信息在实际写入日志消息时可以被 appender 访问。MDC 结构在内部被附加到执行线程上，与 ThreadLocal 变量的方式相同。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">org.apache.log4j.MDC&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">Log4JRunnable&lt;/span> &lt;span class="kd">implements&lt;/span> &lt;span class="n">Runnable&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="n">Transfer&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="kd">static&lt;/span> &lt;span class="n">Log4JTransferService&lt;/span> &lt;span class="n">log4jBusinessService&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Log4JTransferService&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="nf">Log4JRunnable&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">Transfer&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">tx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">run&lt;/span>&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MDC&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;transaction.id&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getTransactionId&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MDC&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;transaction.owner&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getSender&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">log4jBusinessService&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">transfer&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">tx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getAmount&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MDC&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">clear&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>不出所料，MDC.put() 被用来在 MDC 中添加一个键和一个相应的值，而 MDC.clear() 则清空 MDC。现在让我们修改 log4j.properties 来打印我们刚刚存储在 MDC 中的信息。只需改变转换模式，用 %X{} 占位符来表示我们希望被记录的 MDC 中的每个条目。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="l">log4j.appender.consoleAppender.layout.ConversionPattern=&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="l">%-4r [%t] %5p %c{1} %x - %m - tx.id=%X{transaction.id} tx.owner=%X{transaction.owner}%n&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>现在，如果我们运行这个应用程序，我们会注意到每一行都带有正在处理的事务的信息，使我们更容易跟踪应用程序的执行。&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-log" data-lang="log">638 [pool-1-thread-2] INFO Log4JBusinessService
- Has transfer of 1104$ completed successfully ? true. - tx.id=2 tx.owner=Marc
638 [pool-1-thread-2] INFO Log4JBusinessService
- Preparing to transfer 1685$. - tx.id=4 tx.owner=John
666 [pool-1-thread-1] INFO Log4JBusinessService
- Has transfer of 1985$ completed successfully ? true. - tx.id=1 tx.owner=Marc
666 [pool-1-thread-1] INFO Log4JBusinessService
- Preparing to transfer 958$. - tx.id=5 tx.owner=Susan
739 [pool-1-thread-3] INFO Log4JBusinessService
- Has transfer of 783$ completed successfully ? true. - tx.id=3 tx.owner=Samantha
739 [pool-1-thread-3] INFO Log4JBusinessService
- Preparing to transfer 1024$. - tx.id=6 tx.owner=John
1259 [pool-1-thread-2] INFO Log4JBusinessService
- Has transfer of 1685$ completed successfully ? false. - tx.id=4 tx.owner=John
1260 [pool-1-thread-2] INFO Log4JBusinessService
- Preparing to transfer 1667$. - tx.id=7 tx.owner=Marc
&lt;/code>&lt;/pre>&lt;h2 id="log4j2">Log4j2&lt;/h2>
&lt;p>Log4j2 中也有同样的功能，让我们看看如何使用它。首先让我们建立一个 TransferService 子类，使用 Log4j2 进行记录。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">org.apache.logging.log4j.LogManager&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">org.apache.logging.log4j.Logger&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">Log4J2TransferService&lt;/span> &lt;span class="kd">extends&lt;/span> &lt;span class="n">TransferService&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="kd">static&lt;/span> &lt;span class="kd">final&lt;/span> &lt;span class="n">Logger&lt;/span> &lt;span class="n">logger&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LogManager&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getLogger&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">protected&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">beforeTransfer&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="n">amount&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;Preparing to transfer {}$.&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">amount&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">protected&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">afterTransfer&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="n">amount&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="kt">boolean&lt;/span> &lt;span class="n">outcome&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;Has transfer of {}$ completed successfully ? {}.&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">amount&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">outcome&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>然后让我们改变使用 MDC 的代码，它在 Log4j2 中实际上叫做 ThreadContext。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">org.apache.log4j.MDC&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">Log4J2Runnable&lt;/span> &lt;span class="kd">implements&lt;/span> &lt;span class="n">Runnable&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="kd">final&lt;/span> &lt;span class="n">Transaction&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="n">Log4J2BusinessService&lt;/span> &lt;span class="n">log4j2BusinessService&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Log4J2BusinessService&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="nf">Log4J2Runnable&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">Transaction&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">tx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">run&lt;/span>&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ThreadContext&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;transaction.id&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getTransactionId&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ThreadContext&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;transaction.owner&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getOwner&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">log4j2BusinessService&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">transfer&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">tx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getAmount&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ThreadContext&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">clearAll&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>同样，ThreadContext.put() 在 MDC 中添加了一个条目，而 ThreadContext.clearAll() 则删除了所有现有条目。我们仍然想念 log4j2.xml 文件来配置日志记录。我们可以注意到，指定哪些 MDC 条目应该被记录的语法与 Log4j 中使用的语法相同。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;Configuration&lt;/span> &lt;span class="na">status=&lt;/span>&lt;span class="s">&amp;#34;INFO&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Appenders&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Console&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;stdout&amp;#34;&lt;/span> &lt;span class="na">target=&lt;/span>&lt;span class="s">&amp;#34;SYSTEM_OUT&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PatternLayout&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="na">pattern=&lt;/span>&lt;span class="s">&amp;#34;%-4r [%t] %5p %c{1} - %m - tx.id=%X{transaction.id} tx.owner=%X{transaction.owner}%n&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/Console&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/Appenders&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Loggers&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;Logger&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;com.baeldung.log4j2&amp;#34;&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;TRACE&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;AsyncRoot&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;DEBUG&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;AppenderRef&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;stdout&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/AsyncRoot&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/Loggers&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/Configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>再次，让我们执行应用程序，我们将看到 MDC 信息被打印在日志中。&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-log" data-lang="log">1119 [pool-1-thread-3] INFO Log4J2BusinessService
- Has transfer of 1198$ completed successfully ? true. - tx.id=3 tx.owner=Samantha
1120 [pool-1-thread-3] INFO Log4J2BusinessService
- Preparing to transfer 1723$. - tx.id=5 tx.owner=Samantha
1170 [pool-1-thread-2] INFO Log4J2BusinessService
- Has transfer of 701$ completed successfully ? true. - tx.id=2 tx.owner=Susan
1171 [pool-1-thread-2] INFO Log4J2BusinessService
- Preparing to transfer 1108$. - tx.id=6 tx.owner=Susan
1794 [pool-1-thread-1] INFO Log4J2BusinessService
- Has transfer of 645$ completed successfully ? true. - tx.id=4 tx.owner=Susan
&lt;/code>&lt;/pre>&lt;h2 id="slf4jlogback">Slf4j/Logback&lt;/h2>
&lt;p>在 Slf4j 中，MDC 也是可用的，条件是底层日志库支持它。正如我们刚才看到的，Logback 和 Log4j 都支持 MDC，所以我们不需要什么特别的东西就可以在标准的设置下使用它。让我们准备一下通常的 TransferService 子类，这次使用 Java 的 Simple Logging Facade。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">org.slf4j.Logger&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">org.slf4j.LoggerFactory&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">final&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">Slf4TransferService&lt;/span> &lt;span class="kd">extends&lt;/span> &lt;span class="n">TransferService&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="kd">static&lt;/span> &lt;span class="kd">final&lt;/span> &lt;span class="n">Logger&lt;/span> &lt;span class="n">logger&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LoggerFactory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getLogger&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">Slf4TransferService&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">protected&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">beforeTransfer&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="n">amount&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;Preparing to transfer {}$.&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">amount&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">protected&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">afterTransfer&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="kt">long&lt;/span> &lt;span class="n">amount&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="kt">boolean&lt;/span> &lt;span class="n">outcome&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;Has transfer of {}$ completed successfully ? {}.&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">amount&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">outcome&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>现在让我们使用 SLF4J 的 MDC 味道。在这种情况下，其语法和语义与 log4j 中的相同。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">org.slf4j.MDC&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">Slf4jRunnable&lt;/span> &lt;span class="kd">implements&lt;/span> &lt;span class="n">Runnable&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="kd">final&lt;/span> &lt;span class="n">Transaction&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="nf">Slf4jRunnable&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">Transaction&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">tx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">run&lt;/span>&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MDC&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;transaction.id&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getTransactionId&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MDC&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;transaction.owner&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getOwner&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">new&lt;/span> &lt;span class="n">Slf4TransferService&lt;/span>&lt;span class="o">().&lt;/span>&lt;span class="na">transfer&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">tx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getAmount&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MDC&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">clear&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>我们必须提供 Logback 的配置文件：logback.xml。&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender&lt;/span> &lt;span class="na">name=&lt;/span>&lt;span class="s">&amp;#34;stdout&amp;#34;&lt;/span> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;ch.qos.logback.core.ConsoleAppender&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;encoder&lt;/span> &lt;span class="na">class=&lt;/span>&lt;span class="s">&amp;#34;ch.qos.logback.classic.encoder.PatternLayoutEncoder&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;pattern&amp;gt;&lt;/span>%-4r [%t] %5p %c{1} - %m - tx.id=%X{transaction.id} tx.owner=%X{transaction.owner}%n&lt;span class="nt">&amp;lt;/pattern&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/encoder&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/appender&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;root&lt;/span> &lt;span class="na">level=&lt;/span>&lt;span class="s">&amp;#34;TRACE&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;appender-ref&lt;/span> &lt;span class="na">ref=&lt;/span>&lt;span class="s">&amp;#34;stdout&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/root&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/configuration&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>同样，我们将看到 MDC 中的信息被正确地添加到了日志信息中，尽管这些信息并没有在 log.info() 方法中明确提供。&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-log" data-lang="log">1020 [pool-1-thread-3] INFO c.b.m.s.Slf4jBusinessService
- Has transfer of 1869$ completed successfully ? true. - tx.id=3 tx.owner=John
1021 [pool-1-thread-3] INFO c.b.m.s.Slf4jBusinessService
- Preparing to transfer 1303$. - tx.id=6 tx.owner=Samantha
1221 [pool-1-thread-1] INFO c.b.m.s.Slf4jBusinessService
- Has transfer of 1498$ completed successfully ? true. - tx.id=4 tx.owner=Marc
1221 [pool-1-thread-1] INFO c.b.m.s.Slf4jBusinessService
- Preparing to transfer 1528$. - tx.id=7 tx.owner=Samantha
1492 [pool-1-thread-2] INFO c.b.m.s.Slf4jBusinessService
- Has transfer of 1110$ completed successfully ? true. - tx.id=5 tx.owner=Samantha
1493 [pool-1-thread-2] INFO c.b.m.s.Slf4jBusinessService
- Preparing to transfer 644$. - tx.id=8 tx.owner=John
&lt;/code>&lt;/pre>&lt;h1 id="mdc-and-thread-pools">MDC and Thread Pools&lt;/h1>
&lt;p>&lt;strong>MDC implementations are usually using *ThreadLocal*s to store the contextual information.&lt;/strong> That&amp;rsquo;s an easy and reasonable way to achieve thread-safety. However, we should be careful using MDC with thread pools.&lt;/p>
&lt;p>Let&amp;rsquo;s see how the combination of &lt;em>ThreadLocal&lt;/em>-based MDCs and thread pools can be dangerous:&lt;/p>
&lt;ol>
&lt;li>We get a thread from the thread pool.&lt;/li>
&lt;li>Then we store some contextual information in MDC using &lt;em>MDC.put()&lt;/em> or &lt;em>ThreadContext.put()&lt;/em>.&lt;/li>
&lt;li>We use this information in some logs and somehow we forgot to clear the MDC context.&lt;/li>
&lt;li>The borrowed thread comes back to the thread pool.&lt;/li>
&lt;li>After a while, the application gets the same thread from the pool.&lt;/li>
&lt;li>Since we didn&amp;rsquo;t clean up the MDC last time, this thread still owns some data from the previous execution.&lt;/li>
&lt;/ol>
&lt;p>This may cause some unexpected inconsistencies between executions. &lt;strong>One way to prevent this is to always remember to clean up the MDC context at the end of each execution.&lt;/strong> This approach usually needs rigorous human supervision and, therefore, is error-prone.&lt;/p>
&lt;p>&lt;strong>Another approach is to use *ThreadPoolExecutor* hooks and perform necessary cleanups after each execution.&lt;/strong> To do that, we can extend the &lt;em>ThreadPoolExecutor&lt;/em> class and override the &lt;em>afterExecute()&lt;/em> hook:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">MdcAwareThreadPoolExecutor&lt;/span> &lt;span class="kd">extends&lt;/span> &lt;span class="n">ThreadPoolExecutor&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="nf">MdcAwareThreadPoolExecutor&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">corePoolSize&lt;/span>&lt;span class="o">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">maximumPoolSize&lt;/span>&lt;span class="o">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">long&lt;/span> &lt;span class="n">keepAliveTime&lt;/span>&lt;span class="o">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TimeUnit&lt;/span> &lt;span class="n">unit&lt;/span>&lt;span class="o">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">BlockingQueue&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">Runnable&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">workQueue&lt;/span>&lt;span class="o">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ThreadFactory&lt;/span> &lt;span class="n">threadFactory&lt;/span>&lt;span class="o">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">RejectedExecutionHandler&lt;/span> &lt;span class="n">handler&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">super&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">corePoolSize&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">maximumPoolSize&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">keepAliveTime&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">unit&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">workQueue&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">threadFactory&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">handler&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nd">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">protected&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">afterExecute&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">Runnable&lt;/span> &lt;span class="n">r&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">Throwable&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">System&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">out&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">println&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;Cleaning the MDC context&amp;#34;&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MDC&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">clear&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">org&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">apache&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">log4j&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">MDC&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">clear&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ThreadContext&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">clearAll&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This way, the MDC cleanup would happen after each normal or exceptional execution automatically. So, there is no need to do it manually:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@Override&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">run&lt;/span>&lt;span class="o">()&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MDC&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;transaction.id&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getTransactionId&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MDC&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;transaction.owner&amp;#34;&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">tx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getSender&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">new&lt;/span> &lt;span class="n">Slf4TransferService&lt;/span>&lt;span class="o">().&lt;/span>&lt;span class="na">transfer&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">tx&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getAmount&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now we can re-write the same demo with our new executor implementation:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="n">ExecutorService&lt;/span> &lt;span class="n">executor&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">MdcAwareThreadPoolExecutor&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="n">MINUTES&lt;/span>&lt;span class="o">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">new&lt;/span> &lt;span class="n">LinkedBlockingQueue&lt;/span>&lt;span class="o">&amp;lt;&amp;gt;(),&lt;/span> &lt;span class="n">Thread&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="o">,&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">AbortPolicy&lt;/span>&lt;span class="o">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TransactionFactory&lt;/span> &lt;span class="n">transactionFactory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">TransactionFactory&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="o">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="o">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">10&lt;/span>&lt;span class="o">;&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">++)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Transfer&lt;/span> &lt;span class="n">tx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">transactionFactory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">newInstance&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Runnable&lt;/span> &lt;span class="n">task&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">Slf4jRunnable&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">tx&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">executor&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">submit&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">task&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">executor&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">shutdown&lt;/span>&lt;span class="o">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Slf4j</title><link>https://ng-tech.icu/books/spring-notes/01.%E5%9F%BA%E7%A1%80%E4%BD%BF%E7%94%A8/4.%E8%AF%B7%E6%B1%82%E4%B8%8E%E5%93%8D%E5%BA%94/%E6%97%A5%E5%BF%97%E5%A4%84%E7%90%86/slf4j/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://ng-tech.icu/books/spring-notes/01.%E5%9F%BA%E7%A1%80%E4%BD%BF%E7%94%A8/4.%E8%AF%B7%E6%B1%82%E4%B8%8E%E5%93%8D%E5%BA%94/%E6%97%A5%E5%BF%97%E5%A4%84%E7%90%86/slf4j/</guid><description>&lt;h1 id="slf4j">Slf4j&lt;/h1>
&lt;p>SLF4J，即简单日志门面（Simple Logging Facade for Java），不是具体的日志解决方案，它只服务于各种各样的日志系统。按照官方的说法，SLF4J 是一个用于日志系统的简单 Facade，允许最终用户在部署其应用时使用其所希望的日志系统。&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img src="http://www.slf4j.org/images/concrete-bindings.png" alt="" loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">org.slf4j.Logger&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">org.slf4j.LoggerFactory&lt;/span>&lt;span class="o">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">public&lt;/span> &lt;span class="kd">class&lt;/span> &lt;span class="nc">HelloWorld&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kd">static&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="o">[]&lt;/span> &lt;span class="n">args&lt;/span>&lt;span class="o">)&lt;/span> &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Logger&lt;/span> &lt;span class="n">logger&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LoggerFactory&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">getLogger&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="n">HelloWorld&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">class&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logger&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="na">info&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="s">&amp;#34;Hello World&amp;#34;&lt;/span>&lt;span class="o">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Spring Boot 对 slf4j 支持的很好，内部已经集成了 slf4j，一般我们在使用的时候，会对 slf4j 做一下配置。&lt;code>application.yml&lt;/code> 文件是 Spring Boot 中唯一一个需要配置的文件，一开始创建工程的时候是 &lt;code>application.properties&lt;/code> 文件，个人比较细化用 yml 文件，因为 yml 文件的层次感特别好，看起来更直观，但是 yml 文件对格式要求比较高，比如英文冒号后面必须要有个空格，否则项目估计无法启动，而且也不报错。用 properties 还是 yml 视个人习惯而定，都可以。本课程使用 yml。&lt;/p>
&lt;p>我们看一下 application.yml 文件中对日志的配置：&lt;/p>
&lt;pre tabindex="0">&lt;code>logging:
config: logback.xml
level:
com.test.course03.dao: trace
&lt;/code>&lt;/pre>&lt;p>&lt;code>logging.config&lt;/code> 是用来指定项目启动的时候，读取哪个配置文件，这里指定的是日志配置文件是根路径下的 &lt;code>logback.xml&lt;/code> 文件，关于日志的相关配置信息，都放在 &lt;code>logback.xml&lt;/code> 文件中了。&lt;code>logging.level&lt;/code> 是用来指定具体的 mapper 中日志的输出级别，上面的配置表示 &lt;code>com.test.course03.dao&lt;/code> 包下的所有 mapper 日志输出级别为 trace，会将操作数据库的 sql 打印出来，开发时设置成 trace 方便定位问题，在生产环境上，将这个日志级别再设置成 error 级别即可。&lt;/p>
&lt;p>常用的日志级别按照从高到低依次为：ERROR、WARN、INFO、DEBUG。&lt;/p></description></item></channel></rss>