日志框架的了解与使用

什么是日志框架?

  • 是一套能实现日志输出的工具包
  • 能够描述系统运行状态的所有时间都可以算作日志,比如:用户下线、接口超时、数据库崩溃以及Hello World等。

日志框架的能力

  • 定制输出目标:需要把日志输出到文件,某些系统可能还需要定制日志文件的滚动策略,比如一天一个日志文件之类的。
  • 定制输入格式:可以通过配置文件修改日志格式的具体内容,在不修改代码的前提下,可以自由的修改日志格式,方便后期的加工和处理。
  • 携带上下文信息:上下文信息包括很多,比如时间戳、类路径、线程等。
  • 运行时选择性输出:比如当前系统正常,那我只关注正常时的日志。假如当前系统响应特别慢,那我可能就比较关心数据库访问层的问题,这时候如果能把这块的细节通过日志打印出来就显得尤为重要了。
  • 灵活的配置:我们需要的各种能力,如果都需要编写代码上线重启才能实现,那永远都做不出来牛逼系统。好的解决方案一定是可以通过灵活的配置来实现的。
  • 优异的性能:日志终归是正常业务之外的审计和运维需求,如果因为这个拖慢了整个系统的性能那就得不偿失了。所以一款优秀的日志一定具备优异的性能。

主流日志框架Slf4j的使用

实例代码1
LoggerTest.java

@RunWith(SpringRunner.class)
@SpringBootTest
public class LoggerTest {

    private final Logger logger= LoggerFactory.getLogger(LoggerTest.class);

    @Test
    public void test1(){
        logger.debug("debug...");
        logger.info("info...");
        logger.error("error...");
    }
}

执行结果:

2018-11-27 20:04:48.222 INFO 10064 — [ main]
com.ft.LoggerTest : info… 2018-11-27
20:04:48.223 ERROR 10064 — [ main] com.ft.LoggerTest
: error…

结果分析:

可以看到执行结果里面只是打印了info和error的记录,debug并没有打印出来。这是由于系统默认日志级别是info,级别在info及以上的就能打印出来,而debug级别在info之下。

如何查看日志级别?
在项目里搜索Level类(位于org.slf4j.event包下),就能知道日志级别。

ERROR(40, “ERROR”),
WARN(30, “WARN”),
INFO(20, “INFO”),
DEBUG(10, “DEBUG”),
TRACE(0, “TRACE”);

实例代码2

private final Logger logger= LoggerFactory.getLogger(LoggerTest.class);

以上这行代码参数是LoggerTest.class,表示我要告诉日志框架这些日志信息都是来源于LoggerTest类,就好比我们使用IDEA控制台报错了,我们只要点击报错类的超链接就能快速定位到目标类。但是,如果这个参数我写成了LoggerTest1.class,然后打印了报错信息,那么就会被定位到LoggerTest1这个类,其实真实的报错信息还是来源于LoggerTest类。
这里有一种灵活的配置方式,不需要提前指定类,只需要标识@Slf4J注解即可。
LoggerTest.java

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class LoggerTest {

    @Test
    public void test1(){
        log.debug("debug...");
        log.info("info...");
        log.error("error...");
    }
}

结果分析:

这里执行的结果跟实例代码1一致,值得注意的是,这里的@Slf4j来源于lombok.extern.slf4j.Slf4j包,所以pom.xml文件里需要引入lombok包。此外,在标明@Slf4j之后,有些人log找不到,这是因为IDEA缺少lombok插件,安装下该插件即可。

实例代码3(如何在日志里输出变量)
LoggerTest.java

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class LoggerTest {

    @Test
    public void test1(){
        String name="taofut";
        String password="123456";
        log.debug("debug...");
        log.info("name: "+name+", password: "+password);
        //更优
        log.info("name: {}, password: {}",name,password);
        log.error("error...");
    }
}

执行结果:

2018-11-27 20:39:11.017 INFO 8128 — [ main]
com.ft.LoggerTest : name: taofut, password:
123456 2018-11-27 20:39:11.018 INFO 8128 — [ main]
com.ft.LoggerTest : name: taofut, password:
123456 2018-11-27 20:39:11.018 ERROR 8128 — [ main]
com.ft.LoggerTest : error…

结果分析:
以上的2行log.info代码都能正常输出结果,但是更推荐后面那种采用占位符的形式,因为前面那种在变量很多的情况下,拼接起来特别麻烦。

Logback的配置之application.yml的配置

这种配置方式相对比较简单,只能配置一些日志文件的路径、日志输出格式等。

实例代码1
LoggerTest.java

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class LoggerTest {

    @Test
    public void test1(){
        String name="taofut";
        String password="123456";
        log.debug("debug...");
        log.info("name: "+name+", password: "+password);
        //更优
        log.info("name: {}, password: {}",name,password);
        log.error("error...");
    }
}

application.yml配置内容:

logging:
  pattern:
    console: "%d - %msg%n"

执行结果:

2018-11-27 21:07:44,068 - name: taofut, password: 123456 2018-11-27
21:07:44,069 - name: taofut, password: 123456 2018-11-27 21:07:44,069

  • error…

结果分析:

实例代码跟前面的一样,但是执行结果内容明显比之前的短了很多,这是因为在application.yml指定了日志的输出格式只包括时间和内容。

实例代码2
application.yml配置内容里新增日志的输出路径path:

logging:
  pattern:
    console: "%d - %msg%n"
  path: d:/log/

再次执行LoggerTest.java,在d:/log/下可以找到一个spring.log的日志文件。文件里面的内容就是我们控制台输出的所有日志,但是会比控制台的日志信息更加全面。

实例代码3
application.yml配置内容里新增日志的输出路径file:

logging:
  pattern:
    console: "%d - %msg%n"
  path: d:/log/
  file: d:/log/taofut.log

再次执行LoggerTest.java,在d:/log/下可以找到一个taofut.log的日志文件。文件里面的内容就是我们控制台输出的所有日志,但是会比控制台的日志信息更加全面。也就是说file不指定的话,输出日志的文件名默认为spring.log,file指定的话,输出日志的文件名就是指定的。

Logback的配置之logback-spring.xml的配置

这种配置方式可以进行比较复杂的配置。

实例代码1(配置控制台日志内容的输出)
项目resources目录下新增logback-spring.xml文件,配置文件内容为:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>
    <!--配置项-->
    <appender name="consolelog" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>
                %d - %msg%n
            </pattern>
        </layout>
    </appender>

    <root level="info">
        <appender-ref ref="consolelog" />
    </root>
</configuration>

执行LoggerTest.java,结果跟之前的一致。

实例代码2(配置日志内容输出到磁盘文件)
logback-spring.xml文件内容:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>
    <!--配置控制台日志输出格式-->
    <appender name="consolelog" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>
                %d - %msg%n
            </pattern>
        </layout>
    </appender>

    <appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>
                %msg%n
            </pattern>
        </encoder>
        <!--滚动策略-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--路径-->
            <fileNamePattern>
                d:/log/taofut/info.%d.log
            </fileNamePattern>
        </rollingPolicy>
    </appender>

    <!--指定在root info级别下使用该配置-->
    <root level="info">
        <appender-ref ref="consolelog" />
        <appender-ref ref="fileInfoLog" />
    </root>
</configuration>

执行LoggerTest.java,可以发现在 d:/log/taofut/目录下有了一个info.2018-11-27.log的文件,这个时间是当前时间。

实例代码3
实例代码2执行完的info.2018-11-27.log文件内容为:
日志框架的了解与使用
既然是info日志文件,但是error日志也被包含到里面去了,所以下面需要将info日志与error日志分开。
logback-spring.xml文件内容:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>
    <!--配置控制台日志输出格式-->
    <appender name="consolelog" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>
                %d - %msg%n
            </pattern>
        </layout>
    </appender>

    <appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <encoder>
            <pattern>
                %msg%n
            </pattern>
        </encoder>
        <!--滚动策略:每天创建一个文件-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--路径-->
            <fileNamePattern>
                d:/log/taofut/info.%d.log
            </fileNamePattern>
        </rollingPolicy>
    </appender>

    <appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--拦截器,控制只输出error日志-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
        <encoder>
            <pattern>
                %msg%n
            </pattern>
        </encoder>
        <!--滚动策略:每天创建一个文件-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--路径-->
            <fileNamePattern>
                d:/log/taofut/error.%d.log
            </fileNamePattern>
        </rollingPolicy>
    </appender>

    <!--指定在root info级别下使用该配置-->
    <root level="info">
        <appender-ref ref="consolelog" />
        <appender-ref ref="fileInfoLog" />
        <appender-ref ref="fileErrorLog" />
    </root>
</configuration>

同理,执行LoggerTest.java,可以发现在 d:/log/taofut/目录下有了一个error.2018-11-27.log的文件,这个时间是当前时间。文件里的内容就只输出了error日志。

实例代码4
现在error.2018-11-27.log里只输出了error日志,但是info.2018-11-27.log还是存在error日志,出现的原因还是因为配置的info日志,但是error日志级别高于info,所以也会被打印出来。那非要过滤掉error日志怎么做?
只需要更改logback-spring.xml文件内容:

   <appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <!--如果匹配,就禁止掉-->
            <onMatch>DENY</onMatch>
            <!--反之,则使用该规则-->
            <onMismatch>ACCEPT</onMismatch>
        </filter>
        <encoder>
            <pattern>
                %msg%n
            </pattern>
        </encoder>
        <!--滚动策略:每天创建一个文件-->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--路径-->
            <fileNamePattern>
                d:/log/taofut/info.%d.log
            </fileNamePattern>
        </rollingPolicy>
   </appender>