【日志框架-笔记】深入浅出 Log4j,理论-源码-配置

log4j

  • 一、log4j 的概述及其入门程序
    • 入门程序
  • 二、日志输出的需要及PatternLayout类源码分析
    • 日志输出的需要
    • PatternLayout类的源码分析
  • 三、Log4j 占位符的具体含义
  • 四、Log4j 配置文件实操
    • 如何对配置文件进行解析的?(LogManager的静态代码块)
    • 实操
  • 五、自定义 Log4j 配置
    • 取消父类继承的Appender
  • 六、Log4j.xml配置

有关为什么学习Java日志框架和 JUL 的使用在上篇说过:

JUL的介绍及其使用

一、log4j 的概述及其入门程序

Log4j 全称 Logging for Java;它是一个用于Java编程语言的日志记录库(框架),支持多种日志记录格式。包括文本、xml、数据库。和其他日志框架一样,可以把日志信息发送到控制台、文件等不同目的地。有强大的配置功能,当然也可以进行过滤,根据实际情况而论,还是比较灵活的。

Log4j 有三个主要的组件:

  1. Loggers(记录器)
  2. Appenders(输出源)
  3. Layouts(布局,格式转换)

这三组件在上篇 JUL 中也阐述过,Log4j 中的 Logger 也是存在父子关系的,其关系和JUL是一样的。

看看log4j官方对该父子关系的举例(log4j介绍官网):

在这里插入图片描述

Logger 的基本用法:

 public class Logger {

    // Creation & retrieval methods:
    public static Logger getRootLogger();
    public static Logger getLogger(String name);

    // printing methods:
    public void trace(Object message);
    public void debug(Object message);
    public void info(Object message);
    public void warn(Object message);
    public void error(Object message);
    public void fatal(Object message);

    // generic printing method:
    public void log(Level l, Object message);
}

从方法体中也可以发现 log4j 的日志级别名称不同于 JUL。

log4j 的级别(TRACE、DEBUG、INFO、WARN、ERROR、FATAL)。

入门程序

org.apache.log4j.Logger 不同于 java.util.logging.Logger 的是其静态方法的 getLogger 方法除了可以传字符串以外,还可以传Class对象(当然其本质还是XX.class.getName,重载的好处,让你少写点代码)。

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第1张图片

测试:

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第2张图片
下面出现警告信息表示:无法找到该logger的输出源,就是不知道往哪里去输出。

我们可以上官网了解一下如何进行基础配置(就像JUL是默认使用的是conf下面的logging.properties配置文件):

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第3张图片

配置后进行检测:

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第4张图片

可以观察到该基础配置的日志级别的debug

  • 看看BasicCofiguratorconfigure 这个静态方法的实现:

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第5张图片

看完之后咱就可以学着自行去配置输出源和字符串的匹配模式了。上面匹配的是配置父类的,让其按那个格式在控制台进行输出,然后子类会自动继承,也拥有该‘能力’。

二、日志输出的需要及PatternLayout类源码分析

日志输出的需要

首先需要一个输出源(Appender),输出源需要设置输出日志信息的所在位置(setWriter),再确定输出格式,确定日志信息的布局(Layout)。最后将日志联系起输出源进行输出日志信息。

PatternLayout类的源码分析

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第6张图片

  1. 调式进入构造方法(pattern 传入构造 PatternParser解析去去解析它)

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第7张图片
2. 进入到 parse 方法中查看解析过程。首先是去遍历 pattern 字符串,然后遇到占位符(%?)会先存入到 currentLiteral(StringBuffer对象) 当中,然后再通过 finalizeConverter 方法进行转换。

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第8张图片
3. 进入到 finalizeConverter 方法中查看具体实现。

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第9张图片
【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第10张图片
4. 看看最后 head 链表咋用的(看看如何进行布局的)。

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第11张图片

然后下面对上面占位符的具体含义进行介绍(及 finalizeConverter 对占位符的操作)。

三、Log4j 占位符的具体含义

%m 输出代码中指定的日志信息;
%p 输出日志级别,及 DEBUG、INFO 等;
%n 换行符;
%r 输出自应用启动到输出该 log 信息耗费的毫秒数
%c 输出打印语句所属的类的全名
%t 输出产生该日志的线程全名
%d 输出服务器当前时间。这种格式:2023-02-27 09:58:12,977
%l 输出日志时间发生的位置,包括类名、线程、及其代码中的行数。如:com.ncpowernode.test.TestLog4j.testLog4j(TestLog4j.java:44)
%F 输出日志消息产生时所在的文件名称
%L 输出代码中的行号。
%x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。

其中 %l 所可以输出的内容,就包括了类的全名,及其输出代码中的行号。

下面测试一下:“%d [%p]- %l %m%n"的效果:

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第12张图片

四、Log4j 配置文件实操

如何对配置文件进行解析的?(LogManager的静态代码块)

我们知道获取 Logger 对象是通过 Logger的静态方法 getLogger 去得到的。

static public Logger getLogger(Class clazz) {
    return LogManager.getLogger(clazz.getName());
  }

解析配置文件肯定是要在获取 Logger 对象之前的,如何在此之前呢?

通过静态代码块进行实现。 我们知道 getLogger 方法被调用了,那 LogManager 也会被初始化,所以其静态成员会运行。

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第13张图片

下面是部分静态代码块的静态代码,主要就是先会去拿 log4j.xml 的URL,拿不到就去找 log4j.properties 的URL,然后去解析。

static{
      URL url = null;

      // 如果用户没有指定log4j.configuration
      // 我们首先搜索文件"log4j.xml"然后是"log4j.properties"
      if(configurationOptionStr == null) {	
	url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
	if(url == null) {
	  url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
	}
      } else {
	try {
	  url = new URL(configurationOptionStr);
	} catch (MalformedURLException ex) {
	  // so, resource is not a URL:
	  // attempt to get the resource from the class path
	  url = Loader.getResource(configurationOptionStr); 
	}	
      }
      	}
	
	    if(url != null) {
	    LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");
        try {
            OptionConverter.selectAndConfigure(url, configuratorClassName,
					   LogManager.getLoggerRepository());
        } catch (NoClassDefFoundError e) {
            LogLog.warn("Error during default initialization", e);
        }
      } else {
	    LogLog.debug("Could not find resource: ["+configurationOptionStr+"].");
      }

就是说配置文件名字不能乱取,要么log4j.xml 要么log4j.properties.

实操

配置 log4j.properties 文件(文件名不能乱写)。

log4j.rootLogger=TRACE, A1, FILE


log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout

# Print the date in ISO 8601 format
log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %l - %m%n

# Print only messages of level WARN or above in the package com.foo.
log4j.logger.com.foo=WARN

# FILE users FileAppender
# fileName、append属性通过set方法进行赋值的,这里是set后面的名称
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.file=D://logs/log4j.log
log4j.appender.FILE.append=false
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=%d [%t] %-5p %l - %m%n

测试:

	private static final Logger logger = Logger.getLogger(TestLog4j.class);
    @Test
    public void testLog4jFirst(){
        // 日志记录输出
        logger.info("hello log4j");
        // 日志级别
        logger.fatal("fatal");// 严重错误;一般会造成系统崩溃和终止运行
        logger.error("error");// 错误信息, 但不会影响系统运行
        logger.warn("warn");// 警告信息, 可能会发生问题
        logger.info("info");// 程序运行信息,数据库的连接、网络、IO操作等
        logger.debug("debug");// 调试信息,一般在开发阶段使用,记录程序的变量、参数等
        logger.trace("trace");// 追踪信息,记录程序的所有流程信息
    }

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第14张图片

五、自定义 Log4j 配置

当我们需要自定义去配置 Log4j 时,我们可以在配置文件中进行对应的配置。

properties 配置文件中的配置格式:log4j.logger.logger名=级别,Appender....

像下面配置 logger 名为 com.ncpowernode.logstudy 的 logger。

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第15张图片

对该代码进行测试:

    @Test
    public void testLog4jSelfConfig(){
        logger.trace("trace");
        logger.debug("debug");
        logger.error("error");
    }

在这里插入图片描述

输出了两条一样的,是因为 Appender 是可以继承的,可以通过配置或者Java代码进行取消继承,跟 JUL 是一样的,使用配置可以针对某个输出源进行取消,而使用Java代码的话会默认取消所有的父类下来的Appender。从输出结果可以看见,trace没有没输出,可以说明级别取的是父类(最接近的)。

取消父类继承的Appender

log4j 中 Logger 类中有个 setAdditivity 方法,可以取消该 logger 上继承下来的 Appender

当然也可以通过配置文件进行取消:下面取消 com.ncpowernode 的父类继承的Appender。

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第16张图片

然后再将上面的测试代码进行测试结果为:

【日志框架-笔记】深入浅出 Log4j,理论-源码-配置_第17张图片

rootLogger 配置的输出源被完美取消。

六、Log4j.xml配置

log4j.xml 配置感觉会比 properties 配置要好点,复用性强点。

具体可以看 Log.xml配置文件详解.

你可能感兴趣的:(Java日志框架,log4j,java,apache)