如何更有效地学习开源项目的代码?个人觉得如下几点必不可少。
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);
}
}
}
}
相关推荐
在log4j 1.2.15源码中,我们可以深入研究这些核心组件的实现细节,例如Logger是如何根据配置文件进行初始化的,Appender是如何实现日志输出的,以及Level是如何影响日志过滤的。通过对这些关键部分的分析,开发者...
本文将对"Log4j 1.2.17含源码"进行深入探讨,包括其核心组件、配置、使用方法以及源码分析。 首先,Log4j的核心组件主要包括Appender、Layout、Logger和Level。Appender负责输出日志信息到指定的目标,如控制台、...
Apache Log4j 2 源代码( apache-log4j-2.17.1-src.zip) 是对 Log4j 的升级,它比其前身 Log4j 1.x 提供了重大改进,并提供了 Logback 中可用的许多改进,同时修复了 Logback 架构中的一些固有问题。修复了安全漏洞...
《深入解析log4j-1.2.13源码》 Apache Log4j是Java平台上广泛使用的日志记录框架,其1.2.13版本的源码为我们提供了深入了解这个强大工具的机会。本文将围绕log4j的核心概念、设计模式、主要组件以及其实现细节进行...
**log4j学习源码教程** 在Java编程中,日志记录是不可或缺的一部分,它能够帮助开发者追踪程序运行状态,定位错误和异常,为调试和性能优化提供关键信息。Log4j是Apache组织开发的一个开源日志框架,因其强大的功能...
2. **Log4j核心组件** - **Logger**:日志记录器,是日志输出的起点,可以根据级别(如DEBUG, INFO, WARN, ERROR, FATAL)来控制输出哪些日志。 - **Appender**:日志输出的目标,例如控制台、文件、数据库、网络...
Appenders 是 Log4j 的核心组件之一,它们负责将日志信息发送到特定的目的地。例如,`ConsoleAppender` 将日志输出到控制台,`FileAppender` 则写入文件。通过配置不同的 Appenders,开发者可以灵活控制日志的存储...
本测试源码着重关注Log4j2的性能表现,这对于理解和优化日志系统的性能至关重要。Log4j2的设计目标是提供比其前一代Log4j更高的性能,并且具有更丰富的配置选项和更低的内存占用。 1. **Log4j2架构与性能优势** - ...
在《Log4j将System.out搞到log4j中输出四》这篇博文中,作者可能详细讨论了这些步骤,并可能分享了一些实战经验。通过学习这篇博文,读者可以更深入地了解如何在实际项目中实现这一转换,提升日志管理的效率。 总结...
"源码"标签暗示我们将讨论Log4j的内部机制或如何查看和理解其代码,这对于学习和定制Log4j功能很有帮助。而"工具"标签则表明Log4j是一个开发者常用的工具,它的使用和配置是提高开发效率的关键。 **压缩包文件名称...
Log4j之所以受到广泛使用,是因为其具有灵活性、配置简单、扩展性强等特点。它允许开发者自由地控制日志记录的细节,如输出格式、存储位置、输出级别等,这对于大型应用系统的日志管理尤为重要。 以上知识点虽然...
Apache Log4j是Java平台上的一个著名日志记录框架,广泛应用于各种Java应用程序中,包括服务器、Web应用、企业级软件等。Log4j 1.2.16是该框架的一个版本,提供了丰富的日志功能,允许开发者灵活地控制日志信息的...
此次发布的binaries版本包含了编译后的二进制文件,适用于那些不希望从源码编译但需要快速部署和更新Log4j的用户。 描述中提到的“解决了漏洞版本”指的是Log4j 2.x 在2021年末被发现了一个名为“Log4Shell”的高危...
在源码层面,log4j-1.2.15版本主要包含以下几个关键组件: 1. **Logger**:这是日志系统的心脏,负责接收日志事件并决定是否需要进一步处理。每个类都可以拥有一个独立的Logger实例,便于分类记录日志。 2. **...
log4j:ERROR Failed to rename错误解决办法,修改源码里的DailyRollingFileAppender类,用此jar包就不会再出现ERROR Failed to rename的错误了
log4j各个版本源码 1.2.8 1.2.15 1.2.17 2.3 这四个版本的源码 Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录...
6. **apache-log4j-2.14.1-bin.zip**: 这可能是Log4j的完整二进制包,除了log4j.jar之外,还可能包含其他相关文件,如配置示例、文档、源码等。解压这个文件可以获取更多关于Log4j的资源,便于深入学习和调试。 综...
首先,我们要理解Log4j的核心组件。Log4j主要包括三个关键部分:Logger(日志器)、Appender(输出器)和Layout(布局)。Logger负责生成日志事件,Appender决定这些事件如何被输出(例如,写入文件、发送邮件或显示...
总结,`log4j-jsonlayout`是一个用于Log4j1的日志布局组件,它将日志信息转换为JSON格式,增强了日志的可读性和机器可处理性,适应于现代日志管理和分析的需求。理解和运用JSONLayout有助于提升日志系统的效能和开发...
`log4j扩展说明.doc`可能包含了关于如何进行`log4j`二次开发的详细步骤和注意事项,`log4j-1.2.8.jar`是`log4j`的旧版本库,可能用于参考或兼容性测试,而`loggerCenter.rar`可能是日志中心服务的源码或配置文件,...