JAVA的日志体系

一.前言

目前的日志框架有 jdk 自带的 logging,log4j1、log4j2、logback ,这些框架都自己定制了日志 API ,并且有相应的实现;目前用于实现日志统一的框架 Apache commons-logging、slf4j ,遵循「面向接口编程」的原则,这两大框架可以让用户在程序运行期间去选择具体的日志实现系统(log4j1\log4j2\logback等)来记录日志,是统一抽象出来的一些接口。
这些日志系统涉及到的繁杂的各种集成 jar 包,如下:

  1. log4j、log4j-api、log4j-core
  2. log4j-1.2-api、log4j-jcl、log4j-slf4j-impl、log4j-jul
  3. logback-core、logback-classic、logback-access
  4. commons-logging
  5. slf4j-api、slf4j-log4j12、slf4j-simple、jcl-over-slf4j、slf4j-jdk14、log4j-over-slf4j、slf4j-jcl

但是日志系统多了,也不是好事!因为在java EE的项目中,我们会引入很多的第三方包,比如Spring、Mybatis、Httpclient等等。。。每个第三方的包都会有自己的日志系统,问题就来了,如果日志系统不兼容甚至产生冲突,灾难就产生了?或者是不同的日志系统打印日志的规则不同,接口也不同,那使用方就要做各种适配。但是合适的使用搭配日志框架,这些问题将迎刃而解!
这里写图片描述

日志门面接口提供了一套独立于具体日志框架实现的API,应用程序通过使用这些独立的API就能够实现与具体日志框架的解耦,这跟JDBC是类似的。最早的日志门面接口是commons-logging,但目前最受欢迎的是slf4j。
日志门面接口本身通常并没有实际的日志输出能力,它底层还是需要去调用具体的日志框架API的,也就是实际上它需要跟具体的日志框架结合使用。由于具体日志框架比较多,而且互相也大都不兼容,日志门面接口要想实现与任意日志框架结合可能需要对应的桥接器,就好像JDBC与各种不同的数据库之间的结合需要对应的JDBC驱动一样。

二.日志框架

一.slf4j(统一的接口)

SLF4J提供了统一的记录日志的接口,对不同日志系统的具体实现进行了抽象化,只要按照其提供的方法记录即可,最终日志的格式、记录级别、输出方式等通过绑定具体的日志系统来实现。
1. maven包

     <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-api</artifactId>
           <version>1.7.2</version>
     </dependency>

2.示例
SLF4J支持{}作为占位符,等价于C语言中的%s,而不必再进行字符串的拼接,效率有显著的提升。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
public class slf4j {
        public static void main(String[] args) {
            Logger logger = LoggerFactory.getLogger(slf4j.class);
            logger.info("hello world:",new Date());
    }
}

它关心日志是通过哪个日志系统,以什么风格输出的。因为它们取决于部署项目时绑定的日志系统。
例如,在项目中使用了SLF4J记录日志,并且绑定了log4j,则日志会以log4j的风格输出;后期需要改为以logback的风格输出日志,只需要将log4j替换成logback即可,不用修改项目中的代码。

二.commons-logging

common-logging是apache提供的一个通用的日志接口。用户可以自由选择第三方的日志组件作为具体实现,像log4j,或者jdk自带的logging, common-logging会通过动态查找的机制,在程序运行时自动找出真正使用的日志库。当然,common-logging内部有一个Simple logger的简单实现,但是功能很弱。所以使用common-logging,通常都是配合着log4j来使用。使用它的好处就是,代码依赖是common-logging而非log4j, 避免了和具体的日志方案直接耦合,在有必要时,可以更改日志实现的第三方库。
官方网站:http://commons.apache.org/proper/commons-logging/

三.slf4j 与 common-logging 比较

common-logging通过动态查找的机制,在程序运行时自动找出真正使用的日志库。由于它使用了ClassLoader寻找和载入底层的日志库, 导致了象OSGI这样的框架无法正常工作,因为OSGI的不同的插件使用自己的ClassLoader。 OSGI的这种机制保证了插件互相独立,然而却使Apache Common-Logging无法工作。

slf4j在编译时静态绑定真正的Log库,因此可以在OSGI中使用。另外,SLF4J 支持参数化的log字符串,避免了之前为了减少字符串拼接的性能损耗而不得不写的if(logger.isDebugEnable()),现在你可以直接写:logger.debug(“current user is: {}”, user)。拼装消息被推迟到了它能够确定是不是要显示这条消息的时候,但是获取参数的代价并没有幸免。

三.日志系统

一.slf4j+ log4j

1.导入poml包

         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
             <version>1.7.2</version>
         </dependency>
         <dependency>
             <groupId>log4j</groupId>
             <artifactId>log4j</artifactId>
             <version>1.2.17</version>
         </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-log4j12</artifactId>
             <version>1.8.0-alpha2</version>
         </dependency>

2.log4j.properties 配置文件

# rootLogger参数分别为:根Logger级别,输出器stdout,输出器log
log4j.rootLogger = info,stdout,log
# 输出信息到控制台
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %d [%-5p] %l %rms: %m%n
# 输出DEBUG级别以上的日志到D://logs/debug.log
log4j.appender.log = org.apache.log4j.DailyRollingFileAppender
log4j.appender.log.DatePattern = '.'yyyy-MM-dd
log4j.appender.log.File = D://debug.log
log4j.appender.log.Encoding = UTF-8
#log4j.appender.log.Threshold = INFO
log4j.appender.log.layout = org.apache.log4j.PatternLayout
log4j.appender.log.layout.ConversionPattern = %d [%-5p] (%c.%t): %m%n

3.测试类

public class slf4j {
        public static void main(String[] args) {
            Logger logger = LoggerFactory.getLogger(slf4j.class);
            logger.info("hello world");
            logger.debug("hello world word");
            logger.error("hello world word  hello");
    }
}

结果:

2018-08-27 21:36:25,164 [INFO ] logs.slf4j.main(slf4j.java:14) 0ms: hello world
2018-08-27 21:36:25,168 [ERROR] logs.slf4j.main(slf4j.java:16) 4ms: hello world word  hello

通常输出日志开销非常大,从上述结果可见,SLF4J通过{}作为占位符的方式输出字符串,相比字符串拼接的方式,效率有显著的提升。

二.slf4j+jdk14

如果想把slf4j+log4j的组合方式改为使用java自带logging记录日志,我们需要做的仅仅是将pom.xml的依赖项slf4j-log4j12改为slf4j-jdk14即可,无需对上述测试代码做任何修改。
1.pom包的导入包如下:

          <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
             <version>1.7.2</version>
         </dependency>
         <dependency>
             <groupId>log4j</groupId>
             <artifactId>log4j</artifactId>
             <version>1.2.17</version>
         </dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-jdk14</artifactId>
             <version>1.8.0-alpha2</version>
         </dependency>  

2.测试运行结果

八月 27, 2018 9:59:43 下午 logs.slf4j main
信息: hello world
八月 27, 2018 9:59:43 下午 logs.slf4j main
严重: hello world word  hello

此时日志已经变为以logging的方式输出。

三.slfj+logback

logback:http://logback.qos.ch/download.html
Logback 分为三个模块:logback-core(核心),logback-classic,logback-access
logback-classic 改善了 log4j,且自身实现了 slf4j的API,所以即使用 Logback 你仍然可以使用其他的日志实现,如原始的 Log4J,java.util.logging 等;
logback-access 让你方便的访问日志信息,如通过 http 的方式。

1.pom包文件添加

        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
             <version>1.7.2</version>
         </dependency>
         <dependency>
             <groupId>ch.qos.logback</groupId>
             <artifactId>logback-classic</artifactId>
             <version>1.1.6</version>
         </dependency>

2.logback.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <jmxConfigurator />
    <!-- 控制台输出日志 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </layout>
    </appender>
    <!-- 文件输出日志 (文件大小策略进行文件输出,超过指定大小对文件备份)-->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>xxxx.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
            <FileNamePattern>xxxx.log.%i.bak</FileNamePattern>
            <MinIndex>1</MinIndex>
            <MaxIndex>12</MaxIndex>
        </rollingPolicy>
        <triggeringPolicy            class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>100MB</MaxFileSize>
        </triggeringPolicy>
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
        </layout>
    </appender>
    <!--这里指定logger name 是为jmx设置日志级别做铺垫 -->
    <logger name="com.xxx.xxx">
        <level value="INFO" />
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
    </logger>
</configuration>

3.测试类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class logback {
    private static Logger logger = LoggerFactory.getLogger(logback.class);
    public static void main(String[] args) {
        //级别为debug的日志
        logger.debug("Hello  world--- debug");
        //级别为info的日志
        logger.info("Hello world--- info");
        //级别为warn的日志
        logger.warn("Hello world--- warn");
        //级别为error的日志
        logger.error("Hello world--- error");
    }
}

按照定义的输出格式输出结果:

2018-08-27 22:21:49.469|WARN|logs.logback|main|main|20|Hello world--- warn
2018-08-27 22:21:49.475|ERROR|logs.logback|main|main|22|Hello world--- error
2018-08-27 22:24:18.347|WARN|logs.logback|main|main|20|Hello world--- warn
2018-08-27 22:24:18.353|ERROR|logs.logback|main|main|22|Hello world--- error

猜你喜欢

转载自blog.csdn.net/u010520146/article/details/82120145