关于log4j记录日志源码解读

本文详细剖析了Log4j如何通过源码实现日志记录,包括Logger对象的创建、repository的isDisabled方法、Level的设置与判断,以及RootLogger配置对日志级别筛选的影响。
摘要由CSDN通过智能技术生成

上篇文章初步分析了下Log4j加载时候的源码,那log4j 是如果使用这些配置文件去记录日志的勒

那就记录下通过log4j的源码来拆解下log4j记录日志的步骤

我们使用log4j日志记录log的时候,开始会生成一个Logger类的对象如下

private static final Logger logger = Logger.getLogger(getClass());

这里初始化一个logger对象,Logger可以进去看到继承了Category,里面可以看到debug,info,error ,log等等的方法,拿info举例

public void info(Object message) {
        if (!this.repository.isDisabled(20000)) {
            if (Level.INFO.isGreaterOrEqual(this.getEffectiveLevel())) {
                this.forcedLog(FQCN, Level.INFO, message, (Throwable)null);
            }

        }
    }

如上为info的内容,首先判断repository

那这个repository是哪里来的,isDisabled的里面传的参数为什么是20000,为什么INFO不能写Debug的日志

这里我们就要回到Log4j加载参数的过程,上次分析log4j加载参数的过程只是看了加载配置文件的这部分,忽略了repository的内容,所以还是回去看log4j加载参数的过程

回到LogManger的加载过程

static {
        Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));
        repositorySelector = new DefaultRepositorySelector(h);
        String override = OptionConverter.getSystemProperty("log4j.defaultInitOverride", (String)null);
        if (override == null || "false".equalsIgnoreCase(override)) {
            String configurationOptionStr = OptionConverter.getSystemProperty("log4j.configuration", (String)null);
            String configuratorClassName = OptionConverter.getSystemProperty("log4j.configuratorClass", (String)null);
            URL url = null;
            if (configurationOptionStr == null) {
                url = Loader.getResource("log4j.xml");
                if (url == null) {
                    url = Loader.getResource("log4j.properties");
                }
            } else {
                try {
                    url = new URL(configurationOptionStr);
                } catch (MalformedURLException var5) {
                    url = Loader.getResource(configurationOptionStr);
                }
            }

            if (url != null) {
                LogLog.debug("Using URL [" + url + "] for automatic log4j configuration.");
                OptionConverter.selectAndConfigure(url, configuratorClassName, getLoggerRepository());
            } else {
                LogLog.debug("Could not find resource: [" + configurationOptionStr + "].");
            }
        }

    }

首先会初始化一个Hierarchy的对象,这个Hierarchy实现了LoggerRepository接口,所以repository这个对象就是由Hierarchy而来,这个在后面调用的时候会用到,看看Hierarchy的初始化过程

    public Hierarchy(Logger root) {
        this.root = root;
        this.setThreshold(Level.ALL);
        this.root.setHierarchy(this);
        this.rendererMap = new RendererMap();
        this.defaultFactory = new DefaultCategoryFactory();
    }

在setThreshod(Level.ALL)方法中 这里默认设定了所有log4j的log等级的,跟到Level对象中,可以发现

    public static final Level OFF = new Level(2147483647, "OFF", 0);
    public static final Level FATAL = new Level(50000, "FATAL", 0);
    public static final Level ERROR = new Level(40000, "ERROR", 3);
    public static final Level WARN = new Level(30000, "WARN", 4);
    public static final Level INFO = new Level(20000, "INFO", 6);
    public static final Level DEBUG = new Level(10000, "DEBUG", 7);
    public static final Level TRACE = new Level(5000, "TRACE", 7);
    public static final Level ALL = new Level(-2147483648, "ALL", 7);

可以发现INFO 定义的值为20000,所以到这里我们就解决了之前的一个问题,

为什么在执行logger.info时候里面执行的repository.isDisable方法里面传的值为什么是20000

isDisable的执行方法就是判断

之前提到了Hierarchy 实现了LoggerRepository接口,所以isDisable的具体执行过程请在Hierarchy

类中查看如下

 public boolean isDisabled(int level) {
        return this.thresholdInt > level;
    }

这里返回的是一个值的对比结果,thresholdInt的值在初始Hierarchy时候通过setThreshold设定的,可以自行去看

public void info(Object message) {
        if (!this.repository.isDisabled(20000)) {
            if (Level.INFO.isGreaterOrEqual(this.getEffectiveLevel())) {
                this.forcedLog(FQCN, Level.INFO, message, (Throwable)null);
            }

        }
    }

当判断repository.isDisabled返回false的时候,会去判断log等级是否为INFO,

判断的过程中会执行getEffectiveLevel的方法,这个方法在Categroy类中,logger继承了这个类

RootLogger又继承了Logger这个类,为什么要说这个关系,因为getEffectiveLevel这段代码中逻辑如下

public Level getEffectiveLevel() {
        for(Category c = this; c != null; c = c.parent) {
            if (c.level != null) {
                return c.level;
            }
        }

        return null;
    }

这段的逻辑是他会去找最初的RootLogger 去获取RootLogger里面的设置的log等级信息,看下这张截图就可以很明显

而RootLogger中的消息等级在初始化LoggManager的过程将配置文件中的Root下的Level设定的值给设置到RootLogger中,当设定的为Level等级和实际要输出的Level等级不一致的时候,就不会进行记录,实际测试也是这样

设定RootLogger中的Level的值代码如下

   protected void parseRoot(Element rootElement) {
        Logger root = this.repository.getRootLogger();
        synchronized(root){}

        try {
            this.parseChildrenOfLoggerElement(rootElement, root, true);
        } catch (Throwable var5) {
            throw var5;
        }

    }

在parseChildrenOfLoggerElement方法中可以看到有个parseLevel的方法

所以info方法只能写定义为INFO的日志

关于写日志的过程,后面会继续深挖学习,加油学习!

  • 9
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值