美文网首页
深度理解logger框架的继承关系

深度理解logger框架的继承关系

作者: 程序员老帮菜 | 来源:发表于2020-09-11 16:51 被阅读0次

    问题描述

    1. log4j配置文件中没配置应用目录的appender,为什么会有日志输出?
    2. 关闭了log4j中logger节点的日志打印,为什么catalina.out文件仍然有日志输出?
    3. 为什么应用日志会在appender指定的目录及catalina.out文件中重复输出?

    简易log4j XML 配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="INFO">
         <Properties>  
             <Property name="var.log.level">DEBUG</Property>
             <Property name="var.log.path.dir">./</Property>
             <Property name="var.output.pattern">[%-5p %d{yyyy-MM-dd HH:mm:ss.SSS}] [%t] [%l] %m%n</Property>  
         </Properties>
        <Appenders>
            <Console name="stdout" target="SYSTEM_OUT">
                <PatternLayout pattern="${var.output.pattern}" />
            </Console>
            <Console name="stdout1" target="SYSTEM_ERR">
                <PatternLayout pattern="${var.output.pattern}" />
            </Console>
        </Appenders>
    
        <Loggers>
            <AsyncRoot level="INFO" includeLocation="true">
                <AppenderRef ref="stdout" />
            </AsyncRoot>
    
            <AsyncLogger name="com.example.log"
                         level="WARN"
                         includeLocation="true"
                         additivity="true">
                <AppenderRef ref="stdout1"/>
            </AsyncLogger>
        </Loggers>
    </Configuration>
    

    解决

    log框架的继承关系-解决问题1

    • 只配置了com.example.log目录的logger,但是com.example.log.logTest目录也正常输出了日志,这个是由于log框架日志继承关系导致
    • 在日志打印过程中会寻找对应的目录Logger进行日志输出,当前目录不存在Logger,会寻找上级目录Logger
      com.example.log
      com.example;
      com;
    
    • 寻找logger到应用的跟目录仍然未找到,使用rootLogger进行日志输出,rootLogger默认level为ERROR,输出方式为SYSTEM_OUT,输出到控制台

    配置文件中Console节点与AsyncRoot节点-解决问题2

    • Console节点是特殊的appender,无需指定目录,默认输出到控制台,tomcat会输出到catalina.out文件中
    • AsyncRoot(rootLogger)节点是特殊的Logger,指定rootLogger的日志级别及输出目录,AppenderRef一般设置为Console,未配置对应的目录logger会,输出到控制台

    配置文件appender中的additivity属性-解决问题3

    • additivity是子Logger是否继承父Logger的输出源(appender)的标志位。
    • 默认值为true,默认情况下子Logger会继承父Logger的appender,也就是说子Logger会在父Logger的appender里输出。
    • 若是additivity设为false,则子Logger只会在自己的appender里输出,而不会在父Logger的appender里输出。

    demo additivity=true

    • xml配置


      xml配置
    • 日志输出结果-自己的appender及父Logger的appender都输出
      日志输出

    demo additivity=false

    • xml配置


      xml配置
    • 日志输出结果-只在自己的appender里输出
      日志输出结果

    统一修改log框架日志级别

    log4j方式

    • 全局配置,包含rootLogger日志级别
    LoggerContext context = (LoggerContext) LogManager.getContext(false);
    Configuration configuration = context.getConfiguration();
        for (Map.Entry<String, LoggerConfig> entry : configuration.getLoggers().entrySet()) {
                if (entry.getValue().getLevel().intLevel() != level.intLevel()) {
                    entry.getValue().setLevel(Level.ERROR);
                }
            }
        context.updateLoggers(configuration);
    
    • 单独设置Logger日志级别
    // log4j中""为rootLogger的path
    Configurator.setLevel("",Level.ERROR);
    Configurator.setLevel("com.example.log.logTest",Level.ERROR);
    

    logback方式

    • 全局配置,包含rootLogger日志级别
    LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
    List<ch.qos.logback.classic.Logger> loggerList = loggerContext.getLoggerList();
    loggerList.forEach(logger1 -> {
         logger1.setLevel(level);
     });
    
    • 单独设置Logger日志级别
    LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
    // log4j中 ROOT 为rootLogger的path,默认DEBUG
    loggerContext.getLogger("ROOT").setLevel(Level.ERROR);
    

    建议

    1. 动态调整日志级别需要调整rootLogger节点的日志基本
      避免降级到rootLogger,导致日志输出调整不完整
    2. 合理设置设置不同目录的日志级别
      比如spring/dubbo/mq的日志可以设置为ERROR,且独立设置日志文件
      mybatis如果需要看sql日志,可以设置为DEBUG,生产可以ERROR
      我们应用目录的日志文件,需要独立设置文件
    3. 合理设置日志文件生成策略
      设置合理的滚动方式(时间滚动/文件大小)
      设置合理的单个文件文件大小及文件个数
      设置正确的日志输出格式,可参考https://www.jianshu.com/p/de1ca64f2a78
    4. 有目的的合理打印日志
      日志需要包含明确的业务意义
      打印对象需要重写toString或者json序列化
      json序列化需要明确的了解序列化对象的结构,避免由于打印日志导致系统异常
      打印json序列化对象,需要判断当前日志级别,避免需用的json序列化

    参考Log4j配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="INFO">
        <Properties>
            <Property name="var.log.level">ERROR</Property>
            <Property name="var.log.path.dir">/tmp/com.abc.local</Property>
            <Property name="var.output.pattern">[%-5p %d{yyyy-MM-dd HH:mm:ss.SSS}] [%t] [%l] %m%n</Property>
        </Properties>
        <Appenders>
            <Console name="stdout" target="SYSTEM_ERR">
                <PatternLayout pattern="${var.output.pattern}" />
            </Console>
            <RollingRandomAccessFile bufferSize="1024" name="framework-output"  fileName="${var.log.path.dir}/framework-output.log" filePattern="${var.log.path.dir}/framework-output.log.%i">
                <PatternLayout>
                    <Pattern>${var.output.pattern}</Pattern>
                </PatternLayout>
                <Policies>
                    <SizeBasedTriggeringPolicy size="100MB" />
                </Policies>
                <DefaultRolloverStrategy max="5" />
            </RollingRandomAccessFile>
            <RollingRandomAccessFile bufferSize="1024" name="proj-output"
                                     fileName="${var.log.path.dir}/proj-output.log"
                    filePattern="${var.log.path.dir}/proj-output.%d{yyyy-MM-dd HH}.%i.log"
            >
                <PatternLayout>
                    <Pattern>${var.output.pattern}</Pattern>
                </PatternLayout>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500MB" />
                    <!--时间滚动-->
                    <TimeBasedTriggeringPolicy interval="1" modulate="false"/>
                </Policies>
                <DefaultRolloverStrategy max="4">
                    <Delete basePath="${var.log.path.dir}/" maxDepth="1">
                        <!--? 此处会删除目录下所有符合条件的log -->
                        <IfFileName glob="*.log"/>
                        <!--!Note: 这里的age必须和filePattern协调, 后者是精确到HH, 这里就要写成xH, xd就不起作用
                        另外, 数字最好>2, 否则可能造成删除的时候, 最近的文件还处于被占用状态,导致删除不成功!-->
                        <IfLastModified age="1H" />
                    </Delete>
                </DefaultRolloverStrategy>
            </RollingRandomAccessFile>
        </Appenders>
        <Loggers>
            <AsyncRoot level="${var.log.level}" includeLocation="true">
                <AppenderRef ref="stdout" />
            </AsyncRoot>
            <!-- log4spring -->
            <AsyncLogger name="org.springframework" level="ERROR" includeLocation="true" additivity="false">
                <AppenderRef ref="framework-output" />
            </AsyncLogger>
            <!-- log4jdbc -->
            <AsyncLogger name="jdbc.sqltiming" level="INFO" includeLocation="true" additivity="false">
                <AppenderRef ref="framework-output" />
            </AsyncLogger>
            <!-- zookeeper -->
            <AsyncLogger name="org.apache.zookeeper" level="WARN" includeLocation="true" additivity="false">
                <AppenderRef ref="framework-output" />
            </AsyncLogger>
            <!-- apache activemq -->
            <AsyncLogger name="org.apache.activemq" level="WARN" includeLocation="true" additivity="false">
                <AppenderRef ref="framework-output" />
            </AsyncLogger>
            <!-- project loggers begin -->
            <AsyncLogger name="com.abc.web" level="${var.log.level}" includeLocation="true" additivity="false">
                <AppenderRef ref="proj-output" />
            </AsyncLogger>
            <!-- project loggers end -->
        </Loggers>
    </Configuration>
    
    

    相关文章

      网友评论

          本文标题:深度理解logger框架的继承关系

          本文链接:https://www.haomeiwen.com/subject/ytiwektx.html