java 日志框架详解-干货

一、 日志的重要性

对于我们开发人员来说,日志记录往往不被重视。在生产环境中,日志是查找问题来源的重要依据。日志可记录程序运行时产生的错误信息、状态信息、调试信息和执行时间信息等多种多样的信息。可以在程序运行出现错误时,快速地定位潜在的问题源。目前常用的日志系统有java.util.logging、commons logging、slf4j、log4j1.x、logback、log4j2.x 等若干种。

二、 Java常用日志框架历史

 1996年早期,欧洲安全电子市场项目组决定编写它自己的程序跟踪API(Tracing API)。经过不断的完善,这个API终于成为一个十分受欢迎的Java日志软件包,即log4j。后来log4j成为Apache基金会项目中的一员。

 期间log4j近乎成了Java社区的日志标准。据说Apache基金会还曾经建议Sun引入log4j到java的标准库中,但Sun拒绝了。

 2002年Java1.4发布,Sun推出了自己的日志库jul(java util logging),其实现基本模仿了log4j的实现。在JUL出来以前,log4j就已经成为一项成熟的技术,使得log4j在选择上占据了一定的优势。

 接着,Apache推出了jakarta commons logging,jcl只是定义了一套日志接口(其内部也提供一个simple log的简单实现),支持运行时动态加载日志组件的实现,也就是说,在你的应用代码里,只需调用commons logging的接口,底层实现可以是log4j,也可以是java util logging。

 后来(2006年),Ceki Gülcü不适应Apache的工作方式,离开了Apache。然后先后创建了slf4j(日志门面接口,类似于commons logging)和logback(slf4j的实现)两个项目,并回瑞典创建了QOS公司,QOS官网上是这样描述logback的:The Generic,Reliable Fast&Flexible Logging Framework(一个通用,可靠,快速且灵活的日志框架)。

 现今,Java日志领域被划分为两大阵营:commons logging阵营和slf4j阵营。

 commons logging在Apache大树的笼罩下,有很大的用户基数。但有证据表明,形式正在发生变化。2013年底有人分析了GitHub上30000个项目,统计出了最流行的100个Libraries,可以看出slf4j的发展趋势更好。如下图1所示。

 

图1

image.png

Apache眼看有被logback反超的势头,于2012年7月重写了log4j 1.x,成立了新的项目log4j2.x。log4j2在各个方面都与logback非常相似。

Java的logger世界

Commons logging
Apache的commons项目,一个很薄的logging抽象层,制定了使用log的相关接口和规范,可以由不同的logging implementations,[
http://commons.apache.org/proper/commons-logging/]

SLF4J
Simple Logging Facade for Java, 也是一个logging抽象层,底层logging框架可以是(e.g. java.util.logging, logback, log4j),是Commons logging的替代物, [
http://www.slf4j.org/]

jcl-over-slf4j
提供Commons-logging向slf4j迁移用的bridge, 可参考 [
http://www.slf4j.org/legacy.html]

slf4j-log4j
前面说了,SLF4J是个facade,log4j是其实现的一种框架,抽象成接口,具体的绿叶可以是Log4j/Log4j2/LockBack,当使用log4j时,需要此Jar包作为桥接,可参考 [
http://www.slf4j.org/legacy.html]

log4j
这个不用多说,大家使用最多的是1.x版本, [
http://logging.apache.org/log4j/1.2/], 不过好像2.x也出来了,据说采用了异步机制,性能有很大提升 [http://logging.apache.org/log4j/2.x/]

logback
原生实现了SLF4J API,所以不需要中间的bridge,号称是log4j终结者,下一代logging框架,性能比log4j有很大提升,推荐使用,况且现在我们已经在使用SLF4j,所以切换过去应该是很方便的事。[
http://logback.qos.ch/]

总的来说:slf4j与commons-logging只是一个日志门面,实际还是要依赖真正的日志库log4j,虽然slf4j和commons-loggins自带了日志库,但是毕竟log4j才是最强大的。

 

至于Logback是由log4j创始人设计的另一个开源日志组件,是用来取代log4j,

取代的理由自行百度;

https://blog.csdn.net/zbajie001/article/details/79596109

jul日志

 这个是SUN公司自带的日志输出框架,本来Log4j有建议过加入SUN JDK框架,但是SUN不要人家,后台就出了这个框架,但是比较XX,所以很少人使用;

@Test
 public void test() throws IOException {
     Logger logger = Logger.getLogger("");
     logger.info("Hola JDK!");
 }

这就是jul日志。

commons-logging日志

common logging本身不是log,你可以把它看做是一个日志的接口而log4j就是日志的实现,它自身只是实现了简单的日志实现类.

使用很简单:

commons-logging的使用非常简单。首先,需要在pom.xml文件中添加依赖:

<dependency>
     <groupId>commons-logging</groupId>
     <artifactId>commons-logging</artifactId>
     <version>1.2</version>
 </dependency>

声明测试代码:

public class commons_loggingDemo {
    Log log= LogFactory.getLog(commons_loggingDemo.class);
    @Test
    public void test() throws IOException {
        log.debug("Debug info.");
        log.info("Info info");
        log.warn("Warn info");
        log.error("Error info");
        log.fatal("Fatal info");
    }
}

接下来,在classpath下定义配置文件:commons-logging.properties:

#指定日志对象:
 org.apache.commons.logging.Log = org.apache.commons.logging.impl.Jdk14Logger
 #指定日志工厂:
 org.apache.commons.logging.LogFactory = org.apache.commons.logging.impl.LogFactoryImpl

如果只单纯的依赖了commons-logging,那么默认使用的日志对象就是Jdk14Logger,默认使用的日志工厂就是LogFactoryImpl

commons-logging + Log4j使用:

去掉commons-logging.properties 配置文件:

因为commons-logging

1) 首先在classpath下寻找自己的配置文件commons-logging.properties,如果找到,则使用其中定义的Log实现类; 

2) 如果找不到commons-logging.properties文件,则在查找是否已定义系统环境变量org.apache.commons.logging.Log,找到则使用其定义的Log实现类; 

3) 否则,查看classpath中是否有Log4j的包,如果发现,则自动使用Log4j作为日志实现类; 

4) 否则,使用JDK自身的日志实现类(JDK1.4以后才有日志实现类); 
5)
否则,使用commons-logging自己提供的一个简单的日志实现类SimpleLog; 

需要在pom.xml文件中添加依赖:

<dependency>
    <
groupId>log4j</groupId>
    <
artifactId>log4j</artifactId>
    <
version>1.2.17</version>
</
dependency>

接下来,在classpath下定义配置文件:log4j.properties

# Logger root
 # \u6ce8\u610f\uff1a\u7ebf\u4e0a\u7cfb\u7edf\uff0c\u9700\u628aconsole\u5220\u9664
 log4j.rootLogger=INFO ,console
 
 log4j.logger.org.springframework=info,console
 
 # \u6253\u5370\u5230Console\u7684\u65e5\u5fd7\uff0c\u6ce8\u610f\uff1a\u7ebf\u4e0a\u7cfb\u7edf\u9700\u8981\u5c06\u8be5\u6bb5\u65e5\u5fd7\u914d\u7f6e\u5220\u9664
 #### First appender writes to console
 log4j.appender.console=org.apache.log4j.ConsoleAppender
 log4j.appender.console.layout=org.apache.log4j.PatternLayout
 log4j.appender.console.layout.ConversionPattern=%-4p,%t,%d{MM-dd HH:mm:ss.SSS},%c{2}.%M:%L - %m%n

Log4j日志框架

 log4j是Apache的一个开放源代码的项目,通过使用log4j,我们可以控制日志信息输送的目的地, 日志的输出格式, 日志信息的级别,可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码

   如果在我们系统中单独使用log4j的话,我们只需要引入log4j的核心包就可以了

   <dependency>
    <
groupId>log4j</groupId>
    <
artifactId>log4j</artifactId>
    <
version>1.2.17</version>
</
dependency>
public class Log4jTest {
            private static final org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(Log4jTest.class);
           
            public static void main(String[] args) {
                       logger.info("hello word");
            }
}

在系统的src目录下添加依赖的配置文件:

log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

Log4j 的代码结构:

image.png

  Log4j启动过程:

1.找到对应的配置文件,虽然log4j支持多种配置文件,在平时使用中多数使用log4j.xml格式,该文件位置应该放在classpath下面,在log4j启动时候会去clasPath下面找寻log4j.xml。
2.解析xml,根据配置生成对应名字的logger以及绑定该logger应用的appender。

log4j日志输出关键类图

 image.png 

logger组件:logger的父类category完成基本所有的log功能,其中实现的接口appenderAttachable用于存储与该logger绑定的appender。logger主要用于管理日志level,确定是否需要打出日志,即调用appender。其中每个category中都会有自己父亲的引用,当additivity参数为true的时候(默认为true),则会在自己appender输出信息后,调用父category去输出日志。

appender组件:appender是具体message输出的组件,管理信息输出的位置和格式。
Log4j获取特定名字logger

org.apache.log4j.Logger log4j = or.apache.log4h.Logger.getLogger(Name.class)

logger内部的存储是由Hierarchy来完成,在启动过程中会初始化所有log4j.xml中定义的logger。如果logger中不存在该名字的logger则会新生成一个新的logger,由于该名字的logger不存在配置文件中,所以会根据名字规则寻找其父亲logger,如果找不到则会以rootLogger为父亲。所以如果没有配置该类名对应的logger则会调用其父亲logger来输出日志。

Log4j 的详细配置:

#Access log

log4j.appender.A=org.apache.log4j.DailyRollingFileAppender

log4j.additivity.A = false 

log4j.appender.A.File=${catalina.base}/logs/access.log

log4j.appender.A.layout=org.apache.log4j.PatternLayout

log4j.appender.A.layout.ConversionPattern=%-4p,%t,%d{MM-dd   HH:mm:ss.SSS},%c{2}.%M:%L – %m%n

log4j.appender.A.DatePattern='_' yyyy-MM-dd

log4j.appender.A.append=true

log4j.appender.A.ImmediateFlush=true

log4j.appender.A.Threshold = INFO

https://blog.csdn.net/earthchinagl/article/details/70256527

 

高级配置:

https://www.cnblogs.com/dengjiahai/p/4608946.html

https://www.cnblogs.com/leefreeman/p/3610459.html

 

使用坑:

  在高并发时候会有性能问题:

http://zl378837964.iteye.com/blog/2373591

解决的方式:

1.规避:

1)排查代码是否写入了大量日志,删除非必要日志

 2)简化日志序列,尽量不出现日志嵌套(日志B打印调用了日志D)

2.解决:

1)使用log4j异步写AsyncAppender

2)升级到log4j 2 –> 建议

3)使用logback替换log4j –> 建议

4)补丁:使用可重入锁替换synchronized 

slf4j日志

 一个新的日志框架横空出世了:slf4j , 这个框架由log4j的作者开发并且后台出了一个性能更加好的框架logback;

 

 哪有人会问?为什么还需要slf4j了???

 

   https://www.oschina.net/translate/why-use-sl4j-over-log4j-for-logging

  slf4j是一个日志统一的框架,主要是为了接入不同日志系统建立的统一包装层的框架。其他具体实现日志的系统只需要实现slf4j的一些特定规则则可以接入slf4j使用。

 image.png

上图中可以看出,slf4j对外提供了统一的api,这由slf4j-api.jar包提供。另外如果需要接入log4j,则需要在api和具体框架中加入一个适配层,实现slf4j和log4j接口的适配,这个由包slf4j-log4j12实现。

有几个包需要区别一下:

log4j-slf4j-impl 是 slf4j 和 logj4 2 的 Binding,而 slf4j-log4j12 是 slf4j 和 log4j 1.2 的 Binding,jcl-over-slf4j 是common-logggin 适配 slf4j。

log4j-over-slf4j 是把Log4j适配到slf4j,比如有些系统使用logback+slef4j,有一个第三方jar依赖于log4j打印日志,就需要这个包了

log4j-slf4j-impl 是用于log4j2与slf4j 的桥接用的;

slf4j + Log4j 使用过程:

private org.slf4j.Logger log= LoggerFactory.getLogger(SlfloggingDemo.class);

@Test
public void test() throws IOException {
   
log.debug("Debug info.");
   
log.info("Info info");
   
log.warn("Warn info");
   
log.error("Error info");
}
<dependency>
    <
groupId>log4j</groupId>
    <
artifactId>log4j</artifactId>
    <
version>1.2.17</version>
</
dependency>

<
dependency>
    <
groupId>org.slf4j</groupId>
    <
artifactId>slf4j-log4j12</artifactId>
    <
version>1.7.21</version>
</
dependency>

slf4j 底层实现方式:

1.slf4j的loggerfactory在getlogger()的过程中会检查slf4j-LoggerFactory是否初始化没有,其中performInitialization调用会去适配具体的日志框架。(在slf4j-api层)

 image.png 

2.slf4j-LoggerFactory未初始化,则会调用当前classloader去寻找“org/slf4j/impl/StaticLoggerBinder.class”类,并创建该类。所以需要适配slf4j的日志框架都需要实现该类。(在slf4j-api层)

 image.png

3. 调用对应的staticLoggerBinder会初始化log4j。并生成新的log4j12-log4jloggerFactory。该类在返回logger的时候会将log4j返回的logger在装饰一层即log4jLoggerAdapter,用于适配slf4j的logger接口。(在slf4j-log4j12)

image.png

注意:当有多个日志框架的时候,在找寻“org/slf4j/impl/StaticLoggerBinder.class”会出现多个,这时候加载具体那个StaticLoggerBinder.class则会由jvm来决定,从而加载了对应的日志框架。如果不想出现这个问题,则应该保证classPath下只有一个该类。

有些系统你残留了一些另外的日志框架比如apache commons-logging ( 简称 jcl)的接口, 则当引入这个二方包的时候,由于原本自己并不支持jcl接口,或者想将jcl接口最后输入的日志系统为log4j。则需要引入jcl的桥接工具 jcl-over-slf4j,该包功能代码很少,核心为将jcl的接口调用适配成slf4j接口的调用。这样即可让jcl接口的二方库和自己共用一个日志框架。

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.7.13</version>
    <scope>runtime</scope>
</dependency>

总结:在使用日志系统中,通常在工作的系统上,主要即为log4j作为具体的日志系统的实现,然后将slf4j作为日志系统的抽象层,这样使得应用和具体的日志系统解耦。

LogBack日志

  LogBack和Log4j都是开源日记工具库,LogBack是Log4j的改良版本,比Log4j拥有更多的特性,同时也带来很大性能提升。LogBack官方建议配合Slf4j使用,这样可以灵活地替换底层日志框架。 为了优化log4j,以及更大性能的提升,Apache基金会已经着手开发了log4j 2.0

LogBack被分为3个组件,logback-core, logback-classic 和 logback-access。 
logback-core
:提供了LogBack的核心功能,是另外两个组件的基础。 
logback-classic
:实现了Slf4j的API,所以当想配合Slf4j使用时,需要将logback-classic加入classpath。 
logback-access
:是为了集成Servlet环境而准备的,可提供HTTP-access的日志接口。

需要在pom.xml文件中添加依赖:

<dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
  </dependency>
  <dependency>
        <groupId>org.logback-extensions</groupId>
        <artifactId>logback-ext-spring</artifactId>
        <version>0.1.2</version>
  </dependency>

接下来,在classpath下定义配置文件:logback.xml

<?xml version="1.0" encoding="UTF-8"?>
 
<!-- logback日志系统基础配置 -->
<!-- 1.此处debug="true"与logger level无关,只与配置的状态信息有关(如配置文件是否规范,某些标签元素属性是否赋值) -->
<!-- 3.此处scan="true"设置后,可以扫描本日志配置文件变动并重加载配置,可设置扫描间隔时间,默认为1分钟扫描一次,单位milliseconds, seconds, minutes 或 hours,如scanPeriod="30 seconds" -->
<!-- 4.此处packagingData="true"可以在日志后看到依赖jar包名和版本,很费性能,不建议开启 -->
 
<configuration debug="false" scan="true" scanPeriod="30 seconds" packagingData="false">
 
 
 
   
<!-- 设置 logger context 名称,一旦设置不可改变,默认为default -->
   
<contextName>sharding-jdbc-demo</contextName>
 
    <
property name="logDir" value="D:/eclipse-workspace/logs" />
 
 
   
<!--用于对控制台进行日志输出-->
   
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
       
<!-- encoders are by default assigned the type ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
       
<encoder>
            <
pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </
encoder>
    </
appender>
 
    <
appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <
file>${logDir}/info.log</file>
       
<!-- 过滤日志 -->
        <!-- 过滤掉非INFO级别 -->
       
<filter class="ch.qos.logback.classic.filter.LevelFilter">
            <
level>INFO</level>
            <
onMatch>ACCEPT</onMatch>  <!-- 如果命中就禁止这条日志 -->
           
<onMismatch>DENY</onMismatch> <!-- 如果没有命中就使用这条规则 -->
       
</filter>
 
        <
rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
           
<!-- rollover daily -->
           
<fileNamePattern>${logDir}/info-%d{yyyy-MM-dd_HH-mm}.%i.log</fileNamePattern>
            <
maxHistory>5</maxHistory>
            <
timeBasedFileNamingAndTriggeringPolicy
                   
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
               
<!-- or whenever the file size reaches 100MB -->
               
<maxFileSize>10MB</maxFileSize>
            </
timeBasedFileNamingAndTriggeringPolicy>
        </
rollingPolicy>
        <
encoder>
            <
charset>UTF-8</charset>
            <
pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n</pattern>
        </
encoder>
    </
appender> 
 
    <
appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <
file>${logDir}/error.log</file>
       
<!-- 过滤日志 -->
       
<filter class="ch.qos.logback.classic.filter.LevelFilter">
           
<!-- 过滤掉非IERROR级别 -->
           
<level>ERROR</level>
            <
onMatch>ACCEPT</onMatch>  <!-- 如果命中就禁止这条日志 -->
           
<onMismatch>DENY</onMismatch> <!-- 如果没有命中就使用这条规则 -->
       
</filter>
 
        <
rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
           
<!-- rollover daily -->
           
<fileNamePattern>${logDir}/error-%d{yyyy-MM-dd_HH-mm}.%i.log</fileNamePattern>
            <
maxHistory>5</maxHistory>
            <
timeBasedFileNamingAndTriggeringPolicy
                   
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
               
<!-- or whenever the file size reaches 100MB -->
               
<maxFileSize>10MB</maxFileSize>
            </
timeBasedFileNamingAndTriggeringPolicy>
        </
rollingPolicy>
        <
encoder>
            <
charset>UTF-8</charset>
            <
pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n</pattern>
        </
encoder>
    </
appender> 
 
 
    <
appender name="ASYNC_FILEINFO_LOG" class="ch.qos.logback.classic.AsyncAppender">
       
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
       
<discardingThreshold>0</discardingThreshold>
       
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
       
<queueSize>2048</queueSize>
       
<!-- 添加附加的appender,最多只能添加一个 -->
       
<appender-ref ref="fileInfoLog" />
    </
appender>
 
 
   
<!-- 日志级别若没显示定义,则继承最近的父logger(该logger需显示定义level,直到rootLogger)的日志级别-->
    <!-- logger的appender默认具有累加性(默认日志输出到当前logger的appender和所有祖先logger的appender中),可通过配置 “additivity”属性修改默认行为-->
   
<logger name="com.fulihui.sharding.jdbc" level="INFO" additivity="false">
        <
appender-ref ref="ASYNC_FILEINFO_LOG" />
    </
logger>
 
   
<!-- 至多只能配置一个root -->
   
<root level="INFO">
        <
appender-ref ref="STDOUT" />
       
<!--<appender-ref ref="fileInfoLog" />-->
       
<appender-ref ref="fileErrorLog" />
        <
appender-ref ref="ASYNC_FILEINFO_LOG" />
    </
root>
 
  </
configuration>

 

 

https://blog.csdn.net/zzzgd_666/article/details/80458444

 

logback 使用的坑:

 1.packagingData="false" 当此属性设置为true时,logback可以包含它输出的堆栈跟踪行的每一行的打包数据,很影响性能,建议线上不能开启

 2.配置中有个<discardingThreshold>0</discardingThreshold>这个很表示不丢日志,并且底层是通过

//class :  ch.qos.logback.core.AsyncAppenderBase 的 append方法
  BlockingQueue<E> blockingQueue;
  @Override
  protected void append(E eventObject) {
    if (isQueueBelowDiscardingThreshold() && isDiscardable(eventObject)) {
      return;
    }
    preprocess(eventObject);
    put(eventObject);
  }
  private void put(E eventObject) {
    try {
      blockingQueue.put(eventObject);
    } catch (InterruptedException e) {
    }
  }

从代码中可以看出来虽然是异步的,但是把日志塞进队列中用的是put方法.是会block的,直到队列空出位置来,所以在配置上可以把队列配置大一掉;建议还是升级采用异步方式

采用log4j2 所以就出现 log4j2 这个异步框架了。

Log4j 与 logback的性能对比:

https://my.oschina.net/OutOfMemory/blog/789267

Log4j2.0日志

  Log4j2.0基于LMAX Disruptor的异步日志在多线程环境下性能会远远优于Log4j 1.x和logback(官方数据是10倍以上)。我想日后logback 也会优化成异步模式的,具体看官方公告。。。。

 

  https://www.jianshu.com/p/570b406bddcd

 

  具体搭建:

  需要在pom.xml文件中添加依赖:

 

<!--log4j-2模式-->
 
<dependency>
    <
groupId>org.slf4j</groupId>
    <
artifactId>slf4j-api</artifactId>
    <
version>1.7.25</version>
  </
dependency>
 
 
<!--核心log4j2jar包-->
 
<dependency>
    <
groupId>org.apache.logging.log4j</groupId>
    <
artifactId>log4j-api</artifactId>
    <
version>2.11.1</version>
  </
dependency>
 
  <
dependency>
    <
groupId>org.apache.logging.log4j</groupId>
    <
artifactId>log4j-core</artifactId>
    <
version>2.11.1</version>
  </
dependency>
 
 
<!--用于与slf4j保持桥接-->
 
<dependency>
    <
groupId>org.apache.logging.log4j</groupId>
    <
artifactId>log4j-slf4j-impl</artifactId>
    <
version>2.11.1</version>
  </
dependency>
 
 
<!--需要使用log4j2的AsyncLogger需要包含disruptor-->
 
<dependency>
    <
groupId>com.lmax</groupId>
    <
artifactId>disruptor</artifactId>
    <
version>3.4.2</version>
  </
dependency>
 
 
<!--web工程需要包含log4j-web,非web工程不需要-->
 
<dependency>
    <
groupId>org.apache.logging.log4j</groupId>
    <
artifactId>log4j-web</artifactId>
    <
version>2.4.1</version>
    <
scope>runtime</scope>
  </
dependency>

 

 

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="ERROR">
    <Properties>
        <Property name="baseDir">D:\eclipse-workspace\logs</Property>
        <Property name="filename">D:\eclipse-workspace\logs/info.log</Property>
        <Property name="filenameError">D:\eclipse-workspace\logs/error.log</Property>
    </Properties>

    <Appenders>
        <Console name="STDOUT">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %l - %msg%n"/>
        </Console>

        <RollingFile name="RollingFile" fileName="${filename}"
                     filePattern="${baseDir}/${date:yyyy-MM}/info-%d{yyyy-MM-dd-HH-mm}.log.gz">
            <PatternLayout pattern="%d %-5level [%t]%l - %msg%n"/>
            <Policies>
                <SizeBasedTriggeringPolicy size="200 MB"/>
                <TimeBasedTriggeringPolicy interval="10" modulate="true"/>
            </Policies>

            <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="ACCEPT"/>

            <!--自动删除超过120天的日志压缩文件-->
            <DefaultRolloverStrategy>
                <Delete basePath="${baseDir}" maxDepth="2">
                    <IfFileName glob="*/info-*.log.gz"/>
                    <IfLastModified age="20d"/>
                </Delete>
            </DefaultRolloverStrategy>

        </RollingFile>

        <!--错误日志入文件-->
        <RollingFile name="RollingFileError" fileName="${filenameError}"
                     filePattern="${baseDir}/${date:yyyy-MM}/error-%d{yyyy-MM-dd-HH}.log">
            <PatternLayout pattern="%d %-5level [%t]%l - %msg%n"/>
            <Policies>
                <SizeBasedTriggeringPolicy size="200 MB"/>
                <TimeBasedTriggeringPolicy interval="24" modulate="true"/>
            </Policies>

            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>

            <!--自动删除超过120天的日志压缩文件-->
            <DefaultRolloverStrategy>
                <Delete basePath="${baseDir}" maxDepth="2">
                    <IfFileName glob="*/error-*.log"/>
                    <IfLastModified age="30d"/>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>


    </Appenders>

    <Loggers>

        <!--采用异步输出日志-->
        <AsyncLogger name="com.fulihui.sharding.jdbc" level="debug"   additivity="false">
            <!--写入info级别-->
            <AppenderRef ref="RollingFile" />
            <!--写入error级别-->
            <AppenderRef ref="RollingFileError" level="error"/>

            <AppenderRef ref="STDOUT"/>

        </AsyncLogger>

        <!--采用异步输出日志-->
        <AsyncRoot level="debug">
            <AppenderRef ref="STDOUT"/>
        </AsyncRoot>

    </Loggers>
</Configuration>


配置详情:

    https://blog.csdn.net/u013269532/article/details/53186526

https://blog.csdn.net/scherrer/article/details/73744392


logback log4j log4j2 性能实测

 https://blog.souche.com/logback-log4j-log4j2shi-ce/