源碼基於logback 1.1.7app
本文將分析logger打印日誌功能的源碼,好比logger.info("hi"):oop
//ch.qos.logback.classic.Logger public void info(String msg) { filterAndLog_0_Or3Plus(FQCN, null, Level.INFO, msg, null, null); } /** * The next methods are not merged into one because of the time we gain by not * creating a new Object[] with the params. This reduces the cost of not * logging by about 20 nanoseconds. */ private void filterAndLog_0_Or3Plus(final String localFQCN, final Marker marker, final Level level, final String msg, final Object[] params,final Throwable t) { final FilterReply decision = loggerContext.getTurboFilterChainDecision_0_3OrMore(marker, this, level, msg, params, t); if (decision == FilterReply.NEUTRAL) { if (effectiveLevelInt > level.levelInt) { return; } } else if (decision == FilterReply.DENY) { return; } //構建loggingEvent並進行日誌輸出處理 buildLoggingEventAndAppend(localFQCN, marker, level, msg, params, t); } private void buildLoggingEventAndAppend(final String localFQCN, final Marker marker, final Level level, final String msg, final Object[] params,final Throwable t) { //構建loggingEvent對象,主要設置日誌級別以及日誌內容。 LoggingEvent le = new LoggingEvent(localFQCN, this, level, msg, t, params); le.setMarker(marker); //執行logger下全部的appender callAppenders(le); } //首先獲取logger自己的appender list進行處理,處理完後判斷logger的additive屬性是否是true,false的當即中止,true的話 //獲得他的父級logger,好比com.logback.test.LoggerTest對應的logger的父級logger是com.logback.test對應的logger。 //而後使用他的父級logger的appender list進行處理,處理完後並重復以前的邏輯,直到root或則additive爲false。 public void callAppenders(ILoggingEvent event) { int writes = 0; for (Logger l = this; l != null; l = l.parent) { writes += l.appendLoopOnAppenders(event); if (!l.additive) { break; } } // No appenders in hierarchy if (writes == 0) { loggerContext.noAppenderDefinedWarning(this); } }
接下來分析Logger的appendLoopOnAppenders方法:ui
private int appendLoopOnAppenders(ILoggingEvent event) { if (aai != null) { return aai.appendLoopOnAppenders(event); } else { return 0; } } //AppenderAttachableImpl public int appendLoopOnAppenders(E e) { int size = 0; //appenderList是該logger的全部的appender,而後循環調用appender的doAppend方法 for (Appender<E> appender : appenderList) { appender.doAppend(e); size++; } return size; }
下面咱們用RollingFileAppender的doAppend方法來分析下:this
public void doAppend(E eventObject) { // WARNING: The guard check MUST be the first statement in the // doAppend() method. // prevent re-entry. if (Boolean.TRUE.equals(guard.get())) { return; } try { guard.set(Boolean.TRUE); if (!this.started) { if (statusRepeatCount++ < ALLOWED_REPEATS) { addStatus(new WarnStatus("Attempted to append to non started appender [" + name + "].", this)); } return; } if (getFilterChainDecision(eventObject) == FilterReply.DENY) { return; } // ok, we now invoke derived class' implementation of append //核心,下面分析 this.append(eventObject); } catch (Exception e) { if (exceptionCount++ < ALLOWED_REPEATS) { addError("Appender [" + name + "] failed to append.", e); } } finally { guard.set(Boolean.FALSE); } } //OutputStreamAppender protected void append(E eventObject) { if (!isStarted()) { return; } subAppend(eventObject); } //RollingFileAppender protected void subAppend(E event) { // The roll-over check must precede actual writing. This is the // only correct behavior for time driven triggers. // We need to synchronize on triggeringPolicy so that only one rollover // occurs at a time synchronized (triggeringPolicy) { //判斷是否觸發trigger policy,好比基於時間策略來歸檔日誌的,會判斷當前時間是否是大於下次檢測的時間戳, //若是大於則會設置下次檢測時間戳進行日誌歸檔。 if (triggeringPolicy.isTriggeringEvent(currentlyActiveFile, event)) { //假如輸出日誌文件未info.log,這裏便會關閉info.log輸出流,而後對他進行歸檔,好比info.2017.06.08.log, //最後從新建立info.log,打開輸出流,等待後續日誌寫入 rollover(); } } //調用OutputStreamAppender的subAppend方法 super.subAppend(event); } //OutputStreamAppender protected void subAppend(E event) { if (!isStarted()) { return; } try { // this step avoids LBCLASSIC-139 if (event instanceof DeferredProcessingAware) { ((DeferredProcessingAware) event).prepareForDeferredProcessing(); } // the synchronization prevents the OutputStream from being closed while we // are writing. It also prevents multiple threads from entering the same // converter. Converters assume that they are in a synchronized block. //正如上面所說,這裏加鎖是爲了保護當咱們正在寫的時候輸出流被關閉,還有就是防止多個線程進入操做 lock.lock(); try { //輸出日誌 writeOut(event); } finally { lock.unlock(); } } catch (IOException ioe) { // as soon as an exception occurs, move to non-started state // and add a single ErrorStatus to the SM. this.started = false; addStatus(new ErrorStatus("IO failure in appender", this, ioe)); } } protected void writeOut(E event) throws IOException { this.encoder.doEncode(event); } //LayoutWrappingEncoder public void doEncode(E event) throws IOException { //根據encoder partten轉換msg爲真正的輸出內容 String txt = layout.doLayout(event); //轉化成byte而後輸出到日誌文件中 outputStream.write(convertToBytes(txt)); if (immediateFlush) outputStream.flush(); }