java 日志框架总结

日志级别

ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF

  • ALL:最低等级的,用于打开所有日志记录。
  • TRACE: designates finer-grained informational events than the DEBUG.Since:1.2.12,很低的日志级别,一般不会使用。
  • DEBUG: 指出细粒度信息事件对调试应用程序是非常有帮助的,主要用于开发过程中打印一些运行信息。
  • INFO: 消息在粗粒度级别上突出强调应用程序的运行过程。打印一些你感兴趣的或者重要的信息,这个可以用于生产环境中输出程序运行的一些重要信息,但是不能滥用,避免打印     过多的日志。
  • WARN: 表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给程序员的一些提示。
  • ERROR: 指出虽然发生错误事件,但仍然不影响系统的继续运行。打印错误和异常信息,如果不想输出太多的日志,可以使用这个级别。
  • FATAL: 指出每个严重的错误事件将会导致应用程序的退出。这个级别比较高了。重大错误,这种级别你可以直接停止程序了。
  • OFF: 最高等级的,用于关闭所有日志记录。

常见日志框架之间的关系

log4j,logback,log4j2

log4j的作者(Ceki)写了 log4j 把它捐献给apache基金会,然后他写了支持异步的logback

log4j2发布以后,log4停止维护。

目目前一般认为log4j2效率>logback

slf4j也是Ceki写的

slf4j和Conmon Logging

slf4j 和Conmon Logging 都是日志通用接口层,可以理解成是具体日志的适配器,虽然叫做日志门面感觉和外观模式有关,但是我觉得这里使用的适配器模式,可以理解成具体日志的通用接口。它们必须和具体的日志实现搭配使用,不能独立存在。

spring boot 使用log4j2

spring 默认使用的logback,如果要使用log4j需要排除logback,然后导入log4j

排除logback

        <!-- spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.5.5</version>
            <exclusions>

                <!--排查logback -->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-classic</artifactId>
                </exclusion>

            </exclusions>
        </dependency>

导入log4j2,一般我们还会配合slf4j使用

        <!-- log4j2 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
            <version>2.5.5</version>
        </dependency>

spring可以通过修改application.yml,指定日志配置文件的位置

#日志配置
logging:
  config: classpath:log4j2.xml   #日志配置文件位置
  level:
    com.lomi.mapper.GoodsMapper: info   #部分包下面的日志级别
    org.mybatis.spring: info
    com.zaxxer.hikari: info
    org.redisson: info

log4j2.xml 配置示例

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

<!--
    status : 这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,会看到log4j2内部各种详细输出
    monitorInterval : Log4j能够自动检测修改配置文件和重新配置本身, 设置间隔秒数。此处表示每隔600秒重读一次配置文件
-->
<Configuration status="OFF" monitorInterval="600" >

    <!--日志级别:TRACE < DEBUG < INFO < WARN < ERROR < FATAL-->
    <!--如果设置为WARN,则低于WARN的信息都不会输出-->
    <Properties>
        <!-- 配置日志文件输出目录,此处为项目根目录下的logs文件夹 -->
        <Property name="log_home">/log4j2</Property>
        <property name="file_name">TestExample</property>
        <property name="console_pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{requestId}] [%t]  %-5level %logger{36} - %msg %n</property>
        <property name="debug_pattern">%d{yyyy-MM-dd 'at' HH:mm:ss z} C:%X{MDC.REQUSETIDKEY} %-5level %class{36} %L %M - %style%msg{red}!  %xEx%n</property>
        <property name="info_pattern">%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n</property>
        <property name="error_pattern">%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n</property>
        <property name="error_pattern">%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n</property>
    </Properties>

    <Appenders>
        <!--这个输出控制台的配置-->
        <Console name="console" target="SYSTEM_OUT">
            <!--控制台只输出level及其以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
            <ThresholdFilter level="TRACE" onMatch="ACCEPT" onMismatch="DENY"/>
            <!--日志输出的格式-->
            <!--
                %d{yyyy-MM-dd HH:mm:ss, SSS} : 日志生产时间,输出到毫秒的时间
                %-5level : 输出日志级别,-5表示左对齐并且固定输出5个字符,如果不足在右边补0
                %c : logger的名称(%logger)
                %t : 输出当前线程名称
                %p : 日志输出格式
                %m : 日志内容,即 logger.info("message")
                %n : 换行符
                %C : Java类名(%F)appender
                %L : 行号
                %M : 方法名
                %l : 输出语句所在的行数, 包括类名、方法名、文件名、行数
                hostName : 本地机器名
                hostAddress : 本地ip地址
             -->
            <PatternLayout pattern="${console_pattern}"/>
        </Console>


        <!--debug以上的都打印-->
        <!--
            循环日志文件:日志文件大于阀值的时候,就开始写一个新的日志文件
            这个会打印出所有的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档

            fileName    : 指定当前日志文件的位置和文件名称
            filePattern : 指定当发生Rolling时,文件的转移和重命名规则
            SizeBasedTriggeringPolicy : 指定当文件体积大于size指定的值时,触发Rolling
            DefaultRolloverStrategy : 指定最多保存的文件个数
            TimeBasedTriggeringPolicy : 这个配置需要和filePattern结合使用
                注意filePattern中配置的文件重命名规则是${file_name}_%d{yyyy-MM-dd}_%i,最小的时间粒度是dd,即天,
                TimeBasedTriggeringPolicy指定的size是1,结合起来就是每1天生成一个新文件
        -->
        <RollingRandomAccessFile name="debugFileAppender" fileName="${log_home}/${file_name}-debug.log"
                                 filePattern="${log_home}/debug/${file_name}-debug-%d{yyyy-MM-dd HH}-%i.log">
            <Filters>
                <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <PatternLayout pattern="${debug_pattern}"/>
            <Policies>
                <!--文件日期滚动,间隔时间(filePattern里面的最小日期单元的倍数),modulate是true的时候使用的系统时间边界,false的时候使用的好像是启动时间边界-->
                <TimeBasedTriggeringPolicy interval="2" modulate="false"/>

                <!--单个文件的大小-->
                <SizeBasedTriggeringPolicy size="10MB"/>
            </Policies>

            <!--max 20 表示最大生成20个滚动文件,超过的会保留最新的,编号会重名-->
            <DefaultRolloverStrategy max="20"/>
        </RollingRandomAccessFile>

        <!--info以上的都打印-->
        <!--filePattern .gz结尾会压缩文件-->
        <RollingRandomAccessFile name="infoFileAppender" fileName="${log_home}/${file_name}-info.log"
                                 filePattern="${log_home}/info/${file_name}-info-%d{yyyy-MM-dd}-%i.log.gz">
            <Filters>
                <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <PatternLayout pattern="${info_pattern}"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            </Policies>
            <DefaultRolloverStrategy max="40"/>
        </RollingRandomAccessFile>

        <!--error以上的都打印-->
        <RollingRandomAccessFile name="errorFileAppender" fileName="${log_home}/${file_name}-error.log"
                                 filePattern="${log_home}/error/${file_name}-error-%d{yyyy-MM-dd}-%i.log.gz">
            <Filters>
                <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>
            <PatternLayout pattern="${error_pattern}"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            </Policies>
            <DefaultRolloverStrategy max="20"/>
        </RollingRandomAccessFile>

        <!--只打印指定级别(warn)-->
        <!--正常不会只打印一个级别,都是关心这个级别或者以上级别的日志,这里只是演示可以这么用-->
        <RollingRandomAccessFile name="warnOnly" fileName="${log_home}/${file_name}-warnonly.log"
                                 filePattern="${log_home}/warnonly/${file_name}-warnonly-%d{yyyy-MM-dd}-%i.log.gz">
            <!--上下限制只取warn级别-->
            <Filters>
                <ThresholdFilter level="warn" onMatch="NEUTRAL" onMismatch="DENY"/>
                <ThresholdFilter level="error" onMatch="DENY" onMismatch="ACCEPT"/>
            </Filters>
            <PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
            <Policies>
                <TimeBasedTriggeringPolicy interval="1"/>
                <SizeBasedTriggeringPolicy size="10MB"/>
            </Policies>
            <DefaultRolloverStrategy max="20"/>
        </RollingRandomAccessFile>



        <!--配置异步写日志-->
       <!-- <Async name="Async">
            <AppenderRef ref="ALL"/>
        </Async>-->

        <!--输出到MongoDB中-->
        <!--<NoSql name="databaseAppender">
            <MongoDb databaseName="test" collectionName="errorlog" server="localhost" port="27017"/>
        </NoSql>-->
    </Appenders>

    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <Loggers>

        <!-- 这儿为trace表示什么都可以打印出来了,其他几个级别分别为:TRACE、DEBUG、INFO、WARN、ERROR和FATAL -->
        <Root level="INFO">
            <appender-ref ref="console"/>
            <appender-ref ref="infoFileAppender"/>
            <appender-ref ref="errorFileAppender"/>
        </Root>

        <!--additivity:是否继承父的appender,默认是true继承-->
        <logger name="org.springframework" level="INFO" additivity="false">
            <appender-ref ref="console"/>
            <appender-ref ref="infoFileAppender"/>
            <appender-ref ref="errorFileAppender"/>
        </logger>

        <logger name="org.mybatis" level="debug">
            <appender-ref ref="debugFileAppender"/>
        </logger>
        <Logger name="com.lomi" level="debug">
            <appender-ref ref="debugFileAppender"/>
            <!--正常不会只打印一个级别,都是关心这个级别或者以上级别的日志-->
            <appender-ref ref="warnOnly"/>
        </Logger>


        <!--java.lang.NoClassDefFoundError: com/lmax/disruptor/EventHandler-->
        <!--<AsyncLogger  name="com.lomi" level="info" additivity="false">
            <AppenderRef ref="Console"/>
        </AsyncLogger >-->

        <!--输出到NoSQL中-->
       <!-- <Logger name="mongoLog" level="trace" additivity="false">
            <AppenderRef ref="databaseAppender"/>
        </Logger>-->
    </Loggers>


</Configuration>

log4j2配置说明

  • Configuration是最外层标签

    • status:用于控制log4j自身的日志级别
    • monitorInterval: 含义是每隔多少秒重新读取配置文件,可以不重启应用的情况下修改配置
  • Properties: 使用来定义常量,以便在其他配置的时候引用,该配置是可选的,例如定义日志的存放位置

  • Loggers: 定义那些包下面的日志应该怎么输出

    • Logger:定义指定包下面的日志应该交给那些Appender输入
      • level:大于等于该日志级别的才向下传递
      • additivity: 若是additivity设为false,则子Logger只会在自己的appender里输出,而不会在父Logger的appender里输出。
      • name: 给 logger去一个名字,一般就是包名倒写(包含当前包和子包,可以精确到类)
      • AppenderRef: 指定由那个Appender输入(一个日志会经过logger的level和Appender的filter两次过滤才会有机会输出)
      • logger的父子关系: 按照包名的父子关系匹配的父子关系com.xxx的父logger就是com,root是最根上的logger
    • Root:如果没有在Logger里面指定的包默认使用那些Appender输入
  • Appenders

    • Appender的类型

      • Console 输出到控制台
      • File 输入到文件
      • RollingRandomAccessFile 输入到滚动文件
        • fileName: 初始输入的文件名字,或者说当前日志文件名字,触发滚动以后,把文件复制到归档文件,并且重新开始写入一个新的文件
        • filePattern: 文件滚动的时候(跨天,或者文件大小达到限制),新文件的名字模板,如果文件.gz结尾,会压缩历史日志
        • Policies:滚动策略
          • DefaultRolloverStrategy :文件滚动策略(保留多少个滚动文件),如果文件超过限制,会所有文件编号都-1,然后删除掉编号最小的那个,在新建一个编号位max的文件中写入数据
          • TimeBasedTriggeringPolicy: 触发文件滚动是基本时间周期数
            • 基本时间周期来自于filePattern里面配置的时间,filePattern里面的%d{yyyy-MM-dd}除以TimeBasedTriggeringPolicy的值就是%i
          • SizeBasedTriggeringPolicy: 指定触发文件滚动的文件大小
      • NoSql 输入到MongoDB
      • Flume 输出到Apache Flume
      • Async 指定那些Appender需要异步输出,也可以在 logger处使用 AsyncLogger 指定。
    • Appender的通用属性

      • name: 个appender取一个名字

      • ThresholdFilter: 可以使用 ThresholdFilter直接过滤,也可以使用 Filters 里面在套用 ThresholdFilter 这样的过滤器链过滤

        • level:指定日志级别(大于等于当前的基本就是匹配到的)
        • onMatch:匹配到的值级别应该怎么处理
          • ACCEPT: 直接输入
          • DENY:不输出
          • NEUTRAL:传递给下一个过滤器
        • onMismatch:没有匹配到的应该怎么处理
          • ACCEPT: 直接输入
          • DENY:不输出
          • NEUTRAL:传递给下一个过滤器
      • MarkerFilter:和ThresholdFilter类似,区别在于他是按照标记匹配的,ThresholdFilter是按照日志界别匹配的

        #marker是 Flow的 日志才会匹配
        <MarkerFilter marker="FLOW" onMatch="DENY" onMismatch="ACCEPT" />
        

        输出FlOW标记的日志

        
        Marker marker = MarkerManager.getMarker("FLOW");
        
        logger.debug(marker,"debug level");
        
        
      • PatternLayout:指定输入格式(PatternLayout 继承自Layout,实现了按照正则来输入日志格式)

        模式转换字符

        转换字符 含义
        c LoggerFactory.getLogger( "c.b.a" ) 后面指定的部分,传入class的时候默认是包名+类名
        也是Logger的name
        C logger对象的全类名,和LoggerFactory.getLogger( "c.b.a" )指定的name没有关系
        传入class的时候和小写一样
        d 使用它输出记录日志的日期,比如 %d{HH:mm:ss,SSS} 或 %d{dd MMM yyyy HH:mm:ss,SSS}。
        F 在记录日志时,使用它输出java文件名。
        输出:LoggerTest.java
        l 用它输出生成日志的调用者的位置信息,location的缩写。
        输出:com.lomi.logger.LoggerTest.t1(LoggerTest.java:32)
        L 使用它输出发起日志请求的行号。
        m 我们在程序里面控制输入的日志信息,message的缩写。
        M 使用它输出发起日志请求的方法名。
        n 输出平台相关的换行符。
        p 输出日志事件的优先级。和level输入的一样
        %-5level:左对齐的5位日志级别,缺位补空格,[%5level]:输出右对齐的5位日志级别,缺位补空格
        r 使用它输出从构建布局到生成日志事件所花费的时间,以毫秒为单位。
        t 输出生成日志事件的线程名。
        x 输出和生成日志事件线程相关的 NDC (嵌套诊断上下文)。
        X 该字符后跟 MDC 键,比如 X{clientIP} 会输出保存在 MDC 中键 clientIP 对应的值。
        xEx x +Ex 的缩写打印异常
        % 百分号, %% 会输出一个 %。

        格式修饰符

        基本格式:

        %最小值.最大值 变量: 表示最小保留多少位,最大保留多少位。

        省略 .最大致: 最大值不限制

        省略 最小值: 最最小值默认0,省略最小值的时候英文句号不能省略,才能区分是最大值还是最小值

        默认都是右对齐然,左边加0或裁剪。可以分别使用-号表示对齐对齐方式反转

        格式修饰符 删补对齐方向 最小宽度 最大宽度 注释
        %20c 20 少于 20 个字符,左边使用空格补齐,大于20个全量显示
        %-20c 20 少于 20 个字符,右边使用空格补齐,大于20个全量显示
        %.30c 30 如果列名长于 30 个字符,超过部分删除,优先删除左边的。
        %-.30c: 表示优先删除右边
        %20.30c 右(补),右(删) 20 30 如果列名少于 20 个字符,左边使用空格补齐,如果列名长于 30 个字符,从左边开始删除。
        %-20.-30c 左(补),左(删) 20 30 如果列名少于 20 个字符,右边使用空格补齐,如果列名长于 30 个字符,右边开始删除。

        异步日志:

        log4j2中,AsyncAppender采用了ArrayBlockingQueue来保存需要异步输出的日志事件;AsyncLogger则使用了Disruptor框架来实现高吞吐。

        AsyncAppender的常用参数

        参数名 类型 说明
        name String Async Appender的名字。
        AppenderRef String 异步调用的Appender的名字,可以配置多个。
        blocking boolean 默认为true。如果为true,appender将一直等待直到queue中有空闲;如果为false,当队列满的时候,日志事件将被丢弃。(如果配置了error appender,要丢弃的日志事件将由error appender处理)
        bufferSize integer 队列中可存储的日志事件的最大数量,默认为128。(源码中为128,Log4j2官网为1024,官网信息有误)

        日志颜色

        %style{内容表达式}{颜色}
        
        #比如
        %style{%msg}{red}
        
        spring:
            output:
                ansi:
                    enabled: always
        

        修改jvm参数 -Dlog4j.skipJansi=false
        用处不大,设置麻烦,直接使用idea插件 grep console 方便的

spring boot 使用logback

spring boot默认使用的就是 logback
下面是一个logback文件配置文件的例子

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


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

    <!--可以通过%contextName来打印日志上下文名称-->
    <contextName>logback</contextName>

    <!--属性,key-value格式  使用 ${key}引用-->
    <property name="log_home" value="/logback"/>
    <property name="file_name" value="TestExample"/>
    <property name="console_pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%logger{50}] - %msg%n"/>
    <property name="file_pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level [%logger{50}] - %msg%n"/>
    <property name="encode" value="UTF-8"/>


    <!--控制台的appender-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <Pattern>${console_pattern}</Pattern>
            <!-- 设置字符集 -->
            <charset>${encode}</charset>
        </encoder>

    </appender>

    <!-- 滚动文件只记录debug级别 -->
    <appender name="debugFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--日志文件路径-->
        <file>${log_home}/${file_name}-debug.log</file>

        <!--日志文件输出格式和编码-->
        <encoder>
            <pattern>${file_pattern}</pattern>
            <charset>${encode}</charset>
        </encoder>

        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 每天日志归档路径以及格式 -->
            <fileNamePattern>${log_home}/debug/${file_name}-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!--每个日志文件最大10MB-->
            <maxFileSize>10MB</maxFileSize>
            <!--日志量最大20GB-->
            <totalSizeCap>20GB</totalSizeCap>
            <!--最大归档文件个数-->
            <maxHistory>60</maxHistory>
        </rollingPolicy>

        <!-- 此日志文件只记录debug级别的 -->
        <!--LevelFilter 匹配指定类型-->
        <!-- ACCEPT 打印-->
        <!--DENY 不打印 -->
        <!-- NEUTRAL 看下面的过滤器要不要打印 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>debug</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>


    <!-- 滚动文件 记录info级别以上日志 -->
    <appender name="infoFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--日志文件路径-->
        <file>${log_home}/${file_name}-info.log</file>

        <!--日志文件输出格式和编码-->
        <encoder>
            <pattern>${file_pattern}</pattern>
            <charset>${encode}</charset>
        </encoder>

        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!-- 每天日志归档路径以及格式 -->
            <fileNamePattern>${log_home}/info/${file_name}-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!--每个日志文件最大10MB-->
            <maxFileSize>10MB</maxFileSize>
            <!--日志量最大20GB-->
            <totalSizeCap>20GB</totalSizeCap>
            <!--最大归档文件个数-->
            <maxHistory>60</maxHistory>
        </rollingPolicy>

        <!-- 此日志文件只记录debug级别的 -->
        <!--ThresholdFilter 匹配指定类型,是确定的类型,不能高于指定界别 -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>info</level>
        </filter>
    </appender>


    <!-- 滚动文件 记录error以上的级别的日志 -->
    <appender name="errorFileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log_home}/${file_name}-error.log</file>
        <encoder>
            <pattern>${file_pattern}</pattern>
            <charset>${encode}</charset>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${log_home}/error/${file_name}-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!--每个日志文件最大10MB-->
            <maxFileSize>10MB</maxFileSize>
            <!--日志量最大20GB-->
            <totalSizeCap>20GB</totalSizeCap>
            <!--最大归档文件个数-->
            <maxHistory>60</maxHistory>
        </rollingPolicy>

        <!--ThresholdFilter 指定级别一下直接不打印 -->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <!--WARN以上的级别向下决定是否打印,一下的级别直接拒绝-->
            <level>ERROR</level>
        </filter>
    </appender>



    <!--全局info,我们关心的包下面开启debug-->
    <!-- root 是所有 logger 的父亲 -->
    <root level="INFO">
        <appender-ref ref="console"/>
        <appender-ref ref="infoFileAppender"/>
        <appender-ref ref="errorFileAppender"/>
        <!--root 和 logger 里面不允许 filter,只能写在 appender里面-->
    </root>

    <!--additivity:是否继承父的appender,默认是true继承-->
    <logger name="org.springframework" level="INFO" additivity="false">
        <appender-ref ref="console"/>
        <appender-ref ref="infoFileAppender"/>
        <appender-ref ref="errorFileAppender"/>
    </logger>
    <logger name="org.mybatis" level="debug">
        <appender-ref ref="debugFileAppender"/>
    </logger>
    <Logger name="com.lomi" level="debug">
        <appender-ref ref="debugFileAppender"/>
    </Logger>


</configuration>

logback和log2的用法大同小异,配置文件的区别在于 log4j2 标签后面 中间分别是 Properties,Appenders,Loggers,里面才是 Propertie,Appender,Logger标签。loggerback 少了一个中间层,直接写 Propertie,Appender,Logger,别的用法都差不多,标签的属性很多类似,功能接近。

关于日志的几个注意点

  • spring-boot.jar里面有关于logback和log4j2等的一些配置可以include
    image-20230606170641325

  • log4j2的滚动日志文件如果filePattern的日期精度在小时以下就不生效了。

  • logger.isDebugEnabled() 之类的方法只是输入的 logger的level是否允许向下过滤和 append是否会输入没关系

  • logger.isDebugEnabled()之类的方法的作用,看下面的代码

        @Test
        public void loggerPrint1(){
            //一个耗时的操作
            Function<Long,String> getMsg = (i)->{
                try {
                    Thread.sleep(i);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                return "返回的json结果";
            };
    
            //当trace不可用的时候依旧执行了getMsg方法(耗时1003毫秒)
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            logger.trace("打印结果1{}",getMsg.apply(1000*1L));
            stopWatch.stop();
            System.out.println( stopWatch.getLastTaskTimeMillis() );
    
            //当trace不可用的时候不会getMsg方法,可以节省资源(耗时1毫秒)
            stopWatch.start();
            if( logger.isTraceEnabled() ){
                logger.trace("打印结果2{}",getMsg.apply(1000*1L));
            }
            stopWatch.stop();
            System.out.println( stopWatch.getLastTaskTimeMillis() );
        }
    

    结论:

    如果日志是直接打印现成的数据,那么没区别,甚至重复判断了2次
    如果日志输内容是现成数据,并且这个现生成数据比较耗时,那么一定要先判断日志级别是否可用,这样可以在节省大量资源,异步日志是输入日志的时候异步,生成日志类容还是业务线程干的。

posted on 2023-04-10 19:55  zhangyukun  阅读(148)  评论(0编辑  收藏  举报

导航