日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

log4j 源码解析_Log4j源码解析--框架流程+核心解析

發布時間:2025/3/20 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 log4j 源码解析_Log4j源码解析--框架流程+核心解析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

OK,現在我們來研究Log4j的源碼:

這篇博客有參照上善若水的博客,原文出處:http://www.blogjava.net/DLevin/archive/2012/06/28/381667.html。感謝作者的無私分享。

Log4J將寫日志功能抽象成七個核心類或者接口:Logger、LoggerRepository、Level、LoggingEvent、Appender、Layout、ObjectRender。

我們一個一個來看:

1,Logger用于對日志記錄行為的抽象,提供記錄不同級別日志的接口;public class Logger extends Category

{

// Logger繼承Category,Category也是一種日志類

}2,Appender是對記錄日志形式的抽象;

public interface Appender

{

// Appender抽象成了接口,然后主要的實現是WriterAppender,常用的ConsoleAppender,FileAppender都繼承了該類。

// 實際編碼中經常會遇到DailyRollingFileAppender,RollingFileAppender都繼承于FileAppender。

}3,Layout是對日志行格式的抽象;

public abstract class Layout implements OptionHandler

{

// Layout抽象成一個模板,比較常用的PatternLayout,HTMLLayout都是該類子類

}4,Level對日志級別的抽象;

public class Level extends Priority implements Serializable

{

// 該類封裝一系列日志等級的名字和數字,然后內容封裝多個等級的相關枚舉

public final static int INFO_INT = 20000;

private static final String INFO_NAME = "INFO";

final static public Level INFO = new Level(INFO_INT, INFO_NAME, 6);

}5,LoggingEvent是對一次日志記錄過程中所能取到信息的抽象;

public class LoggingEvent implements java.io.Serializable

{

// 該類定義了一堆堆屬性,封裝了所有的日志信息。

}6,LoggerRepository是Logger實例的容器

public interface LoggerRepository

{

// 常見的Hierarchy就是該接口實現,里面封裝了框架一堆默認配置,還有Logger工廠。

// 可以理解該類就是事件源,該類內部封裝了以系列的事件

}7,ObjectRender是對日志實例的解析接口,它們主要提供了一種擴展支持。

public interface ObjectRenderer

{

/**

* @創建時間: 2016年2月25日

* @相關參數: @param o

* @相關參數: @return

* @功能描述: 解析日志對象,默認實現返回toString()

*/

public String doRender(Object o);

}

OK,現在介紹完了Log4j核心類了,現在我們來研究下Log4j的實際運行情況。

暫時不涉及Logger核心類的初始化,簡單的一次記錄日志過程的序列圖如下:

關于上圖的解釋:

獲取Logger實例->判斷Logger實例對應的日志記錄級別是否要比請求的級別低->若是調用forceLog記錄日志->創建LoggingEvent實例->將LoggingEvent實例傳遞給Appender->Appender調用Layout實例格式化日志消息->Appender將格式化后的日志信息寫入該Appender對應的日志輸出中。

OK,現在我們在輸出日志到某個指定位置處打個斷點,看下eclipse中方法的調用棧。protected void subAppend(LoggingEvent event)

{

// layout格式化日志事件,然后appender輸出日志

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

}

具體調用如下:

我們從我們自己寫的bug()方法來開始一步一步走:

1,我們自己寫的測試類中輸出日志:public void logTest()

{

log.debug("debug()。。。");

}2,Category類中debug方法,輸出之前先判斷了下日志級別:

public void debug(Object message)

{

if (repository.isDisabled(Level.DEBUG_INT))

{

return;

}

if (Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel()))

{

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

}

}isDisabled()方法如下:

public boolean isDisabled(int level)

{

return thresholdInt > level;

}3,創建日志事件 LoggingEvent,傳遞給AppenderAttachableImpl

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

{

LoggingEvent loggingEvent = new LoggingEvent(fqcn, this, level, message, t);

callAppenders(loggingEvent);

}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);

}

}

4,AppenderAttachableImpl處理LoggingEvent事件。這里可能有多個appender,用appenderList來封裝。

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,對應的appender來處理日志。

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);

}

public void append(LoggingEvent event)

{

if (!checkEntryConditions())

{

return;

}

subAppend(event);

}protected void subAppend(LoggingEvent event)

{

// layout格式化日志事件,然后appender輸出日志

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

if (layout.ignoresThrowable())

{

String[] s = event.getThrowableStrRep();

if (s != null)

{

int len = s.length;

for (int i = 0; i < len; i++)

{

this.qw.write(s[i]);

this.qw.write(Layout.LINE_SEP);

}

}

}

if (shouldFlush(event))

{

this.qw.flush();

}

}6,使用特定的日志格式化器layout格式化日志:

public String format(LoggingEvent event)

{

// Reset working stringbuffer

if (sbuf.capacity() > MAX_CAPACITY)

{

sbuf = new StringBuffer(BUF_SIZE);

}

else

{

sbuf.setLength(0);

}

PatternConverter c = head;

while (c != null)

{

c.format(sbuf, event);

c = c.next;

}

return sbuf.toString();

}7,appender輸出日志到特定的輸出位置:

public void write(String string)

{

try

{

out.write(string);

count += string.length();

}

catch (IOException e)

{

errorHandler.error("Write failure.", e, ErrorCode.WRITE_FAILURE);

}

}

OK,上面的過程不涉及Logger的初始化過程,我們是在使用Log4j初始化日志框架的時候,第一行代碼就是獲取靜態常量log,代碼如下:

public static Logger log = Logger.getLogger(Log4jTest.class);也就是說項目在啟動時就加載log到我們的項目中了,具體的加載過程源碼如下,log4j這里使用了一個工廠,然后用Hashtable來裝各個Logger,同時保持單例。

public static Logger getLogger(Class clazz)

{

return LogManager.getLogger(clazz.getName());

}public static Logger getLogger(final String name)

{

// Delegate the actual manufacturing of the logger to the logger repository.

return getLoggerRepository().getLogger(name);

}public Logger getLogger(String name)

{

return getLogger(name, defaultFactory);

}public Logger getLogger(String name, LoggerFactory factory)

{

CategoryKey key = new CategoryKey(name);

Logger logger;

synchronized (ht)

{

Object o = ht.get(key);

if (o == null)

{

logger = factory.makeNewLoggerInstance(name);

logger.setHierarchy(this);

ht.put(key, logger);

updateParents(logger);

return logger;

}

else if (o instanceof Logger)

{

return (Logger) o;

}

else if (o instanceof ProvisionNode)

{

// System.out.println("("+name+") ht.get(this) returned ProvisionNode");

logger = factory.makeNewLoggerInstance(name);

logger.setHierarchy(this);

ht.put(key, logger);

updateChildren((ProvisionNode) o, logger);

updateParents(logger);

return logger;

}

else

{

// It should be impossible to arrive here

return null; // but let's keep the compiler happy.

}

}

}

涉及Logger的初始化過程,詳細的一點的框架序列圖如下:

認真的看懂上面的流程圖,建議在框架最后一步打一個斷點,然后從頭到尾調試一遍代碼。個人覺得這也是最合理最有效的閱讀框架源碼的方法。OK,下幾篇博客我轉載上善若水的幾篇源碼帖,他已經整理的很詳細了。時間原因我自己就不整理了。

總結

以上是生活随笔為你收集整理的log4j 源码解析_Log4j源码解析--框架流程+核心解析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。