美文网首页
深度理解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