Overview
Log4j is a very convenient and powerful open-source log project. After simple configuration, it can achieve quite good results.
One hot brain decides to read the source code of log4j. Its original intention is to read the source code to improve the ability to write code.
The core concepts of log4j can be divided:
The logger log receiver is used by programmers to record logs in their own code in the form of logger. Error.
Append log writer writes the log information received by logger to various devices, such as files and the console.
Layout log formatter first formats the input logs and then outputs them.
Log4j divides logs into several levels, from low to high: Debug <info <warn <error <fatal.
If low-level logs can be output, logs with a higher level can also be output.
UML
The first time I painted UML, I was a little nervous...
The main purpose is to use a UML diagram to reflect the logger's organizational structure and the relationship between logger and other components. Therefore, many factors are ignored.
Omitted. The subsequent descriptions will be compared with this figure.
Logger
Logger inherits from category, and in category, it indicates that
This class has been deprecated andreplaced by the {@link Logger} <em>subclass</em></b></font>. Itwill be kept around to preserve backward compatibility until mid2003.
Name: As your identity, you can use name in the factory method to generate a new logger instance.
Level: Each logger has a Level Attribute. when outputting a log, the logger obtains the Level Attribute by getjavastivelevel ().
Valid level to determine whether the log can be output. The level judgment process will be described in detail later.
Parent: Each logger has a parent node. The parent-child relationship is generated in loggerrepository. It's because of the parent-child relationship.
Therefore, in the geteffectivelevel method, we traverse the parent node and find the first level not empty. That is
If the level of the current node is not explicitly specified, the level of the parent node is used.
You know, there is a public parent node rootlogger.
1 /** 2 Starting from this category, search the category hierarchy for a 3 non-null level and return it. Otherwise, return the level of the 4 root category. 5 6 <p>The Category class is designed so that this method executes as 7 quickly as possible. 8 */ 9 public10 Level getEffectiveLevel() {11 for(Category c = this; c != null; c=c.parent) {12 if(c.level != null) {13 return c.level;14 }15 }16 return null; // If reached will cause an NullPointerException.17 }View code
AAI: Each logger can be associated with multiple appender. The received logs are output to each appender in sequence. Logger will
Management proxy to appenderattachableimpl. For example, the addappender operation is actually handled by aII.
/** Add <code>newAppender</code> to the list of appenders of this Category instance. <p>If <code>newAppender</code> is already in the list of appenders, then it won‘t be added again. */ synchronized public void addAppender(Appender newAppender) { if(aai == null) { aai = new AppenderAttachableImpl(); } aai.addAppender(newAppender); repository.fireAddAppenderEvent(this, newAppender); }View code
In appenderattachableimpl, only appender is added to vector.
/** Attach an appender. If the appender is already in the list in won‘t be added again. */ public void addAppender(Appender newAppender) { // Null values for newAppender parameter are strictly forbidden. if(newAppender == null) { return; } if(appenderList == null) { appenderList = new Vector(1); } if(!appenderList.contains(newAppender)) { appenderList.addElement(newAppender); } }View code
Debug: Similarly, info, error, and so on are all user call logging methods. After determining the level, send the log to the appender for output.
After the log is converted to a loggingevent, callappenders is called to traverse the parent node. Each node calls appenderattachableimpl.
. In fact, in appenderattachableimpl, all appender associated with the current node are traversed and output in sequence.
public void debug(Object message) { if(repository.isDisabled(Level.DEBUG_INT)) { return; } if(Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel())) { forcedLog(FQCN, Level.DEBUG, message, null); } }View code
/** This method creates a new logging event and logs the event without further checks. */ protected void forcedLog(String fqcn, Priority level, Object message, Throwable t) { callAppenders(new LoggingEvent(fqcn, this, level, message, t)); }View code
public void callAppenders(LoggingEvent event) { int writes = 0; for(Category c = this; c != null; c=c.parent) { // Protected against simultaneous call to addAppender, removeAppender,... synchronized(c) { if(c.aai != null) { writes += c.aai.appendLoopOnAppenders(event); } if(!c.additive) { break; } } } if(writes == 0) { repository.emitNoAppenderWarning(this); } }View code
Loggerrepository
Loggerrepository is the repository for logger management. It provides the factory method getlogger (name) to create a logger instance. And all created
Is logically organized into a tree structure (the parent field of logger ). Each instance has a parent node. The loggerrepository package
Contains a rootlogger Member, which is the sharing ancestor node of all logger. It is precisely because of the existence of this tree structure that operations such as getjavastivelevel
And will traverse the parent node. When no special configuration is made for all child nodes, the attributes of the parent node (Level, outputer) are used ).
Loggerrepository also has the Level Attribute. When logging, you first determine whether the level exceeds the level of the warehouse. This attribute is all by default.
public void debug(Object message) { if(repository.isDisabled(Level.DEBUG_INT)) { return; }...View code
public boolean isDisabled(int level) { return thresholdInt > level; }View code
Appender
Logger converts the received logs into loggingevents and submits them to the proxy appenderattachableimpl for output through the callappender method.
Appenderattachableimpl, after traversing all associated appender, calls its doappender Method for output in sequence. And every appender
There is also a concept of priority, and other levels are the same as those of logger. Therefore, when the appender executes the output, it is also necessary to determine whether the level meets the requirements.
Before the appender settings. In addition, the appender provides some filters to further control the output (it should be a simple responsibility chain mode ).
public synchronized void doAppend(LoggingEvent event) { if(closed) { LogLog.error("Attempted to append to closed appender named ["+name+"]."); return; } if(!isAsSevereAsThreshold(event.getLevel())) { return; } Filter f = this.headFilter; FILTER_LOOP: while(f != null) { switch(f.decide(event)) { case Filter.DENY: return; case Filter.ACCEPT: break FILTER_LOOP; case Filter.NEUTRAL: f = f.getNext(); } } this.append(event); }View code
Of course, you also need to format the output through layout. Format (...) before outputting.
Logmagager
This is a user interface that is not shown in the above UML diagram. It mainly provides some factories and some default configurations. For example, the default repository.
At the same time, some factory methods, such as getlogger, are also provided. The content will only be transferred to other modules for implementation.
static { // By default we use a DefaultRepositorySelector which always returns ‘h‘. Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG)); repositorySelector = new DefaultRepositorySelector(h);}View code
Therefore, you can use logger using logmanager and testmain. Class to convert it to name.
public static Logger LOG = LogManager.getLogger(TestMain.class);
View code
Summary
When reading the source code, I only had some knowledge about the big framework, such as the Implementation Details of appender and layout. Although log4j is powerful, it is only a record.
Logs. The source code is not complex. If you are interested, read the logs once. For me, this is just the beginning of reading the source code. Hope to learn more in the future
Excellent Design.
Logger for log4j source code reading (1)