log4j2(四) - 日誌位置是怎麼獲取到的?有什麼影響?

日誌的位置信息包含哪些?

  • %C or $class:類名
  • %F or %file:文件名
  • %L or %line:打印日誌的方法所在文件的行數
  • %M or %method:打印日誌的方法名
  • %l or %location:包含以上的位置信息

打印日誌位置有什麼影響?

官方文檔上強調了三遍,位置使用要慎用慎用。相比於不使用位置信息:

  • 對於同步日誌來說,速度要慢3~5倍
  • 對於異步日誌來說,速度要慢30~100

日誌位置是怎麼獲取到的?

提示:includeLocation必須得設置爲true

以AsyncLogger爲例, 從處理日誌事件開始看起:

  • AsyncLoggerConfig
    protected void callAppenders(final LogEvent event) {
    	//填充屬性
        populateLazilyInitializedFields(event);
        ///把日誌事件入隊
        if (!delegate.tryEnqueue(event, this)) {
            handleQueueFull(event);
        }
    }
   
   private void populateLazilyInitializedFields(final LogEvent event) {
   		//這裏
        event.getSource();
        event.getThreadName();
    }
  • MutableLogEvent
    public StackTraceElement getSource() {
        if (source != null) {
            return source;
        }
        //檢查是否includeLocation爲true
        if (loggerFqcn == null || !includeLocation) {
            return null;
        }
        //獲取位置信息的步驟
        source = StackLocatorUtil.calcLocation(loggerFqcn);
        return source;
    }
  • StackLocatorUtil
    public static StackTraceElement calcLocation(final String fqcnOfLogger) {
        return stackLocator.calcLocation(fqcnOfLogger);
    }
  • StackLocator
public StackTraceElement calcLocation(final String fqcnOfLogger) {
        if (fqcnOfLogger == null) {
            return null;
        }
        // LOG4J2-1029 new Throwable().getStackTrace is faster than Thread.currentThread().getStackTrace().
        //這個就是獲取位置的真正方法了,通過創建一個異常來獲取棧信息
        final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
        StackTraceElement last = null;
        for (int i = stackTrace.length - 1; i > 0; i--) {
            final String className = stackTrace[i].getClassName();
            if (fqcnOfLogger.equals(className)) {
                return last;
            }
            last = stackTrace[i];
        }
        return null;
    }
  • StackTraceElement: 我們想要的位置信息
public final class StackTraceElement implements java.io.Serializable {
    // Normally initialized by VM (public constructor added in 1.5)
    private String declaringClass;
    private String methodName;
    private String fileName;
    private int    lineNumber;
    //省略部分代碼...
}
  • Throwable
public StackTraceElement[] getStackTrace() {
        return getOurStackTrace().clone();
    }

    private synchronized StackTraceElement[] getOurStackTrace() {
        //初始化棧信息
        if (stackTrace == UNASSIGNED_STACK ||
            (stackTrace == null && backtrace != null) /* Out of protocol state */) {
            int depth = getStackTraceDepth();
            stackTrace = new StackTraceElement[depth];
            //填充棧信息
            for (int i=0; i < depth; i++)
                stackTrace[i] = getStackTraceElement(i);
        } else if (stackTrace == null) {
            return UNASSIGNED_STACK;
        }
        return stackTrace;
    }
 	
 	//每一層的棧信息是通過本地方法來調用的
 	native StackTraceElement getStackTraceElement(int index);

小結

日誌位置以前是通過Thread.currentThread().getStackTrace()獲取的, 在2.5版本之後,採用new Throwable().getStackTrace()的方式來獲取,性能上有了一定的提升的。改動見jira, 裏邊有bench mark。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章