初探log4j

作者: 赵阳_c149 | 来源:发表于2019-09-25 13:18 被阅读0次

    log4j 1.x

    log4j是目前较为常用的日志框架,该项目由apache进行维护。(其他常见的日志框架还有java.util.logging,Logback)。Log4j相比较于System.out.println 的一个最大优势,就在于他将日志进行了归类,即划分了不同的优先级。通过优先级的划分,log4j可以控制输出哪些日志,不输出哪些日志。

    那么log4j是如何做到的呢?我想从结构相对简单的log4j 1.x说起。事实上,从2015年 8月5日起,apache已经宣称不再继续维护Log4j 1,而转而支持Log4j 2【1】。Log4j 2在Log4j 1的基础上进行了一系列的增强和扩展,但是基本思路并没有本质的改变,即都是通过继承的层级实现了对日志输出的控制。Log4j 1的结构相对简单,所以本文将从Log4j 1开始讲起,介绍Log4j的基本思路,然后扩展到Log4j 2的一些特性。

    在介绍log4j 1.x的组件之前,让我们首先看看他是如何对日志划分优先级的。默认情况下,日志的优先级按照从低到高的顺序可以列为:
    TRACE,DEBUG,INFO,WARN,ERROR 和FATAL。
    这些优先级的含义可以从其名字粗略的推断出来,详细的说明可以查看文档【2】。

    简单来说log4j 1.x主要包含三个组件:Loggers,Appenders 和Layout。

    • Logger
      Logger是Log4j的核心类,大多数和日志有关的操作都是通过Logger来进行。例如,要输出一条error级别的日志:
    Logger logger = LogManager.getLogger();logger.error("trace level");
    

    Logger有自己的名字,他的名字是区分大小写的,logger和Logger之间通过名字定义继承关系。

    Named Hierarchy
    A logger is said to be an ancestor of another logger if its name followed by a dot is a prefix of >the descendant logger name. A logger is said to be a parent of a child logger if there are no >ancestors between itself and the descendant logger.

    举个简单的例子,一个命名为“com.foo”的logger是命名为“com.foo.Bar”的logger的父亲。在继承树的顶端是一个默认的根logger,由Log4j维护(类似java 类型树的根节点Object)。运行以下代码将创建一个logger,并将其加入以root logger为根的继承树

    LogManager.getLogger()
    

    如果不特殊指定,新创建的Logger从其祖先节点继承日志的优先级。

    • Appender
      Appender定义了日志输出的目标,包括console,file,GUI组件,远程套接服务器,JMS等等。为同一个Logger可以添加多个appender,并且一个logger将继承其所有祖先的appender,也就是说执行
    logger.error("trace level");
    

    将向所有添加到该logger及其祖先的appender输出一条日志。

    • Layout
      Layout决定了日志输出的格式。

    可以使用Configuration 文件对log4j 进行配置:

    # Set root logger level to DEBUG and its only appender to A1.
    log4j.rootLogger=DEBUG, A1
    
    # A1 is set to be a ConsoleAppender.
    log4j.appender.A1=org.apache.log4j.ConsoleAppender
    
    # A1 uses PatternLayout.
    log4j.appender.A1.layout=org.apache.log4j.PatternLayout
    log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
    

    log4j 2

    以上我们简单浏览了log4j 1 的一些基本特性,接下来我们看看在此基础上,log4j 2做了哪些改进。
    首先来看一下log4j 2的架构图:


    log_4_j_2.png

    通过架构图,不难看出,log4j主要增加了一下几个组件:Filter,LoggerContext,LoggerConfig,StrSubsitutor和StrLookup【3】。下面逐一对其进行介绍。

    • Filter:
      Filter类似防火墙的filter【4】,对于每一个传入的日志请求,每个filter都可以返回三种结果:Accept,Deny或者Neutral。
    1. Accept:日志请求可以被处理,不管还有没有必要调用其他filter。
    2. Deny:日志请求将被直接返回。
    3. Neutral:日志请求将被传输给其他的filter。
      Filter是对依据logLevel(日志优先级)自动过滤机制的补充。
    • LoggerConfig
      LoggerConfig:在log4j 2中,logger之间的层级关系是通过LoggerConfig实现的。LoggerConfig 有自己的名字,他的名字是区分大小写的,LoggerConfig和LoggerConfig之间通过名字定义继承关系。

    Named Hierarchy
    A LoggerConfig is said to be an ancestor of another LoggerConfig if its name followed by a >dot is a prefix of the descendant logger name. A LoggerConfig is said to be a parent of >a child LoggerConfig if there are no ancestors between itself and the descendant >LoggerConfig.

    举个简单的例子,一个命名为“com.foo”的LoggerConfig是命名为“com.foo.Bar”的LoggerConfig的父亲。在继承树的顶端是一个默认的根LoggerConfig,由Log4j维护。

    每个在configuration 中声明的Logger都有一个LoggerConfig,不同Logger之间可能会共享同一个LoggerConfig。当创建一个logger的时候,将为其关联一个LoggerConfig,具体关联到哪个,遵从以下规则:

    1. LoggerConfig的名字和logger相同
    2. LoggerConfig的名字和父包相同。
    3. 根LoggerConfig
      每个LoggerConfig上关联着Filter和Appender,用于日志事件的实际分发。
    • LoggerContext:
      log4j 2支持一些新的应用场景,例如可能会有多个应用共享同一个环境,而每个应用都希望拥有独立的日志环境,在这种情况下,需要为每个需要建立独立日志环境的应用配置各自的LoggerContext【4】。每个LoggerContext都有自己的Configuration,这个configuration包含所有的appender,context-wide filter,LoggerConfig等组件。

    • StrSubsitutor和StrLookup
      这两个组件借鉴于Apache Commons Lang。他们提供了一种从System Properties, onfiguration 文件和ThreadContext Map内获取变量引用的机制 LogEvent.【6】

    总体来说,log4j 2在保留了log4j 1的诸多特性的同时,配置更加灵活,支持更多的场景(包括多线程),自动重加载, 支持基于context data,markers,regular 表达式,和其他组件的filter,此外,极大的改善了性能。

    Sample

    最后,是一个简单的sample。

    • 首先将log4j所需jar包导入工程:


      config_log4j.png
    • 其次,在src/main/resoures下建立log4j配置文件
    <?xml version="1.0" encoding="UTF-8"?>
    <Configuration status="WARN">
        <Appenders>
            <Console name="Console" target="SYSTEM_OUT">
                <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
            </Console>
        </Appenders>
        <Loggers>
            <Root level="error">
                <AppenderRef ref="Console" />
            </Root>
        </Loggers>
    </Configuration>
    
    • 添加代码:
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    
    public class Log4jHelloWorld {
        public static void main(String...strings){
            Logger logger = LogManager.getLogger();
            logger.error("trace level");
        }
    }
    
    • 运行:
    13:39:52.521 [main] ERROR Log4jHelloWorld - trace level
    

    如果没有配置文件,运行没有问题,但是log4j会抱怨找不到配置文件,而且日志只会在console中输出。

    ERROR StatusLogger No Log4j 2 configuration file found. Using default configuration (logging only errors to the console), or user programmatically provided configurations. Set system property 'log4j2.debug' to show Log4j 2 internal initialization logging. See https://logging.apache.org/log4j/2.x/manual/configuration.html for instructions on how to configure Log4j 2
    13:37:48.723 [main] ERROR Log4jHelloWorld - trace level
    

    【1】log4j 1.x
    【2】log4j log level
    【3】architecture
    【4】Iptables
    【5】Logging Separation
    【6】commons-lang

    相关文章

      网友评论

        本文标题:初探log4j

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