`

源码赏析之log4j组件初探

阅读更多

       如何更有效地学习开源项目的代码?个人觉得如下几点必不可少。

1.在下载源代码之后,首先编译通过、要跑起来正常运行;

2. 找到项目在正常运行时的入口点,从入口点所在的那个源文件开始阅读,逐步把握整个项目是如何运转的;

3.尝试理解系统的内部结构,有多少组成部分,主要模块是哪些?辅助模块又是哪些?

4.从实际需要出发,修改这个项目,满足自己的某一个小的需求。

5.看看相关的讨论与心得,是否与自己的理解相一致。

    本人最近在探究log4j开源项目,正是按照上面的步骤一步一步的走下来,接下来就把这段时间的学习过程及成果总结一下。

1       源码获取及编译

源码获取可以到官网下载,也可以从github抽取;编译方式可以使用javac,IDE工具,还有ant,maven。方式多种多样,这里略过。

2      源码测试

根据源码包提供的INSTALL说明,可以使用如下的程序作为正常运行时的入口点。

import org.apache.log4j.Logger;

import org.apache.log4j.BasicConfigurator;

public class Hello {

  static Logger logger = Logger.getLogger(Hello.class);

  public static void main(String argv[]) {

       BasicConfigurator.configure();

       logger.debug("Hello world.");

  }

}

    接下来贴出log4j的类图及时序图,先从整体上先了解一下log4j的结构。

3       架构图之类图



 

    因为图片大小限制,再加上侧重点的原因。上图只包含了跟日志记录有关的逻辑完整的类图。图中实线表示关联关系,虚线表示依赖关系,带空心三角线表示继承关系。

4       架构图之时序图



 

    时序图通过描述对象之间发送消息的时间顺序显示多个对象之间的动态协作。由于图片大小限制,上图显示的是log4j组件里主要对象之间的协作关系,某些细节的对象协作关系并没有在图中体现。比如:Logger对象是如何通过repository进行管理,Logger如何管理多个Appender,PatternLayout是如何创建PatternConverter的。这些内容将在后续的博文里进行分析。

5       源码分析

    此处的代码均来自log4j官网,由于侧重点的不同,为了分析的方便,本文删除了一些无关的代码。

5.1              BasicConfigurator类

/**

  *使用这个类可以快速地配置log4j包,如果需要基于properties文件的配置,使用PropertyConfigurator类;如果需要xml文件的配置,使用DOMConfigurator类。

*/

public class BasicConfigurator {

  /**

    *添加一个使用PatternLayout布局的ConsoleAppender,并且将其添加到root日志记录器。

    */

  public static void configure() {

    Logger root = Logger.getRootLogger();

    root.addAppender(

      new ConsoleAppender(

        new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN)));

  }

}

5.2              Logger类

/**

 * 这是log4j的核心类。大部分的日志操作,除了配置之外,都是通过这个类来实现的。

*/

public class Logger extends Category {

}

5.3              Category类

    这是Logger的继承类。

 // Logger是Category的一个子类,它继承自Category。

public class Category implements ULogger, AppenderAttachable {

   // Category类的全名。

  private static final String FQCN = Category.class.getName();

   // category的名字。

  protected String name;

   // category的级别。

  protected volatile Level level;

   // category的父类。

  protected volatile Category parent;

       // 输出目的地。该类存放多个Appender。

  AppenderAttachableImpl aai;

  /**

   * 添加新的输出目的地到Category的目的地列表。如果该输出目的地已经存在于该列表,则不会再次新增。

   */

  public void addAppender(Appender newAppender) {

    try {

           if (aai == null) {

             aai = new AppenderAttachableImpl();

           }

           aai.addAppender(newAppender);

    } finally {

    }

  }

  /**

   * 记录调试级别的日志。

   */

  public void debug(Object message)

     forcedLog(FQCN, Level.DEBUG, message, null);

  }

  /**

   * 该方法创建一个新的日志上下文,并且记录相关日志信息。

   */

  protected void forcedLog(String fqcn, Priority level, Object message, Throwable t) {

    callAppenders(new LoggingEvent(fqcn, (Logger) this, level, message, t));

  }

  /**

   * 调用输出目的地进行日志记录。

   */

  public void callAppenders(LoggingEvent event) {

    int writes = 0;

    for (Category c = this; c != null; c = c.parent) {

          try {

        if (c.aai != null) {

          writes += c.aai.appendLoopOnAppenders(event);

        }

           } finally {

              }

    }

  }

}

5.4             AppenderAttachableImpl类

/**

  *AppenderAttachable类的实现类。

  */

public class AppenderAttachableImpl implements AppenderAttachable {

  /** 输出目的地列表 */

  protected Vector appenderList;

  /**

    *调用所有输出目的地的doAppend方法。

    */

  public int appendLoopOnAppenders(LoggingEvent event) {

    int size = 0;

    Appender appender;

    if (appenderList != null) {

      size = appenderList.size();

      for (int i = 0; i < size; i++) {

        appender = (Appender) appenderList.elementAt(i);

        appender.doAppend(event);

      }

    }

    return size;

  }

}

5.5             ConsoleAppender类

/**

  * ConsoleAppender使用用户指定的格式将日志输出到System.out或者System.err。

  */

public class ConsoleAppender extends WriterAppender {

    /**

     * 构造一个输了目的地。

     */

    public ConsoleAppender(final Layout layout) {

        setLayout(layout);

    }

}

5.6              WriterAppender类

    这是ConsoleAppender的父类。

/**

      * WriterAppender输出日志到java.io.Writer或者java.io.OutputStream,这依赖于用户的选择。

      */

public class WriterAppender extends AppenderSkeleton {

  /**

   * 这是一个我们将要记录日志的地方QuietWriter。

     */

  protected QuietWriter qw;

  /**

*该方法由AppenderSkeleton类的doAppend方法调用。

  */

  public void append(LoggingEvent event) {

    if (!checkEntryConditions()) {

      return;

    }

    subAppend(event);

  }

  /**

   * 这是实际进行日志记录的方法。

   * 大部分WriterAppender的子类需要重写这个方法。

   */

  protected void subAppend(LoggingEvent event) {

         // 调用layout的format方法对日志信息进行格式化,再将其输出到相应的appender。

      this.qw.write(this.layout.format(event));

  }

}

5.7              AppenderSkeleton类

    这是WriterAppender的父类。

/**

 * 这是log4j包里其它输出目的地的抽象父类。这个类提供了公用功能的代码。

 */

public abstract class AppenderSkeleton extends ComponentBase implements Appender, OptionHandler {

 

  /**

   * 布局变量。如果输出目的地实现类有指定布局,则不需要设置该变量。

   */

  protected Layout layout;

  /**

   * 输出目的地的名称。

   */

  protected String name;

   

  /**

   * 设置输出目的地的布局。注意:有些输出目的地有它们自己的布局而无需指定该变量。

   */

  public void setLayout(Layout layout) {

    this.layout = layout;

  }

  /**

   * 这个方法执行一些日志记录前的检查然后调用子类的日志记录功能。

   */

  public synchronized void doAppend(LoggingEvent event) {

              ... ...

    // 调用子类WriterAppender类的append方法。

    this.append(event);

  }

}

5.8              PatternLayout类

/**

  * 一个灵活的使用模式字符串的布局。这个类的目标是格式化一个日志上下文并返回结果。

  *

public class PatternLayout extends Layout {

  public static final String DEFAULT_CONVERSION_PATTERN = "%m%n";

  public static final String TTCC_CONVERSION_PATTERN = "%r [%t] %p %c %x - %m%n";

  /**

    *  模式转换器。

    */

  private PatternConverter head;

  /**

   * 转换模式。

   */

  private String conversionPattern;

 

  /**

    * 使用给定的模式字符串构造一个模式布局类。

   * @param pattern conversion pattern.

     */

  public PatternLayout(final String pattern) {

    this.conversionPattern = pattern;

    head = createPatternParser(

            (pattern == null) ? DEFAULT_CONVERSION_PATTERN : pattern).parse();

  }

    /**

      * 返回一个模式转换器的解析器。

          */

    protected org.apache.log4j.helpers.PatternParser createPatternParser(String pattern) {

      return new org.apache.log4j.pattern.BridgePatternParser(pattern,repository, getLogger());

    }

  /**

   *  格式化一个日志上下文到writer。

  */

  public String format(final LoggingEvent event) {

      StringBuffer buf = new StringBuffer();

      for(PatternConverter c = head;

          c != null;

          c = c.next) {

          c.format(buf, event);

      }

      return buf.toString();

  }

}

5.9              解析器BridgePatternParser类

/**

 * 该类实现于log4j 1.3的org.apache.log4j.helpers.PatternConverter类。

 */

public final class BridgePatternParser extends org.apache.log4j.helpers.PatternParser {

  /**

   * 创建一个新的模式转换器。

   * @return pattern converter.

   */

  public org.apache.log4j.helpers.PatternConverter parse() {

    return new BridgePatternConverter(pattern, repository, logger);

  }

}

5.10        转换器BridgePatternConverter类

/**

* 该类实现了log4j 1.3的org.apache.log4j.helpers.PatternConverter类。

 */

public final class BridgePatternConverter extends org.apache.log4j.helpers.PatternConverter {

  /**

   * 模式转换器数组。

   */

  private LoggingEventPatternConverter[] patternConverters;

  /**

   * 日志的长度和排列规则。

   */

  private FormattingInfo[] patternFields;

  /**

   * 构造一个BridgePatternConverter。

   */

  public BridgePatternConverter(final String pattern, final LoggerRepository repository,final ULogger logger) {

    patternConverters = new LoggingEventPatternConverter[converters.size()];

    patternFields = new FormattingInfo[converters.size()];

    Iterator converterIter = converters.iterator();

    Iterator fieldIter = fields.iterator();

    while (converterIter.hasNext()) {

      Object converter = converterIter.next();

      if (converter instanceof LoggingEventPatternConverter) {

        patternConverters[i] = (LoggingEventPatternConverter) converter;

      } else {

        patternConverters[i] = new org.apache.log4j.pattern.LiteralPatternConverter("");

      }

      if (fieldIter.hasNext()) {

        patternFields[i] = (FormattingInfo) fieldIter.next();

      } else {

        patternFields[i] = FormattingInfo.getDefault();

      }

    }

  }

  /**

     格式化日志上下文到string buffer.

   */

  public void format(final StringBuffer sbuf, final LoggingEvent e) {

    for (int i = 0; i < patternConverters.length; i++) {

      int startField = sbuf.length();

      patternConverters[i].format(e, sbuf);

      patternFields[i].format(startField, sbuf);

    }

  }

}

5.11        转换器LoggingEventPatternConverter类

/**

 * LoggingEventPatternConverter是一个模式转换器的基础类,可以从日志上下文转换信息。

 */

public abstract class LoggingEventPatternConverter extends PatternConverter {

  /**

   * 格式化一个日志上下文到StringBuffer.

   */

  public abstract void format(final LoggingEvent event, final StringBuffer toAppendTo);

  /**

   * {@inheritDoc}

   */

  public void format(final Object obj, final StringBuffer output) {

if (obj instanceof LoggingEvent) {

  // 调用子类LiteralPatternConverter的format方法。

      format((LoggingEvent) obj, output);

    }

  }

5.12        LiteralPatternConverter类

    这是LoggingEventPatternConver的子类。

/**

 * 格式化一个字符串。

 */

public final class LiteralPatternConverter extends LoggingEventPatternConverter {

  private final String literal;

  /**

   * 构造一个新实例。

   */

  public LiteralPatternConverter(final String literal) {

    super("Literal", "literal");

    this.literal = literal;

  }

  /**

   * {@inheritDoc}

   */

  public void format(final LoggingEvent event, final StringBuffer toAppendTo) {

    toAppendTo.append(literal);

  }

}

5.13        FormattingInfo类

/**

 * 根据一个指定的最小和最大宽度和排列来修改模式转换器的输出。

 */

public final class FormattingInfo {

  /**

   * 根据指定的长度和排列方式来调整buffer的内容。

   */

  public final void format(final int fieldStart, final StringBuffer buffer) {

      final int rawLength = buffer.length() - fieldStart;

    if (rawLength > maxLength) {

      buffer.delete(fieldStart, buffer.length() - maxLength);

    } else if (rawLength < minLength) {

      if (leftAlign) {

        final int fieldEnd = buffer.length();

        buffer.setLength(fieldStart + minLength);

        for (int i = fieldEnd; i < buffer.length(); i++) {

          buffer.setCharAt(i, ' ');

        }

      } else {

        int padLength = minLength - rawLength;

        for (; padLength > 8; padLength -= 8) {

          buffer.insert(fieldStart, SPACES);

        }

        buffer.insert(fieldStart, SPACES, 0, padLength);

      }

    }

  }

}

  • 大小: 37.9 KB
  • 大小: 27.7 KB
1
0
分享到:
评论

相关推荐

    log4j 源码包 日志包 2.11.0

    本文将围绕Log4j 2.11.0的源码展开,详细剖析其核心组件、设计理念和关键特性。 1. **日志系统概述** 日志系统是软件开发中的重要组成部分,它用于记录程序运行过程中的信息,帮助开发者定位问题、监控系统状态和...

    Apache Log4j 2 源代码( apache-log4j-2.17.1-src.zip)

    Apache Log4j 2 源代码( apache-log4j-2.17.1-src.zip) 是对 Log4j 的升级,它比其前身 Log4j 1.x 提供了重大改进,并提供了 Logback 中可用的许多改进,同时修复了 Logback 架构中的一些固有问题。修复了安全漏洞...

    Log4j工程官方源码

    - **Logger**: 日志器是Log4j中最基本的组件,用于记录日志信息。开发者可以通过获取特定级别的Logger来记录不同严重程度的消息。 - **Appender**: Appender负责将日志信息输出到指定的目标,如控制台、文件、...

    Log4j详解,详细讲解log4j的使用,和原理

    配置文件是 Log4j 的核心组件之一,通过配置文件可以灵活地控制日志的输出。 Log4j 的配置方式 Log4j 提供了多种配置方式,包括: * 通过配置文件来配置 Log4j * 通过 Java 代码来配置 Log4j Log4j 的输出级别 ...

    Log4j2、Fastjson、Log4j的BurpSuite插件亲测有效

    Log4j、Log4j2和Fastjson的安全性问题在过去曾引起广泛关注,例如Log4j2的CVE-2021-44228(也被称为Log4Shell漏洞),这是一个远程代码执行漏洞,影响了许多使用Log4j2的系统。这个插件可能就是为了检测和利用这些...

    log4j 1.2.8 jar 包含源码

    Appenders 是 Log4j 的核心组件之一,它们负责将日志信息发送到特定的目的地。例如,`ConsoleAppender` 将日志输出到控制台,`FileAppender` 则写入文件。通过配置不同的 Appenders,开发者可以灵活控制日志的存储...

    Log4j2异步写日志效率测试源码

    本文主要探讨Log4j2异步写日志的效率,通过源码分析和测试来展示其优势。首先,我们要理解Log4j2中的异步日志工作原理。默认情况下,Log4j2使用同步模式记录日志,即每个日志事件都会阻塞直到写入完成。然而,通过...

    Log4j2效率测试源码

    本测试源码着重关注Log4j2的性能表现,这对于理解和优化日志系统的性能至关重要。Log4j2的设计目标是提供比其前一代Log4j更高的性能,并且具有更丰富的配置选项和更低的内存占用。 1. **Log4j2架构与性能优势** - ...

    Log4j将System.out搞到log4j中输出四

    在《Log4j将System.out搞到log4j中输出四》这篇博文中,作者可能详细讨论了这些步骤,并可能分享了一些实战经验。通过学习这篇博文,读者可以更深入地了解如何在实际项目中实现这一转换,提升日志管理的效率。 总结...

    log4j简单使用

    "源码"标签暗示我们将讨论Log4j的内部机制或如何查看和理解其代码,这对于学习和定制Log4j功能很有帮助。而"工具"标签则表明Log4j是一个开发者常用的工具,它的使用和配置是提高开发效率的关键。 **压缩包文件名称...

    apache-log4j-1.2.16.rar_apache log4j 1.2.16_log4j source code_lo

    Apache Log4j是Java平台上的一个著名日志记录框架,广泛应用于各种Java应用程序中,包括服务器、Web应用、企业级软件等。Log4j 1.2.16是该框架的一个版本,提供了丰富的日志功能,允许开发者灵活地控制日志信息的...

    apache-log4j-2.17.2-bin.tar

    此次发布的binaries版本包含了编译后的二进制文件,适用于那些不希望从源码编译但需要快速部署和更新Log4j的用户。 描述中提到的“解决了漏洞版本”指的是Log4j 2.x 在2021年末被发现了一个名为“Log4Shell”的高危...

    log4j 源码

    log4j 源代码 需要源码的请下载,java版,下载后自行关联。

    log4j-1.2.15

    在源码层面,log4j-1.2.15版本主要包含以下几个关键组件: 1. **Logger**:这是日志系统的心脏,负责接收日志事件并决定是否需要进一步处理。每个类都可以拥有一个独立的Logger实例,便于分类记录日志。 2. **...

    Apache Log4j 2 源代码( apache-log4j-2.17.1-src.tar.gz)

    Apache Log4j 2 源代码( apache-log4j-2.17.1-src.tar.gz) 是对 Log4j 的升级,它比其前身 Log4j 1.x 提供了重大改进,并提供了 Logback 中可用的许多改进,同时修复了 Logback 架构中的一些固有问题。修复了安全...

    log4j各个版本源码

    log4j各个版本源码 1.2.8 1.2.15 1.2.17 2.3 这四个版本的源码 Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录...

    mybatis源码+练习代码+插件+log4j2+maven

    【标题】"mybatis源码+练习代码+插件+log4j2+maven" 提供的资源包是一个综合的学习资料集合,旨在帮助用户深入理解MyBatis这一流行持久层框架,同时涵盖日志管理工具Log4j2和项目构建工具Maven。以下是这些组件的...

    新版本与旧版本log4j.jar包下载,附使用说明----.zip

    6. **apache-log4j-2.14.1-bin.zip**: 这可能是Log4j的完整二进制包,除了log4j.jar之外,还可能包含其他相关文件,如配置示例、文档、源码等。解压这个文件可以获取更多关于Log4j的资源,便于深入学习和调试。 综...

    log4j-1.2.16.jar 源码包

    log4j-1.2.16.jar 源码包

    log4j(二):动态配置日志输出路径

    首先,我们要理解Log4j的核心组件。Log4j主要包括三个关键部分:Logger(日志器)、Appender(输出器)和Layout(布局)。Logger负责生成日志事件,Appender决定这些事件如何被输出(例如,写入文件、发送邮件或显示...

Global site tag (gtag.js) - Google Analytics