Spring Boot Logback日志组件使用

一 logback简介

      Logback是一个开源日志组件。Logback一般和SLF4结合起来使用。外层使用SLF4J,里面的实现是logback。什么意思,简单来说我们使用层看到的是SLF4J。我们使用的时候就是和SLF4J提供的一些api打交道。

      我们可能经常听到SLF4J,logback,log4j,logging这些个名词。下面我们用一个简单的图来解释下他们之间的关系之间的关系。我们用一个图来表述他们之间的关系。

log.png
  • SLF4J: Simple Logging Facade for Java 简单日志门面,不是具体的日志解决方案,它只服务于各种各样的日志系统。换句话说SLF4J只是一个日志标准(按照我们程序员的思维,我们可以把它认为是接口)。并不是日志系统的具体实现。如果项目只有slf4j的包是没办法实现日志功能的,需要配合其他的实现框架比如logback,logback,log4j,logging等。
  • Logback:logback框架完全按照slf4j的规范开发。是日志的具体实现框架。
  • log4j:log4j不是按照slf4j标准来开发的,所以log4j需要一个中间适配器。
  • logging: logging是jdk提供的一个日志框架,也不是按照slf4j标准来的,所以也需要一个总结适配器。才能兼容slf4j。

因为logback的使用不需要适配器,讲道理logback比其他需要中间适配器的日志框架要快。

二 logback配置文件

      logback使用最关键在配置文件的使用。因为在logbakc(包括其他的日志框架)的使用过程中,有很大一部分工作都是在配置文件上做文章的,比如需要输出什么级别的日志,日志文件怎么归档,打印日志文件的时候需要打印哪些信息等等。这些个东西都是通过logback的配置文件来定义的。

      logback的配置文件是一个xml格式的文件logback-test.xml或者logback.xml,读取配置文件的步骤:

  • (1)尝试classpath下查找文件logback-test.xml
  • (2)如果文件不存在,尝试查找logback.xml
  • (3)如果两个文件都不存在,LogBack用BasicConfiguration自动对自己进行最小化配置,这样既实现了上面我们不需要添加任何配置就可以输出到控制台日志信息。

      logback配置文件是一个xml文件,根节点是configuration标签。如下就是一个完成的logback配置文件。

<?xml version="1.0" encoding="UTF-8"?>
<!--
   说明:
   1、日志级别及文件
       日志记录采用分级记录,级别与日志文件名相对应,不同级别的日志信息记录到不同的日志文件中
       例如:error级别记录到xxx-error-xxx.log(该文件为当前记录的日志文件),而xxx-error-xxx.x.log为归档日志,
       日志文件按日期记录,同一天内,若日志文件大小等于或大于2M,则按0、1、2...顺序分别命名
       例如xxx-error-2013-12-21.0.log
       其它级别的日志也是如此。
   2、文件路径
       若本地开发,以绝对路径指定,如:/Users/zhangboqing/Downloads/logs。
       若部署到服务器,则各个服务器约定一个固定的日志路径如/data/home/logs/【项目名】/
   3、Appender
       FILE-ALL对应所有级别,文件名以xxx-all-xxx.log形式命名
       FILE-ERROR对应error级别,文件名以xxx-error-xxx.log形式命名
       FILE-WARN对应warn级别,文件名以xxx-warn-xxx.log形式命名
       FILE-INFO对应info级别,文件名以xxx-info-xxx.log形式命名
       FILE-DEBUG对应debug级别,文件名以xxx-debug-xxx.log形式命名
       STDOUT将日志信息输出到控制上,为方便开发测试使用
-->

<!--
    根节点<configuration>,包含下面三个属性
    1.scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
    2.scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
    3.debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->

<configuration scan="true" scanPeriod="10 seconds">

    <!--
        子节点<property name="" value=""> :用来定义变量值,它有两个属性name和value,通过<property>定义的值会被插入到logger上下文中,可以使“${}”来使用变量。
    -->
    <!--
        子节点<property resource=""/> :用来引入外部属性文件,可以使“${}”来使用变量。
    -->
    <property name="APP_NAME" value="IoServer4J"/>
    <property name="LOG_DIR" value="./logs/${APP_NAME}"/>

    <property name="LOG_FILE_NAME" value="sys"/>
    <property name="ERROR_LOG_FILE_NAME" value="error"/>
    <property name="DB_LOG_FILE_NAME" value="db"/>

    <property name="logFileNamePattern" value="${LOG_FILE_NAME}_%d{yyyy-MM-dd}_%i"/>
    <property name="errorLogFileNamePattern" value="${ERROR_LOG_FILE_NAME}_%d{yyyy-MM-dd}_%i"/>
    <property name="dbLogFileNamePattern" value="${DB_LOG_FILE_NAME}_%d{yyyy-MM-dd}_%i"/>

    <!-- 注册转换器,颜色转化器 -->
    <!-- 彩色日志依赖的渲染类 -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
    <conversionRule conversionWord="wex"
                    converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
    <conversionRule conversionWord="wEx"
                    converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>

    <!-- 日志输出格式 -->
    <!--
    日志输出格式:
        %d:             表示日期时间,
        %5p:            级别从左显示5个字符宽度,
        ${PID:- }:      pid
        %15.20t:        线程名字(如果宽度不足15,左侧补空白;如果宽度超过20,从左侧截断)
        %logger{50}:    表示logger名字最长50个字符,否则按照句点分割
        %%-4L:          行号,保留后面四位字符宽度
        %m:             日志消息
        %n:             换行符
        ${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}:
-->
    <property name="consoleLayoutPattern"
              value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.20t]){faint} %clr(%-40.40logger{60} Line:%-4L){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>

    <property name="fileLayoutPattern"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${PID:- } --- [%15.20t] %-40.40logger{60} Line:%-4L : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>

    <!--
        子节点<contextName>:用来设置上下文名称,每个logger都关联到logger上下文,默认上下文名称为default。但可以使用<contextName>设置成其他名字,用于区分不同应用程序的记录。一旦设置,不能修改。
    -->
    <contextName>${APP_NAME}</contextName>

    <!--
        子节点<appender>:负责写日志的组件,它有两个必要属性name和class。name指定appender名称,class指定appender的全限定名
        class为ch.qos.logback.core.ConsoleAppender 把日志输出到控制台
        class为ch.qos.logback.core.FileAppender 把日志添加到文件
        class为ch.qos.logback.core.rolling.RollingFileAppender 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件
    -->

    <!-- 控制台输出 -->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <charset>UTF-8</charset>
            <pattern>${consoleLayoutPattern}</pattern>
        </encoder>
    </appender>

    <appender name="SYS_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_DIR}/${LOG_FILE_NAME}.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_DIR}/${logFileNamePattern}.log.zip
            </fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <charset>UTF-8</charset>
            <pattern>${fileLayoutPattern}</pattern>
        </encoder>
    </appender>

    <appender name="ERROR_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_DIR}/${ERROR_LOG_FILE_NAME}.log</file>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_DIR}/${errorLogFileNamePattern}.log.zip
            </fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>30</maxHistory><!-- days -->
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <charset>UTF-8</charset>
            <pattern>${fileLayoutPattern}</pattern>
        </encoder>
    </appender>

    <appender name="DB_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_DIR}/${DB_LOG_FILE_NAME}.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_DIR}/${dbLogFileNamePattern}.log.zip
            </fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>30</maxHistory><!-- days -->
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <charset>UTF-8</charset>
            <pattern>${fileLayoutPattern}</pattern>
        </encoder>
    </appender>

    <!--
        子节点<logger>:用来设置某一个包或者具体的某一个类的日志打印级别,以及指定<appender>,
        logger 仅有一个name属性,两个可选属性 level/additivity
            name:用来指定受此logger约束的某一个包或者具体的某一个类。
            level:用来设置打印级别,大小写无关,TRACE,DEBUG,INFO,WARE,ERROR,ALL和OFF,还有一个特俗值INHERITED
                    或者 同义词NULL,代表强制执行上级的级别。
            additivity:是否向上级logger传递打印信息。默认是true。
    -->

    <!-- db/sql logger -->
    <logger name="java.sql.Connection" level="warn" additivity="true">
        <appender-ref ref="DB_LOG"/>
    </logger>
    <logger name="java.sql.PreparedStatement" level="warn" additivity="true">
        <appender-ref ref="DB_LOG"/>
    </logger>
    <logger name="java.sql.Statement" level="warn" additivity="true">
        <appender-ref ref="DB_LOG"/>
    </logger>

    <!-- sql -->
    <logger name="com.pilot.ioserver.basic.biz.pbl.core.db.mapper" level="warn" additivity="true">
        <appender-ref ref="DB_LOG"/>
    </logger>

    <!-- 生产环境下,将此级别配置为适合的级别,以免日志文件太多或影响程序性能 -->
    <!--
        <root>元素也是<logger>元素,logger。只有一个level属性。
        root 元素配置根 logger。该元素有一个 level 属性。没有 name 属性,因为已经被命名为“root”
        <root>可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个logger。
    -->
    <root level="warn">
        <!-- 控制台输出 -->
        <appender-ref ref="CONSOLE"/>
        <!-- 文件输出 -->
        <appender-ref ref="SYS_LOG"/>
        <!-- 错误日志 -->
        <appender-ref ref="ERROR_LOG"/>
    </root>
</configuration>

2.1 configuration

      configuration标签是logback文件的根目录。

2.1.1 configuration属性

configuration属性 默认值 解释
scan true 当设置为true时,如果配置文件发生改变,将会被重新加载
scanPeriod 1分钟 默认单位是毫秒,当scan为true时,此属性生效。作用:检测配置文件是否有修改时间间隔
debug false 当此属性为true时,将打印出logback内部日志信息,实时查看logback运行状态
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <!--其他配置省略-->
</configuration>

      上述配置代码每隔60毫秒会检查logback配置文件是否有修改,如果有修改就重写加载。并且不会输出logback的内部日志。

2.1.2 configuration子节点

configuration子节点 含义
contextName 设置日志上下文名称,后面输出格式中可以通过定义 %contextName 来打印日志上下文名称
property 用来设置相关变量,通过key-value的方式配置,然后在后面的配置文件中通过 ${key}来访问
timestamp 用于获取时间戳字符串
conversionRule 注册转换器,颜色转化器
appender 负责写日志的组件,主要负责日志的输出以及格式化日志
logger 用来设置某一个包或者具体的某一个类的日志输出级别,以及指定<appender>
root 用来指定最基础的日志输出级别
filter 通过使用该标签指定过滤策略
encoder 使用该标签下的标签指定日志输出格式
rollingPolicy 指定收集策略,比如基于时间进行收集

2.2 contextName

      contextName标签用于设置日志上下文名称,后面输出格式中可以通过定义%contextName来打印日志上下文名称。每个logger都关联到logger上下文,默认上下文名称为default。可以使用<contextName>设置成其他名字,用于区分不同的应用程序的记录。一旦设置,不能修改。

<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <contextName>myAppName</contextName>
    <!--其他配置省略-->
</configuration>

2.3 property

      property标签用来设置相关变量,通过key-value的方式配置,然后在后面的配置文件中通过 ${key}来访问。

<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <property name="APP_Name" value="myAppName"></property>
    <contextName>${APP_Name}</contextName>
    <!--其他配置省略-->
</configuration>

2.4 timestamp

      timestamp标签用于获取时间戳字符串,有两个属性key和datePattern。

timestamp属性 解释
key 标识此<timestamp>的名字
datePattern 设置将当前时间(即解析配置文件的时间)转换为字符串,遵循java.txt.SimpleDateFormat的格式
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <timestamp key="bySecond"datePattern="yyyyMMdd'T'HHmmss"></timestamp>
    <contextName>${bySecond}</contextName>
</configuration>

2.5 appender

      appender标签负责主要负责日志的输出以及格式化日志。当一个日志记录被触发的时候,logback会根据我们的配置信息把这个事件交给appender处理。appender有两个必要属性name和class。

appender属性

appender属性 解释
name 指定appender名称
class 指定appender的全限定名

      append属性class设置的不同,append子节点也不同,所以这里我们直接看appender对应的class类型有哪些。

appender class 类型

appender类型 class全限定名 解释
ConsoleAppender ch.qos.logback.core.ConsoleAppender 把日志输出到控制台
FileAppender ch.qos.logback.core.FileAppender 把日志输出到文件
RollingFileAppender ch.qos.logback.core.rolling.RollingFileAppender 滚动记录文件,先将日志文件指定到文件,当符合某个条件是,将日志记录到其他文件
SocketAppender ch.qos.logback.classic.net.SocketAppender 输出日志到远程实例中,明文
SSLSocketAppender ch.qos.logback.classic.net.SSLSocketAppender 输出日志到远程实例中,加密
SMTPAppender ch.qos.logback.classic.net.SMTPAppender 将日志以邮件的形式发出
DBAppender ch.qos.logback.classic.db.DBAppender 将日志保存在数据库中
SyslogAppender ch.qos.logback.classic.net.SyslogAppender 将日志发送给远程的receiver
SiftingAppender ch.qos.logback.classic.sift.SiftingAppender SiftingAppender提供过滤筛选日志的功能。你可以通过用户的sessions的数据来筛选日志,然后分发到不同日志文件
AsyncAppender ch.qos.logback.classic.AsyncAppender 记录ILoggingEvents的方式是异步的。它仅仅相当于一个event分配器,因此需要配合其他appender才能有所作为

      ConsoleAppender、FileAppender、RollingFileAppender。这几个appender是最常用的,我们对他们的使用做一个简单的介绍。

2.5.1 ConsoleAppender

      ConsoleAppender用于把日志输出到控制台。

      如下配置会把DEBUG级别的日志输出到控制台。

<configuration>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    <encoder>
      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>

      ConsoleAppender节点介绍。

ConsoleAppender子节点 解释
encoder 对日志进行格式化
target 字符串System.out(默认)或者System.err
filter 对某些日志做过滤

2.5.2 FileAppender

      FileAppender用于把日志添加到文件。

      比如如下的配置会把DEBUG级别的日志输出到log-${bySecond}.txt文件。

<configuration>

  <!-- Insert the current time formatted as "yyyyMMdd'T'HHmmss" under
       the key "bySecond" into the logger context. This value will be
       available to all subsequent configuration elements. -->
  <timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>

  <appender name="FILE" class="ch.qos.logback.core.FileAppender">
    <!-- use the previously created timestamp to create a uniquely
         named log file -->
    <file>log-${bySecond}.txt</file>
    <encoder>
      <pattern>%logger{35} - %msg%n</pattern>
    </encoder>
  </appender>

  <root level="DEBUG">
    <appender-ref ref="FILE" />
  </root>
</configuration>

      FileAppender节点介绍。

FileAppender子节点 解释
file 被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值
append 默认为true。如果是true,日志被追加到文件结尾,如果是false,清空现存文件
encoder 对记录事件进行格式化
prudent 默认为false。如果是true,日志会被安全的写入文件,即其他的FileAppender也在向此文件做写入操作,效率低
filter 对某些日志做过滤

2.5.3 RollingFileAppender

      RollingFileAppender是对FileAppender的一个扩展。相较于它的父类,它的主要作用是滚动记录日志。先将日志文件指定到文件,当符合某个条件时,将日志记录到其他文件。所以这里有两个重要的点,一是什么时候滚动(触发triggeringPolicy)、二是发生滚动的时候怎么去保存文件(动作,策略rollingPolicy)。

      RollingFileAppender节点介绍。

RollingFileAppender子节点

RollingFileAppender子节点 解释
file 被写入的文件名,可以是相对目录,也可以是绝对目录,如果上级目录不存在会自动创建,没有默认值,就说说日志会先保存在这个文件里面
append 默认为true。如果是true,日志被追加到文件结尾,如果是false,清空现存文件
encoder 对记录事件进行格式化
rollingPolicy 发送滚动是,RollingFileAppender的行为。例如日志文件名的修改
triggeringPolicy 决定什么时候发送日志滚动,例如日期,日志文件大小到达一定值
prudent 当为true时,不支持FixedWindowRollingPolicy。支持TimeBasedRollingPolicy,但是有两个限制,1不支持也不允许文件压缩,2不能设置file属性,必须留空
filter 对某些日志做过滤

2.5.3.1 rollingPolicy介绍

      rollingPolicy节点用于定义当发生日志切换时(发生日志滚动是),RollingFileAppender的切换行为。例如日志文件名的修改。(RollingPolicy实际上就是负责日志文件的切换以及重命名的)。logback里面已经给我们提供个多种切换的实现类。下面我们对这些实现类做一个简单的介绍。

rollingPolicy类型 解释
TimeBasedRollingPolicy 是最受欢迎的日志滚动策略。它的滚动策略是基于时间的,例如根据天数,月份
SizeAndTimeBasedRollingPolicy 有时候你不仅想通过时间来规定滚动策略,还希望同时限制每个日志文件的大小。在TimeBasedRoolingPolicy中已经提供限制总日志文件的大小的功能,而SizeAndTimeBasedRollingPolicy提供了更为强大的,针对单个日志文件的大小限制能力
FixedWindowRollingPolicy 固定窗口的日志滚动策略
2.5.3.1.1 TimeBasedRollingPolicy

      是最受欢迎的日志滚动策略。它的滚动策略是基于时间的。根据时间触发滚动(fileNamePattern属性会解析到),同时可以设置保留的文件的最大大小(超过最大大小就会把之前的覆盖掉),以及最大保留的粒度(如果是触发保存是按天来的,可以设置最大保留多少天的日志。如果是月,可以设置最多保留多少月等等)。

注意,TimeBasedRollingPolicy已经包含了triggeringPolicy节点的功能。所以设置了rollingPolicy为TimeBasedRollingPolicy就不需要在设置triggeringPolicy了。

      比如如下的配置,会每天保存一个日志文件,并且最大保存30天的日志,同时限制所以日志文件的最大大小为3G。

<configuration>
  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>logFile.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!-- daily rollover -->
      <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>

      <!-- keep 30 days' worth of history capped at 3GB total size -->
      <maxHistory>30</maxHistory>
      <totalSizeCap>3GB</totalSizeCap>

    </rollingPolicy>

    <encoder>
      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
    </encoder>
  </appender> 

  <root level="DEBUG">
    <appender-ref ref="FILE" />
  </root>
</configuration>

      TimeBasedRollingPolicy属性介绍。

TimeBasedRollingPolicy属性 类型 解释
fileNamePattern String 必要节点,包含文件名及"%d"转换符,"%d"可以包含一个java.text.SimpleDateFormat指定的时间格式,默认格式是yyyy-MM-dd。也可以设置其他格式,如:%d{yyyy-MM}
maxHistory int 可选参数,声明归档日志最大保留时间。如果你是基于月份的日志滚动,则当maxHisory为6时,说明会保留6个月的日志。大于6个月的就会被删除。日志所存在的目录也会被合适的删除掉
totalSizeCap String 可选参数,声明归档日志的最大存储量。当超过这个值,最老的归档日志文件也会被删除
cleanHistoryOnStart boolean 可选参数,声明归档日志的最大存储量。当超过这个值,最老的归档日志文件也会被删除 可选参数,默认为false,如果设置为true,则当appender启动时,会删除所有归档日志文件

旧版本的logback里可以在TimeBasedRollingPolicy这个rollingPolicy下配置一个timeBasedFileNamingAndTriggeringPolicy(实现类为SizeAndTimeBasedFNATP)达到同时配置时间和文件大小的滚动策略;而在新版本里可以使用SizeAndTimeBasedRollingPolicy替代。

2.5.3.1.2 SizeAndTimeBasedRollingPolicy

      有时候你不仅想通过时间来规定滚动策略,还希望同时限制每个日志文件的大小。在TimeBasedRoolingPolicy中已经提供限制总日志文件的大小的功能,而SizeAndTimeBasedRollingPolicy提供了更为强大的针对单个日志文件的大小限制能力。

      比如如下的配置,按天触发日志的保存,同时限制了单个文件的大小。所以每天可能有多个日志文件。关于fileNamePattern的使用我们会在下文中讲到。

<configuration>
  <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>mylog.txt</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
      <!-- rollover daily -->
      <fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
       <!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
       <maxFileSize>100MB</maxFileSize>    
       <maxHistory>60</maxHistory>
       <totalSizeCap>20GB</totalSizeCap>
    </rollingPolicy>
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>


  <root level="DEBUG">
    <appender-ref ref="ROLLING" />
  </root>

</configuration>

      SizeAndTimeBasedRollingPolicy属性介绍,SizeAndTimeBasedRollingPolicy继承TimeBasedRollingPolicy就额外的多一个属性maxFileSize

SizeAndTimeBasedRollingPolicy属性 类型 解释
fileNamePattern String 必要节点,包含文件名及"%d"转换符,"%d"可以包含一个java.text.SimpleDateFormat指定的时间格式,默认格式是yyyy-MM-dd。也可以设置其他格式,如:%d{yyyy-MM}
maxHistory int 可选参数,声明归档日志最大保留时间。如果你是基于月份的日志滚动,则当maxHisory为6时,说明会保留6个月的日志。大于6个月的就会被删除。日志所存在的目录也会被合适的删除掉
totalSizeCap String 可选参数,声明归档日志的最大存储量。当超过这个值,最老的归档日志文件也会被删除
cleanHistoryOnStart boolean 可选参数,声明归档日志的最大存储量。当超过这个值,最老的归档日志文件也会被删除 可选参数,默认为false,如果设置为true,则当appender启动时,会删除所有归档日志文件
maxFileSize String 限定单个日志文件的大小
2.5.3.1.3 FixedWindowRollingPolicy

      基于窗口大小的滚动策略。这个听起来可能有点难理解,其实说白了就是将归档日志文件到最大了就写到下一个文件里,而窗口大小就是最多允许多少份日志文件。要特别注意FixedWindowRollingPolicy是没有告知什么时候触发日志的保存的。所以FixedWindowRollingPolicy的使用需要配合triggeringPolicy标签一起使用,而且一般triggeringPolicy标签会设置SizeBasedTriggeringPolicy。

      比如如下的配置,保存DEBUG级别的日志,并且每5MB保存一个文件,窗口大小是1到3,当保存了3个归档文件后,将覆盖最早的日志。

<configuration>
  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>test.log</file>

    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>tests.%i.log.zip</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>3</maxIndex>
    </rollingPolicy>

    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>5MB</maxFileSize>
    </triggeringPolicy>
    <encoder>
      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
    </encoder>
  </appender>
        
  <root level="DEBUG">
    <appender-ref ref="FILE" />
  </root>
</configuration>

      FixedWindowRollingPolicy属性属性介绍。

FixedWindowRollingPolicy属性 类型 解释
minIndex int 这个参数指定窗口索引的最小值
maxIndex int 这个参数指定窗口索引的最大值
fileNamePattern String 这个参数与之前的fileNamePattern没什么差别,唯一需要注意的是必须包含%i标识符,这个标识符的作用是指明当前窗口索引的值。例如将fileNamePattern设置成 "MyLogFile%i ",minIndex为1,maxIndex为3,则会创建MyLogFile1.log, MyLogFile2.log and MyLogFile3.log.这三个归档日志文件。

特别要注意,如果rollingPolicy标签设置为FixedWindowRollingPolicy。一定要设置triggeringPolicy标签的内容。要不然是没办法触发保存日志的。

2.5.3.2 triggeringPolicy介绍

      triggeringPolicy标签设置的值用于告知RollingFileAppender什么时候触发滚动操作。上面rollingPolicy介绍的各种rollingPolicy类型里面,其实TimeBasedRollingPolicy和SizeAndTimeBasedRollingPolicy已经包含了triggeringPolicy的功能(会通过fileNamePattern属性解析出来),只有FixedWindowRollingPolicy是没有triggeringPolicy功能的,所以FixedWindowRollingPolicy要设置triggeringPolicy。一般都是设置SizeBasedTriggeringPolicy。

      SizeBasedTriggeringPolicy只有一个属性,就是文件大小。当到达了设置的大小的时候RollingFileAppender就触发一次滚动操作。

SizeBasedTriggeringPolicy属性 类型 解释
maxFileSize String 默认10MB, 当到达这个容量,就开启日志滚动

2.6 filter

      logback的过滤器可以过滤记录日志的内容,执行一个过滤器会有返回个枚举值,即DENY,NEUTRAL,ACCEPT其中之一。从而将不符合条件的日志信息过滤掉。

  • DENY: 日志将立即被抛弃不再经过其他过滤器。
  • NEUTRAL: 有序列表里的下个过滤器过接着处理日志。
  • ACCEPT: 日志会被立即处理,不再经过剩余过滤器。

      过滤器被添加到<Appender>中,为<Appender>添加一个或多个过滤器后,可以用任意条件对日志进行过滤。<Appender>有多个过滤器时,按照配置顺序执行。

      关于Filter的实现类有多种,这里我们主要讲两种:LevelFilter以及ThresholdFilter.

2.6.1 LevelFilter

      级别过滤器,根据日志级别进行过滤。如果日志级别等于配置级别,过滤器会根据onMath 和 onMismatch接收或拒绝日志。

LevelFilter子节点 解释
level 设置过滤级别
onMatch 用于配置符合过滤条件的操作
onMismatch 用于配置不符合过滤条件的操作

      比如以下的配置,只接受ERROR级别的日志,非ERROR级别的日志都会被过滤掉。

    <appender name="ERROR_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_DIR}/${ERROR_LOG_FILE_NAME}.log</file>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_DIR}/${errorLogFileNamePattern}.log.zip
            </fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>30</maxHistory><!-- days -->
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <charset>UTF-8</charset>
            <pattern>${fileLayoutPattern}</pattern>
        </encoder>
    </appender>

2.6.2 ThresholdFilter

      临界值过滤器,过滤掉低于指定临界值的日志。ThresholdFilter只有一个level子节点可以设置。当日志级别等于或高于临界值时,过滤器返回NEUTRAL;当日志级别低于临界值时,日志会被拒绝。

      比如如下的配置,过滤掉TRACE和DEBUG级别的日志。

    <appender name="ERROR_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_DIR}/${ERROR_LOG_FILE_NAME}.log</file>
        <!-- 过滤掉 TRACE 和 DEBUG 级别的日志-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_DIR}/${errorLogFileNamePattern}.log.zip
            </fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>30</maxHistory><!-- days -->
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <charset>UTF-8</charset>
            <pattern>${fileLayoutPattern}</pattern>
        </encoder>
    </appender>

2.6.3 EvaluatorFilter

      求值过滤器,评估、鉴别日志是否符合指定条件。需要额外的两个JAR包,commons-compiler.jar和janino.jar。

EvaluatorFilter子节点 解释
evaluator 鉴别器,常用的鉴别器是JaninoEventEvaluato,也是默认的鉴别器,它以任意的java布尔值表达式作为求值条件,求值条件在配置文件解释过成功被动态编译,布尔值表达式返回true就表示符合过滤条件。evaluator有个子标签<expression>,用于配置求值条件
onMatch 用于配置符合过滤条件的操作
onMismatch 用于配置不符合过滤条件的操作

      这里注意下evaluator子节点的使用,。evaluator有个子标签<expression>,用于配置求值条件。求值表达式作用于当前日志,logback向求值表达式暴露日志的各种字段如下:

expression可以使用字段 类型 含义
event LoggingEvent 与记录请求相关联的原始记录事件,下面所有变量都来自event,例如,event.getMessage()返回下面"message"相同的字符串
message String 日志的原始消息,例如,设有logger mylogger,"name"的值是"AUB",对于 mylogger.info("Hello {}",name); "Hello {}"就是原始消息。
formatedMessage String 日志被各式话的消息,例如,设有logger mylogger,"name"的值是"AUB",对于 mylogger.info("Hello {}",name); "Hello Aub"就是格式化后的消息。
logger String logger 名。
loggerContext LoggerContextVO 日志所属的logger上下文。
level int 级别对应的整数值,所以 level > INFO 是正确的表达式。
timeStamp long 创建日志的时间戳。
marker Marker 与日志请求相关联的Marker对象,注意“Marker”有可能为null,所以你要确保它不能是null
mdc Map 包含创建日志期间的MDC所有值得map。访问方法是:mdc.get("myKey") 。mdc.get()返回的是Object不是String,要想调用String的方法就要强转,例如,((String) mdc.get("k")).contains("val") .MDC可能为null,调用时注意。
throwable java.lang.Throwable 如果没有异常与日志关联"throwable" 变量为 null. 不幸的是, "throwable" 不能被序列化。在远程系统上永远为null,对于与位置无关的表达式请使用下面的变量throwableProxy
throwableProxy IThrowableProxy 与日志事件关联的异常代理。如果没有异常与日志事件关联,则变量"throwableProxy" 为 null. 当异常被关联到日志事件时,"throwableProxy" 在远程系统上不会为null

      比如如下的配置,过滤掉所有日志消息中不包含“billing”字符串的日志。

    <appender name="ERROR_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_DIR}/${ERROR_LOG_FILE_NAME}.log</file>
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
            <evaluator> <!-- 默认为 ch.qos.logback.classic.boolex.JaninoEventEvaluator -->
                <expression>return message.contains("billing");</expression>
            </evaluator>
            <OnMatch>ACCEPT </OnMatch>
            <OnMismatch>DENY</OnMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_DIR}/${errorLogFileNamePattern}.log.zip
            </fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>30</maxHistory><!-- days -->
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <charset>UTF-8</charset>
            <pattern>${fileLayoutPattern}</pattern>
        </encoder>
    </appender>

2.7 logger,root

2.7.1 logger

      用来设置某一个包或者具体某一个类的日志打印级别,以及指定<appender>。什么意思。

logger属性 解释
name 用来指定受此loger约束的某一个包或者具体的某一个类
level 用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL和OFF,还有一个特俗值INHERITED或者同义词NULL,代表强制执行上级的级别。如果未设置此属性,那么当前loger将会继承上级(root标签)的级别
addtivity 是否向上级loger传递打印信息。默认是true

      关于addtivity。如果addtivity设置为true,那么当前logger标签作用完之后,logger标签对应类或者包的父类,继续作用一直到root标签。如果addtivity设置为false,那么当前logger标签作用完之后就直接掐断了,不在继续往上走了。

      <loger>可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个loger。<appender-ref>标签就是去指定日志的相应操作,不管是控制台输出,还是保存到文件,还是滚动日志输出啥的。

2.7.2 root

      <root>也是<loger>元素,但是它是根loger。只有一个level属性。用来设置打印级别。

root是不用指定包或者类的,我们可以简单的认为root指定了所有的包.

      这里要重点说下logger和root的关系。怕有些同学搞混掉.我们通过几个例子来说明。

2.7.3 root,logger关系

      关于root和logger之间的关系要重点讲下。当root和logger同时存在的时候。要特别注意logger和root标签两者最终的level;和logger标签是否设置了向上传递的属性。

  • logger没有设置level,那么logger直接继承root的level(如果有多个logger并且logger直接有上下级关系的话,就一层一层往上)。
  • logger设置了level,同时如果logger设置了向上传递的话(additivity="true")。那么root标签和logger相同的包也使用logger的level。

2.7.3.1 情况一

    <logger name="com.tuacy.logback"/>

    <root level="info">
        <!-- 控制台输出 -->
        <appender-ref ref="CONSOLE"/>
    </root>

      因为logger没有设置等级,直接继承root的等级.addtivity默认就是true向上传递.可以认为当com.tuacy.logback包里面某个类打印日志的时候使用的配置为.

    <logger name="com.tuacy.logback" level="info" additivity="true"/>

    <root level="info">
        <!-- 控制台输出 -->
        <appender-ref ref="CONSOLE"/>
    </root>

      logger没有设置appender-ref所以logger没啥用,不打印,这个时候只有root设置起作用,com.tuacy.logback包下面的类info,warn,error的日志都会打印出来。

2.7.3.2 情况二

    <logger name="com.tuacy.logback" additivity="false"/>

    <root level="info">
        <!-- 控制台输出 -->
        <appender-ref ref="CONSOLE"/>
    </root>

      logger没有设置等级,继承root的等级,additivity为false,不往上传递,root不作用。配置相当于

    <logger name="com.tuacy.logback" additivity="false"/>

      如果这个时候有com.tuacy.logback包或者子包有打印的时候,不会有任何打印信息。

2.7.3.3 情况三

    <logger name="com.tuacy.logback" level="error"/>

    <root level="info">
        <!-- 控制台输出 -->
        <appender-ref ref="CONSOLE"/>
    </root>

      logger和root都有设置等级取两者等级较高的。所以上述配置等价于

    <logger name="com.tuacy.logback" level="error" additivity="true"/>

    <root level="error">
        <!-- 控制台输出 -->
        <appender-ref ref="CONSOLE"/>
    </root>

      这个时候只有error级别的信息会打印出来,root也使用logger的level。注意,只是针对com.tuacy.logback包和他的子包root的等级改变了。其他的包root还是保持原来的level。

2.7.3.4 情况四

    <logger name="com.tuacy.logback" level="error" additivity="false"/>

    <root level="info">
        <!-- 控制台输出 -->
        <appender-ref ref="CONSOLE"/>
    </root>

      上述配置等价于

    <logger name="com.tuacy.logback" level="error" additivity="false"/>

      不会有打印的

2.7.3.5 情况五

    <logger name="com.tuacy.logback" level="error">
        <!-- 控制台输出 -->
        <appender-ref ref="CONSOLE"/>
    </logger>

    <root level="info">
        <!-- 控制台输出 -->
        <appender-ref ref="CONSOLE"/>
    </root>

      上述配置等价于

    <logger name="com.tuacy.logback" level="error">
        <!-- 控制台输出 -->
        <appender-ref ref="CONSOLE"/>
    </logger>

    <root level="error">
        <!-- 控制台输出 -->
        <appender-ref ref="CONSOLE"/>
    </root>

      会打印error级别的日志,而且会打印两次。logger的一次,root的一次。

2.7.3.6 情况六

    <logger name="com.tuacy.logback" level="error" additivity="false">
        <!-- 控制台输出 -->
        <appender-ref ref="CONSOLE"/>
    </logger>

    <root level="info">
        <!-- 控制台输出 -->
        <appender-ref ref="CONSOLE"/>
    </root>

      上述配置等价于

    <logger name="com.tuacy.logback" level="error" additivity="false">
        <!-- 控制台输出 -->
        <appender-ref ref="CONSOLE"/>
    </logger>

      所以只会打印一次error级别的日志信息。

2.8 日志模板配置

      日志模板配置,使用%为前缀让解析器识别特殊输出模式,然后以{}后缀结尾,内部指定相应的参数设置。

2.8.1 模板输入参数

模板输入参数 对应的类 对应输出
d DateConverter 输出logger时间
date DateConverter 输出logger时间
r RelativeTimeConverter 输出应用启动到日志时间触发时候的毫秒数
relative RelativeTimeConverter 输出应用启动到日志时间触发时候的毫秒数
level LevelConverter 输出logger对应的等级
le LevelConverter 输出logger对应的等级
p LevelConverter 输出logger对应的等级
t ThreadConverter 输出logger对应线程名字
thread ThreadConverter 输出logger对应线程名字
lo LoggerConverter 输出logger对应的名字
logger LoggerConverter 输出logger对应的名字
c LoggerConverter 输出logger对应的名字
m MessageConverter 输出logger对应的信息
msg MessageConverter 输出logger对应的信息
message MessageConverter 输出logger对应的信息
C ClassOfCallerConverter 输出调用方发布日志事件的完整类名
class ClassOfCallerConverter 输出调用方发布日志事件的完整类名
M MethodOfCallerConverter 输出发布日志请求的方法名
method MethodOfCallerConverter 输出发布日志请求的方法名
L LineOfCallerConverter 输出log请求的行数
line LineOfCallerConverter 输出log请求的行数
F FileOfCallerConverter 输出发布日志请求的java源码的文件名
file FileOfCallerConverter 输出发布日志请求的java源码的文件名
X MDCConverter 输出和发布日志事件关联的线程的MDC
mdc MDCConverter 输出和发布日志事件关联的线程的MDC
ex ThrowableProxyConverter 输出和日志事件关联的异常的堆栈信息
exception ThrowableProxyConverter 输出和日志事件关联的异常的堆栈信息
rEx RootCauseFirstThrowableProxyConverter 输出和日志事件关联的异常的堆栈信息
rootException RootCauseFirstThrowableProxyConverter 输出和日志事件关联的异常的堆栈信息
throwable ThrowableProxyConverter 输出和日志事件关联的异常的堆栈信息
xEx ExtendedThrowableProxyConverter 和上面一样,此外增加类的包信息
xException ExtendedThrowableProxyConverter 和上面一样,此外增加类的包信息
xThrowable ExtendedThrowableProxyConverter 和上面一样,此外增加类的包信息
nopex NopThrowableInformationConverter 当我们想不输出异常信息时,使用这个。其假装处理异常,其实无任何输出
nopexception NopThrowableInformationConverter 当我们想不输出异常信息时,使用这个。其假装处理异常,其实无任何输出
cn ContextNameConverter 输出在类附加到日志上的上下文名字
contextName ContextNameConverter 输出在类附加到日志上的上下文名字
caller CallerDataConverter 输出产生日志事件的调用者的位置信息
marker MarkerConverter 输出和日志请求关联的marker
property PropertyConverter 输出属性对应的值,一般为System.properties中的属性
n LineSeparatorConverter 输出依赖系统的行分隔符
black BlackCompositeConverter 颜色格式black
red RedCompositeConverter 颜色格式red
green GreenCompositeConverter 颜色格式green
yellow YellowCompositeConverter 颜色格式yellow
blue BlueCompositeConverter 颜色格式blue
magenta MagentaCompositeConverter 颜色格式magenta
cyan CyanCompositeConverter 颜色格式cyan
white WhiteCompositeConverter 颜色格式white
gray GrayCompositeConverter 颜色格式gray
boldRed BoldRedCompositeConverter 颜色格式boldRed
boldGreen BoldGreenCompositeConverter 颜色格式boldGreen
boldYellow BoldYellowCompositeConverter 颜色格式boldYellow
boldBlue BoldBlueCompositeConverter 颜色格式boldBlue
boldMagenta BoldMagentaCompositeConverter 颜色格式boldMagenta
boldCyan BoldCyanCompositeConverter 颜色格式boldCyan
boldWhite BoldWhiteCompositeConverter 颜色格式boldWhite
highlight HighlightingCompositeConverter 颜色格式highlight
lsn LocalSequenceNumberConverter 颜色格式lsn

      d,data可以配置额外的参数,用来确定日期格式。输出日志的打印日志,模式语法与java.text.SimpleDateFormat 兼容。

      c、lo、logger输出日志的logger名,可有一个整形参数,功能是缩短logger名,设置为0表示只输入logger最右边点符号之后的字符串

模板 数据源 结果
%logger mainPackage.sub.sample.Bar mainPackage.sub.sample.Bar
%logger{0} mainPackage.sub.sample.Bar Bar
%logger{5} mainPackage.sub.sample.Bar m.s.s.Bar
%logger{10} mainPackage.sub.sample.Bar m.s.s.Bar
%logger{15} mainPackage.sub.sample.Bar m.s.sample.Bar
%logger{16} mainPackage.sub.sample.Bar m.sub.sample.Bar
%logger{26} mainPackage.sub.sample.Bar mainPackage.sub.sample.Bar

2.8.2 模板长度设置

      每个模板配置都是可以限制模长度的,上面介绍了模块可以设置那么多的输入参数,其实每个输入参数都是可以限制长度的。

日志模板配置 解释
%5p 级别从左显示5个字符宽度(从左到右)
%-4L 行号,保留后面四位字符宽度(从右到左)
%15.20t 线程名字(如果宽度不足15,左侧补空白;如果宽度超过20,从左侧截断)

三 spring boot项目使用logback

3.1 pom依赖

      在Spring Boot项目中要使用logback原则上是要添加spring-boot-starter-logging依赖的。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>

但是因为我们使用Spring Boot肯定会添加

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

又因为spring-boot-starter里面已经包含了对于spring-boot-starter-logging的依赖,所以我们一般在项目中不用显示的添加spring-boot-starter-logging。

3.1 logback配置文件

      spring boot项目中使用logback.关于配置文件官方推荐使用的xml名字的格式为:logback-spring.xml而不是logback.xml(当然了,你也可以使用logback.xml),因为带spring后缀的可以使用<springProfile>这个标签来控制比如生成环境还是调试环境。比如如下的配置分了对生成环境和开发环境做了一个简单的区分.

<configuration scan="true" scanPeriod="10 seconds">

...

    <!-- 生产环境下,将此级别配置为适合的级别,以免日志文件太多或影响程序性能 -->
    <!--
        <root>元素也是<logger>元素,logger。只有一个level属性。
        root 元素配置根 logger。该元素有一个 level 属性。没有 name 属性,因为已经被命名为“root”
        <root>可以包含零个或多个<appender-ref>元素,标识这个appender将会添加到这个logger。
    -->
    <springProfile name="dev">
        <root level="info">
            <!-- 控制台输出 -->
            <appender-ref ref="CONSOLE"/>
            <!-- 文件输出 -->
            <appender-ref ref="SYS_LOG"/>
            <!-- 错误日志 -->
            <appender-ref ref="ERROR_LOG"/>
        </root>
    </springProfile>
    <springProfile name="pro">
        <root level="warn">
            <!-- 控制台输出 -->
            <appender-ref ref="CONSOLE"/>
            <!-- 文件输出 -->
            <appender-ref ref="SYS_LOG"/>
            <!-- 错误日志 -->
            <appender-ref ref="ERROR_LOG"/>
        </root>
    </springProfile>
</configuration>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容