草庐IT

鏃ュ織瀵艰嚧绾跨▼Block鐨勮繖浜涘潙锛屼綘涓嶅緱涓嶉槻

浣滆€咃細 缇庡洟鎶€鏈洟闃?/a> 2023-03-28 原文

浣滆€咃細蹇楁磱 闄堣秴 鏉庢晱绛?/p>

鐮斿彂浜哄憳鍦ㄩ」鐩紑鍙戜腑涓嶅彲閬垮厤鍦拌浣跨敤鏃ュ織锛岄€氳繃瀹冩潵璁板綍淇℃伅鍜屾帓鏌ラ棶棰樸€侫pache Log4j2鎻愪緵浜嗙伒娲讳笖寮哄ぇ鐨勬棩蹇楁鏋讹紝铏界劧涓婃墜姣旇緝蹇紝浣嗙◢鏈変笉鎱庝篃闈炲父瀹规槗韪┾€滃潙鈥濄€?/p>

1. 鍓嶈█

鏃ュ織瀵圭▼搴忕殑閲嶈鎬т笉瑷€鑰屽柣銆傚畠寰堚€滃ぇ鈥濓紝鎴戜滑鍦ㄩ」鐩腑缁忓父閫氳繃鏃ュ織鏉ヨ褰曚俊鎭拰鎺掓煡闂锛岀浉鍏充唬鐮侀殢澶勫彲瑙併€傚畠涔熷緢鈥滃皬鈥濓紝浣滀负杈呭姪宸ュ叿锛屾棩蹇椾娇鐢ㄧ畝鍗曘€佷笂鎵嬪揩锛屾垜浠€氬父涓嶄細鑺辫垂杩囧绮惧姏鑰楀湪鏃ュ織涓娿€備絾鐪嬩技涓嶈捣鐪肩殑鏃ュ織涔熼殣钘忕潃鍚勭鍚勬牱鐨勨€滃潙鈥濓紝濡傛灉浣跨敤涓嶅綋锛屽畠涓嶄粎涓嶈兘甯姪鎴戜滑锛屽弽鑰岃繕鍙兘闄嶄綆鏈嶅姟鎬ц兘锛岀敋鑷虫嫋鍨垜浠殑鏈嶅姟銆?/p>

鏃ュ織瀵艰嚧绾跨▼Block鐨勯棶棰橈紝鐩镐俊浣犳垨璁稿凡缁忛亣鍒拌繃锛屽姝ゅ簲璇ユ繁鏈変綋浼氾紱鎴栬浣犺繕娌¢亣鍒拌繃锛屼絾涓嶄唬琛ㄦ病鏈夐棶棰橈紝鍙槸鍙兘杩樻病鏈夎Е鍙戣€屽凡銆傛湰鏂囦富瑕佷粙缁嶇編鍥㈢粺涓€API缃戝叧鏈嶅姟Shepherd锛堝弬瑙併€婄櫨浜胯妯PI缃戝叧鏈嶅姟Shepherd鐨勮璁′笌瀹炵幇銆嬩竴鏂囷級鍦ㄥ疄璺典腑鎵€韪╄繃鐨勫叧浜庢棩蹇楀鑷寸嚎绋婤lock鐨勯偅浜涒€滃潙鈥濓紝鐒跺悗鍐嶅垎浜竴浜涢伩鈥滃潙鈥濈粡楠屻€?/p>

2. 鑳屾櫙

API缃戝叧鏈嶅姟Shepherd鍩轰簬Java璇█寮€鍙戯紝浣跨敤涓氱晫澶у悕榧庨紟鐨凙pache Log4j2浣滀负涓昏鏃ュ織妗嗘灦锛屽悓鏃朵娇鐢ㄧ編鍥㈠唴閮ㄧ殑XMD-Log SDK鍜孲cribe-Log SDK瀵规棩蹇楀唴瀹硅繘琛屽鐞嗭紝鏃ュ織澶勭悊鏁翠綋娴佺▼濡備笅鍥?鎵€绀恒€備笟鍔℃墦鍗版棩蹇楁椂锛屾棩蹇楁鏋跺熀浜嶭ogger閰嶇疆鏉ュ喅瀹氭妸鏃ュ織浜ょ粰XMDFile澶勭悊杩樻槸Scribe澶勭悊銆傚叾涓紝XMDFile鏄疿MD-Log鍐呴儴鎻愪緵鐨勬棩蹇桝ppender鍚嶇О锛岃礋璐h緭鍑烘棩蹇楀埌鏈湴纾佺洏锛孲cribe鏄疭cribe-Log鍐呴儴鎻愪緵鐨勬棩蹇桝ppender鍚嶇О锛岃礋璐d笂鎶ユ棩蹇楀埌杩滅▼鏃ュ織涓績銆?/p>

鍥? 鏃ュ織澶勭悊娴佺▼绀烘剰鍥?/p>

闅忕潃涓氬姟鐨勫揩閫熷闀匡紝鏃ュ織瀵艰嚧鐨勭嚎绋婤lock闂鎰堝彂棰戠箒銆傛瘮濡傝皟鐢ㄥ悗绔疪PC鏈嶅姟瓒呮椂锛屽鑷磋皟鐢ㄦ柟澶ч噺绾跨▼Block锛涘啀姣斿锛屼笟鍔″唴閮ㄨ緭鍑哄紓甯告棩蹇楀鑷存湇鍔″ぇ閲忕嚎绋婤lock绛夛紝杩欎簺闂涓ラ噸褰卞搷鐫€鏈嶅姟鐨勭ǔ瀹氭€с€傚洜姝わ紝鎴戜滑缁撳悎椤圭洰鍦ㄨ繃鍘讳竴娈垫椂闂存毚闇插嚭鏉ョ殑鍚勭鐢变簬鏃ュ織瀵艰嚧鐨勭嚎绋婤lock闂锛屽鏃ュ織妗嗘灦瀛樺湪鐨勭ǔ瀹氭€ч闄╁洜绱犺繘琛屼簡褰诲簳鐨勬帓鏌ュ拰淇锛屽苟鍦ㄧ嚎涓嬨€佺嚎涓婄幆澧冭繘琛屽叏鏂逛綅楠岃瘉銆傚湪姝よ繃绋嬩腑锛屾垜浠€荤粨浜嗕竴浜涙棩蹇椾娇鐢ㄧ浉鍏崇殑瀹炶返缁忛獙锛屽笇鏈涘垎浜粰澶у銆?/p>

鍦ㄨ繘鍏ユ鏂囧墠锛岄鍏堜粙缁嶉」鐩綋鏃剁殑杩愯鐜鍜屾棩蹇楃浉鍏抽厤缃俊鎭€?/p>

  • JDK鐗堟湰
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)
  • 鏃ュ織渚濊禆鐗堟湰
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.7</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.7</version>
</dependency>
  • 鏃ュ織閰嶇疆鏂囦欢
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="warn">
<appenders>
<Console name="Console" target="SYSTEM_OUT" follow="true">
<PatternLayout pattern="%d{yyyy/MM/dd HH:mm:ss.SSS} %t [%p] %c{1} (%F:%L) %msg%n" />
</Console>

<XMDFile name="ShepherdLog" fileName="shepherd.log"/>

<!--XMDFile寮傛纾佺洏鏃ュ織閰嶇疆绀轰緥-->
<!--榛樿鎸夊ぉ&鎸?12M鏂囦欢澶у皬鍒囧垎鏃ュ織锛岄粯璁ゆ渶澶氫繚鐣?0涓棩蹇楁枃浠躲€?->
<!--娉ㄦ剰锛歠ileName鍓嶄細鑷姩澧炲姞鏂囦欢璺緞锛屽彧閰嶇疆鏂囦欢鍚嶅嵆鍙?->
<XMDFile name="LocalServiceLog" fileName="request.log"/>

<Scribe name="LogCenterSync">
<!-- 鍦ㄦ寚瀹氭棩蹇楀悕鏂归潰锛宻cribeCategory 鍜?appkey 涓よ€呰嚦灏戝瓨鍦ㄤ竴绉嶏紝涓?scribeCategory 楂樹簬 appkey銆?->
<!-- <Property name="scribeCategory">data_update_test_lc</Property> -->
<LcLayout/>
</Scribe>
<Async name="LogCenterAsync" blocking="false">
<AppenderRef ref="LogCenterSync"/>
</Async>
</appenders>

<loggers>
<AsyncLogger name="com.sankuai.shepherd" level="info" additivity="false">
<AppenderRef ref="ShepherdLog" level="warn"/>
<AppenderRef ref="LogCenterAsync" level="info"/>
</AsyncLogger>

<root level="info">
<!--Console鏃ュ織鏄悓姝ャ€侀樆濉炵殑锛屾帹鑽愬彧鍦ㄦ湰鍦拌皟璇曟椂浣跨敤锛岀嚎涓婂皢璇ラ厤缃幓鎺?->
<!--appender-ref ref="Console" /-->
<appender-ref ref="LocalServiceLog"/>
<appender-ref ref="LogCenterAsync"/>
</root>
</loggers>
</configuration>

3. 韪╄繃鐨勫潙

鏈珷鑺備富瑕佽褰曢」鐩繃鍘讳竴娈垫椂闂达紝鎴戜滑鎵€閬囧埌鐨勪竴绯诲垪鏃ュ織瀵艰嚧鐨勭嚎绋婤lock闂锛屽苟閫愪釜娣卞叆鍒嗘瀽闂鏍瑰洜銆?/span>

3.1 鏃ュ織闃熷垪婊″鑷寸嚎绋婤lock

3.1.1 闂鐜板満

鏀跺埌鈥渏vm.thread.blocked.count鈥濆憡璀﹀悗绔嬪埢閫氳繃鐩戞帶骞冲彴鏌ョ湅绾跨▼鐩戞帶鎸囨爣锛屽綋鏃剁殑绾跨▼鍫嗘爤濡傚浘2鍜屽浘3鎵€绀恒€?/span>

鍥? 绛夊緟閿佺殑Blocked绾跨▼鍫嗘爤

鍥? 鎸佹湁閿佺殑Runnable绾跨▼鍫嗘爤

浠嶣locked绾跨▼鍫嗘爤涓嶉毦鐪嬪嚭杩欒窡鏃ュ織鎵撳嵃鐩稿叧锛岃€屼笖鏄疘NFO绾у埆鐨勬棩蹇楋紝閬傚嵆鐧婚檰鏈哄櫒鏌ョ湅鏃ュ織鏄惁鏈夊紓鏍凤紝鍙戠幇褰撴椂鏃ュ織閲忛潪甯稿ぇ锛屽樊涓嶅姣忎袱鍒嗛挓灏卞啓婊′竴涓?00MB鐨勬棩蹇楁枃浠躲€傞偅澶ч噺杈撳嚭鏃ュ織鍜岀嚎绋婤lock涔嬮棿浼氭湁鎬庢牱鐨勫叧鑱斿憿锛熸帴涓嬫潵鏈珷鑺傚皢缁撳悎濡備笅鍥?鎵€绀虹殑璋冪敤閾捐矾娣卞叆鍒嗘瀽绾跨▼Block鐨勬牴鍥犮€?/span>

鍥? 鏃ュ織璋冪敤閾捐矾

3.1.2 涓轰粈涔堜細Block绾跨▼锛?/h4>

浠嶣locked绾跨▼鍫嗘爤鐫€鎵嬪垎鏋愶紝鏌ョ湅PrintStream鐩稿叧浠g爜鐗囨濡備笅鍥?鎵€绀猴紝鍙互鐪嬪埌琚樆濉炲湴鏂规湁synchronized鍚屾璋冪敤锛屽啀缁撳悎涓婃枃鍙戠幇姣忎袱鍒嗛挓鍐欐弧涓€涓?00MB鏃ュ織鏂囦欢鐨勭幇璞★紝鍒濇鎬€鐤戞槸鏃ュ織閲忚繃澶у鑷翠簡绾跨▼闃诲銆?/span>

鍥? PringStream浠g爜鐗囨

浣嗕笂杩扮寽娴嬩粛鏈変竴浜涘€煎緱鎺ㄦ暡鐨勫湴鏂癸細

  1. 濡傛灉浠呬粎鍥犱负鏃ュ織閲忚繃澶у氨瀵艰嚧绾跨▼Block锛岄偅鏃ュ織妗嗘灦涔熷お涓嶅牚閲嶇敤浜嗭紝鏍规湰娌℃硶鍦ㄩ珮骞跺彂銆侀珮鍚炲悙涓氬姟鍦烘櫙涓嬩娇鐢ㄣ€?/span>
  2. 鏃ュ織閰嶇疆閲屾槑鏄庢槸杈撳嚭鏃ュ織鍒版枃浠讹紝鎬庝箞浼氳緭鍑哄埌Console PrintStream锛?/span>

3.1.3 涓轰粈涔堜細杈撳嚭鍒癈onsole锛?/h4>

缁х画娌跨潃绾跨▼鍫嗘爤璋冪敤閾捐矾鍒嗘瀽锛屽彲浠ョ湅鍑烘槸AsyncAppender璋冪敤append鏂规硶杩藉姞鏃ュ織鏃跺彂鐢熶簡閿欒锛岀浉鍏充唬鐮佺墖娈靛涓嬶細

// org.apache.logging.log4j.core.appender.AsyncAppender

// 鍐呴儴缁存姢鐨勯樆濉為槦鍒楋紝闃熷垪澶у皬榛樿鏄?28
private final BlockingQueue<LogEvent> queue;

@Override
public void append(final LogEvent logEvent) {
if (!isStarted()) {
throw new IllegalStateException("AsyncAppender " + getName() + " is not active");
}
if (!Constants.FORMAT_MESSAGES_IN_BACKGROUND) { // LOG4J2-898: user may choose
logEvent.getMessage().getFormattedMessage(); // LOG4J2-763: ask message to freeze parameters
}
final Log4jLogEvent memento = Log4jLogEvent.createMemento(logEvent, includeLocation);
// 鏃ュ織浜嬩欢杞叆寮傛闃熷垪
if (!transfer(memento)) {
// 鎵ц鍒拌繖閲岃鏄庨槦鍒楁弧浜嗭紝鍏ラ槦澶辫触锛屾牴鎹槸鍚locking鎵ц鍏蜂綋绛栫暐
if (blocking) {
// 闃诲妯″紡锛岄€夊彇鐗瑰畾鐨勭瓥鐣ユ潵澶勭悊锛岀瓥鐣ュ彲鑳芥槸 "蹇界暐鏃ュ織"銆?鏃ュ織鍏ラ槦骞堕樆濉?銆?褰撳墠绾跨▼鎵撳嵃鏃ュ織"
// delegate to the event router (which may discard, enqueue and block, or log in current thread)
final EventRoute route = asyncQueueFullPolicy.getRoute(thread.getId(), memento.getLevel());
route.logMessage(this, memento);
} else {
// 闈為樆濉炴ā寮忥紝浜ょ敱 ErrorHandler 澶勭悊澶辫触鏃ュ織
error("Appender " + getName() + " is unable to write primary appenders. queue is full");
logToErrorAppenderIfNecessary(false, memento);
}
}
}

private boolean transfer(final LogEvent memento) {
return queue instanceof TransferQueue
? ((TransferQueue<LogEvent>) queue).tryTransfer(memento)
: queue.offer(memento);
}

public void error(final String msg) {
handler.error(msg);
}

AsyncAppender椤惧悕鎬濅箟鏄釜寮傛Appender锛岄噰鐢ㄥ紓姝ユ柟寮忓鐞嗘棩蹇楋紝鍦ㄥ叾鍐呴儴缁存姢浜嗕竴涓狟lockingQueue闃熷垪锛屾瘡娆″鐞嗘棩蹇楁椂锛岄兘鍏堝皾璇曟妸Log4jLogEvent浜嬩欢瀛樺叆闃熷垪涓紝鐒跺悗浜ょ敱鍚庡彴绾跨▼浠庨槦鍒椾腑鍙栧嚭浜嬩欢骞跺鐞嗭紙鎶婃棩蹇椾氦鐢盇syncAppender鎵€鍏宠仈鐨凙ppender澶勭悊锛夛紝浣嗛槦鍒楅暱搴︽€绘槸鏈夐檺鐨勶紝涓旈槦鍒楅粯璁ゅぇ灏忔槸128锛屽鏋滄棩蹇楅噺杩囧ぇ鎴栨棩蹇楀紓姝ョ嚎绋嬪鐞嗕笉鍙婃椂锛屽氨寰堝彲鑳藉鑷存棩蹇楅槦鍒楄鎵撴弧銆?/p>

褰撴棩蹇楅槦鍒楁弧鏃讹紝鏃ュ織妗嗘灦鍐呴儴鎻愪緵浜嗕袱绉嶅鐞嗘柟寮忥紝鍏蜂綋濡備笅锛?/p>

  • 濡傛灉blocking閰嶇疆涓簍rue锛屼細閫夋嫨鐩稿簲鐨勫鐞嗙瓥鐣ワ紝榛樿鏄疭YNCHRONOUS绛栫暐锛屽彲浠ュ湪log4j2.component.properties鏂囦欢涓紝閫氳繃log4j2.AsyncQueueFullPolicy鍙傛暟閰嶇疆鏃ュ織妗嗘灦鎻愪緵鐨勫叾浠栫瓥鐣ユ垨鑷畾涔夌瓥鐣ャ€?/li>
  1. DISCARD绛栫暐锛岀洿鎺ュ拷鐣ユ棩蹇椼€?/li>
  2. SYNCHRONOUS绛栫暐锛屽綋鍓嶇嚎绋嬬洿鎺ュ彂閫佹棩蹇楀埌Appender銆?/li>
  3. ENQUEUE绛栫暐锛屽己鍒堕樆濉炲叆闃熴€?/li>
  • 濡傛灉blocking閰嶇疆涓篺alse锛屽垯鐢盓rrorHandler鍜孍rrorAppender澶勭悊澶辫触鏃ュ織銆傛棩蹇楁鏋舵彁渚涗簡榛樿鐨凟rrorHandler瀹炵幇锛屽嵆DefaultErrorHandler锛岀洰鍓嶆殏涓嶆敮鎸佷笟鍔″湪XML銆丣SON绛夋棩蹇楅厤缃枃浠堕噷鑷畾涔塃rrorHandler銆傛棩蹇楁鏋堕粯璁や笉鎻愪緵ErrorAppender锛屼笟鍔″鏈夐渶瑕佸彲鍦╔ML銆丣SON绛夋棩蹇楅厤缃枃浠堕噷鑷畾涔塭rror-ref閰嶇疆銆?/li>

鍦ㄦ湰椤圭洰鐨勬棩蹇楅厤缃枃浠朵腑鍙互鐪嬪埌锛孉syncAppender璁剧疆浜哹locking涓篺alse锛屼笖娌℃湁閰嶇疆error-ref锛屼笅闈㈠叿浣撳垎鏋怐efaultErrorHandler銆?/p>

// org.apache.logging.log4j.core.appender.DefaultErrorHandler

private static final Logger LOGGER = StatusLogger.getLogger();

private static final int MAX_EXCEPTIONS = 3;

// 5min 鏃堕棿闂撮殧
private static final long EXCEPTION_INTERVAL = TimeUnit.MINUTES.toNanos(5);

private int exceptionCount = 0;

private long lastException = System.nanoTime() - EXCEPTION_INTERVAL - 1;

public void error(final String msg) {
final long current = System.nanoTime();
// 褰撳墠鏃堕棿璺濈涓婃寮傚父澶勭悊鏃堕棿闂撮殧瓒呰繃5min 鎴栬€呭紓甯稿鐞嗘暟灏忎簬3娆?/span>
if (current - lastException > EXCEPTION_INTERVAL || exceptionCount++ < MAX_EXCEPTIONS) {
// StatusLogger 璐熻矗澶勭悊
LOGGER.error(msg);
}
lastException = current;
}

DefaultErrorHandler鍐呴儴鍦ㄥ鐞嗗紓甯告棩蹇楁椂澧炲姞浜嗘潯浠堕檺鍒讹紝鍙湁涓嬭堪涓や釜鏉′欢浠讳竴婊¤冻鏃舵墠浼氬鐞嗭紝浠庤€岄伩鍏嶅ぇ閲忓紓甯告棩蹇楀鑷寸殑鎬ц兘闂銆?/p>

  • 涓ゆ潯鏃ュ織澶勭悊闂撮殧瓒呰繃5min銆?/li>
  • 寮傚父鏃ュ織鏁伴噺涓嶈秴杩?娆°€?/li>

浣嗛」鐩墍鐢ㄦ棩蹇楁鏋剁増鏈殑榛樿瀹炵幇鐪嬭捣鏉ュ瓨鍦ㄤ竴浜涗笉澶悎鐞嗙殑鍦版柟锛?/p>

  • lastException鐢ㄤ簬鏍囪涓婃寮傚父鐨勬椂闂存埑锛岃鍙橀噺鍙兘琚绾跨▼璁块棶锛屾棤娉曚繚璇佸绾跨▼鎯呭喌涓嬬殑绾跨▼瀹夊叏銆?/li>
  • exceptionCount鐢ㄤ簬缁熻寮傚父鏃ュ織娆℃暟锛岃鍙橀噺鍙兘琚绾跨▼璁块棶锛屾棤娉曚繚璇佸绾跨▼鎯呭喌涓嬬殑绾跨▼瀹夊叏銆?/li>

鎵€浠ワ紝鍦ㄥ绾跨▼鍦烘櫙涓嬶紝鍙兘鏈夊ぇ閲忓紓甯告棩蹇楀悓鏃惰DefaultErrorHandler澶勭悊锛屽甫鏉ョ嚎绋嬪畨鍏ㄩ棶棰樸€傚€煎緱涓€鎻愮殑鏄紝璇ラ棶棰樺凡鏈夌浉鍏矷ssue: DefaultErrorHandler can not share values across threads鍙嶉缁欑ぞ鍖猴紝骞跺湪2.15.0鐗堟湰涓繘琛屼簡淇銆?/p>

浠庝笂杩癉efaultErrorHandler浠g爜涓彲浠ョ湅鍒帮紝鐪熸璐熻矗澶勭悊鏃ュ織鐨勬槸StatusLogger锛岀户缁窡杩涗唬鐮佽繘鍏ogMessage鏂规硶锛屾柟娉曟墽琛岄€昏緫濡備笅锛?/p>

  • 濡傛灉StatusLogger鍐呴儴娉ㄥ唽浜哠tatusListener锛屽垯鐢卞搴旂殑StatusListener璐熻矗澶勭悊鏃ュ織銆?/li>
  • 鍚﹀垯鐢盨impleLogger璐熻矗澶勭悊鏃ュ織锛岀洿鎺ヨ緭鍑烘棩蹇楀埌System.err杈撳嚭娴併€?/li>
// org.apache.logging.log4j.status.StatusLogger

private static final StatusLogger STATUS_LOGGER = new StatusLogger(StatusLogger.class.getName(),
ParameterizedNoReferenceMessageFactory.INSTANCE);

// StatusListener
private final Collection<StatusListener> listeners = new CopyOnWriteArrayList<>();

private final SimpleLogger logger;

private StatusLogger(final String name, final MessageFactory messageFactory) {
super(name, messageFactory);
this.logger = new SimpleLogger("StatusLogger", Level.ERROR, false, true, false, false, Strings.EMPTY,
messageFactory, PROPS, System.err);
this.listenersLevel = Level.toLevel(DEFAULT_STATUS_LEVEL, Level.WARN).intLevel();
}

/**
* Retrieve the StatusLogger.
*
* @return The StatusLogger.
*/
public static StatusLogger getLogger() {
return STATUS_LOGGER;
}

@Override
public void logMessage(final String fqcn, final Level level, final Marker marker, final Message msg,
final Throwable t) {
StackTraceElement element = null;
if (fqcn != null) {
element = getStackTraceElement(fqcn, Thread.currentThread().getStackTrace());
}
final StatusData data = new StatusData(element, level, msg, t, null);
msgLock.lock();
try {
messages.add(data);
} finally {
msgLock.unlock();
}

if (listeners.size() > 0) {
// 濡傛灉绯荤粺娉ㄥ唽浜?listener锛岀敱 StatusConsoleListener 澶勭悊鏃ュ織
for (final StatusListener listener : listeners) {
if (data.getLevel().isMoreSpecificThan(listener.getStatusLevel())) {
listener.log(data);
}
}
} else {
// 鍚﹀垯鐢?SimpleLogger 澶勭悊鏃ュ織锛岀洿鎺ヨ緭鍑哄埌 System.err
logger.logMessage(fqcn, level, marker, msg, t);
}
}

浠庝笂杩癇locked绾跨▼鍫嗘爤鏉ョ湅锛屾槸StatusConsoleListener璐熻矗澶勭悊鏃ュ織锛岃€孲tatusConsoleListener鏄疭tatusListener鎺ュ彛鐨勫疄鐜扮被锛岄偅涔圫tatusConsoleListener鏄浣曡鍒涘缓鐨勶紵

3.1.4 StatusConsoleListener鏄€庝箞鏉ョ殑锛?/h4>

閫氬父鏉ヨ锛屾瘡涓」鐩兘浼氭湁涓€涓棩蹇楅厤缃枃浠讹紙濡俵og4j2.xml锛夛紝璇ラ厤缃搴擫og4j2鏃ュ織妗嗘灦涓殑Configuration鎺ュ彛锛屼笉鍚岀殑鏃ュ織閰嶇疆鏂囦欢鏍煎紡鏈変笉鍚岀殑瀹炵幇绫伙細

XmlConfiguration锛屽嵆XML鏍煎紡鏃ュ織閰嶇疆

JsonConfiguration锛屽嵆JSON鏍煎紡鏃ュ織閰嶇疆

XMDConfiguration锛屽嵆缇庡洟鍐呴儴鏃ュ織缁勪欢XMD-Log瀹氫箟鐨勬棩蹇楅厤缃紙XML鏍煎紡锛?/p>

......

log4j2.xml 绀轰緥閰嶇疆锛堜粎鍋氱ず渚嬶紝璇峰嬁瀹為檯椤圭洰涓娇鐢ㄨ閰嶇疆锛夈€?/p>

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug" name="RoutingTest">
<Properties>
<Property name="filename">target/rolling1/rollingtest-$${sd:type}.log</Property>
</Properties>
<ThresholdFilter level="debug"/>

<Appenders>
<Console name="STDOUT">
<PatternLayout pattern="%m%n"/>
<ThresholdFilter level="debug"/>
</Console>
<Routing name="Routing">
<Routes pattern="$${sd:type}">
<Route>
<RollingFile name="Rolling-${sd:type}" fileName="${filename}"
filePattern="target/rolling1/test1-${sd:type}.%i.log.gz">
<PatternLayout>
<pattern>%d %p %c{1.} [%t] %m%n</pattern>
</PatternLayout>
<SizeBasedTriggeringPolicy size="500" />
</RollingFile>
</Route>
<Route ref="STDOUT" key="Audit"/>
</Routes>
</Routing>
</Appenders>

<Loggers>
<Logger name="EventLogger" level="info" additivity="false">
<AppenderRef ref="Routing"/>
</Logger>

<Root level="error">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>

Log4j2鍦ㄥ惎鍔ㄦ椂浼氬姞杞藉苟瑙f瀽log4j2.xml閰嶇疆鏂囦欢锛岀敱瀵瑰簲鐨凜onfigurationFactory鍒涘缓鍏蜂綋Configuration瀹炰緥銆?/p>

// org.apache.logging.log4j.core.config.xml.XmlConfiguration

public XmlConfiguration(final LoggerContext loggerContext, final ConfigurationSource configSource) {
super(loggerContext, configSource);
final File configFile = configSource.getFile();
byte[] buffer = null;

try {
final InputStream configStream = configSource.getInputStream();
try {
buffer = toByteArray(configStream);
} finally {
Closer.closeSilently(configStream);
}
final InputSource source = new InputSource(new ByteArrayInputStream(buffer));
source.setSystemId(configSource.getLocation());
final DocumentBuilder documentBuilder = newDocumentBuilder(true);
Document document;
try {
// 瑙f瀽 xml 閰嶇疆鏂囦欢
document = documentBuilder.parse(source);
} catch (final Exception e) {
// LOG4J2-1127
final Throwable throwable = Throwables.getRootCause(e);
if (throwable instanceof UnsupportedOperationException) {
LOGGER.warn(
"The DocumentBuilder {} does not support an operation: {}."
+ "Trying again without XInclude...",
documentBuilder, e);
document = newDocumentBuilder(false).parse(source);
} else {
throw e;
}
}
rootElement = document.getDocumentElement();
// 澶勭悊鏍硅妭鐐瑰睘鎬ч厤缃紝鍗?<Configuration></Configuration> 鑺傜偣
final Map<String, String> attrs = processAttributes(rootNode, rootElement);
// 鍒涘缓 StatusConfiguration
final StatusConfiguration statusConfig = new StatusConfiguration().withVerboseClasses(VERBOSE_CLASSES)
.withStatus(getDefaultStatus());
for (final Map.Entry<String, String> entry : attrs.entrySet()) {
final String key = entry.getKey();
final String value = getStrSubstitutor().replace(entry.getValue());
// 鏍规嵁閰嶇疆鏂囦欢涓殑 status 灞炴€у€硷紝鏉ヨ缃?StatusConfiguration 鐨?status level
if ("status".equalsIgnoreCase(key)) {
statusConfig.withStatus(value);
// 鏍规嵁閰嶇疆鏂囦欢涓殑 dest 灞炴€у€硷紝鏉ヨ缃?StatusConfiguration 鐨勬棩蹇楄緭鍑?destination
} else if ("dest".equalsIgnoreCase(key)) {
statusConfig.withDestination(value);
} else if ("shutdownHook".equalsIgnoreCase(key)) {
isShutdownHookEnabled = !"disable".equalsIgnoreCase(value);
} else if ("verbose".equalsIgnoreCase(key)) {
statusConfig.withVerbosity(value);
} else if ("packages".equalsIgnoreCase(key)) {
pluginPackages.addAll(Arrays.asList(value.split(Patterns.COMMA_SEPARATOR)));
} else if ("name".equalsIgnoreCase(key)) {
setName(value);
} else if ("strict".equalsIgnoreCase(key)) {
strict = Boolean.parseBoolean(value);
} else if ("schema".equalsIgnoreCase(key)) {
schemaResource = value;
} else if ("monitorInterval".equalsIgnoreCase(key)) {
final int intervalSeconds = Integer.parseInt(value);
if (intervalSeconds > 0) {
getWatchManager().setIntervalSeconds(intervalSeconds);
if (configFile != null) {
final FileWatcher watcher = new ConfiguratonFileWatcher(this, listeners);
getWatchManager().watchFile(configFile, watcher);
}
}
} else if ("advertiser".equalsIgnoreCase(key)) {
createAdvertiser(value, configSource, buffer, "text/xml");
}
}

// 鍒濆鍖?StatusConfiguration
statusConfig.initialize();
} catch (final SAXException | IOException | ParserConfigurationException e) {
LOGGER.error("Error parsing " + configSource.getLocation(), e);
}

if (getName() == null) {
setName(configSource.getLocation());
}

// 蹇界暐浠ヤ笅鍐呭
}
// org.apache.logging.log4j.core.config.status.StatusConfiguration

private static final PrintStream DEFAULT_STREAM = System.out;
private static final Level DEFAULT_STATUS = Level.ERROR;
private static final Verbosity DEFAULT_VERBOSITY = Verbosity.QUIET;

private final Collection<String> errorMessages = Collections.synchronizedCollection(new LinkedList<String>());
// StatusLogger
private final StatusLogger logger = StatusLogger.getLogger();

private volatile boolean initialized = false;

private PrintStream destination = DEFAULT_STREAM;
private Level status = DEFAULT_STATUS;
private Verbosity verbosity = DEFAULT_VERBOSITY;

public void initialize() {
if (!this.initialized) {
if (this.status == Level.OFF) {
this.initialized = true;
} else {
final boolean configured = configureExistingStatusConsoleListener();
if (!configured) {
// 娉ㄥ唽鏂?StatusConsoleListener
registerNewStatusConsoleListener();
}
migrateSavedLogMessages();
}
}
}

private boolean configureExistingStatusConsoleListener() {
boolean configured = false;
for (final StatusListener statusListener : this.logger.getListeners()) {
if (statusListener instanceof StatusConsoleListener) {
final StatusConsoleListener listener = (StatusConsoleListener) statusListener;
// StatusConsoleListener 鐨?level 浠?StatusConfiguration 鐨?status 涓哄噯
listener.setLevel(this.status);
this.logger.updateListenerLevel(this.status);
if (this.verbosity == Verbosity.QUIET) {
listener.setFilters(this.verboseClasses);
}
configured = true;
}
}
return configured;
}


private void registerNewStatusConsoleListener() {
// 鍒涘缓 StatusConsoleListener锛岀骇鍒互 StatusConfiguration 涓哄噯
// 榛樿 status 鏄?DEFAULT_STATUS 鍗?ERROR
// 榛樿 destination 鏄?DEFAULT_STREAM 鍗?System.out
final StatusConsoleListener listener = new StatusConsoleListener(this.status, this.destination);
if (this.verbosity == Verbosity.QUIET) {
listener.setFilters(this.verboseClasses);
}
this.logger.registerListener(listener);
}
// org.apache.logging.log4j.status.StatusConsoleListener

private Level level = Level.FATAL; // 绾у埆
private String[] filters;
private final PrintStream stream; // 杈撳嚭娴?/span>

public StatusConsoleListener(final Level level, final PrintStream stream) {
if (stream == null) {
throw new IllegalArgumentException("You must provide a stream to use for this listener.");
}
this.level = level;
this.stream = stream;
}

浠mlConfiguration涓轰緥锛屽垎鏋愪笂杩版棩蹇楅厤缃В鏋愪唬鐮佺墖娈靛彲浠ュ緱鐭ワ紝鍒涘缓XmlConfiguration鏃讹紝浼氬厛鍒涘缓StatusConfiguration锛岄殢鍚庡湪鍒濆鍖朣tatusConfiguration鏃跺垱寤哄苟娉ㄥ唽StatusConsoleListener鍒癝tatusLogger鐨刲isteners涓紝鏃ュ織閰嶇疆鏂囦欢涓?lt;Configuration>鏍囩鐨勫睘鎬у€奸€氳繃XmlConfiguration->StatusConfiguration->StatusConsoleListener杩欐牱鐨勫叧绯婚摼璺渶缁堝奖鍝峉tatusConsoleListener鐨勮涓恒€傛棩蹇楅厤缃枃浠朵腑鐨?lt;Configuration>鏍囩鍙互閰嶇疆灞炴€у瓧娈碉紝閮ㄥ垎瀛楁濡備笅鎵€绀猴細

  • status锛屽彲閫夊€煎寘鎷琌FF銆丗ATAL銆丒RROR銆乄ARN銆両NFO銆丏EBUG銆乀RACE銆丄LL锛岃鍊煎喅瀹歋tatusConsoleListener绾у埆锛岄粯璁ゆ槸ERROR銆?/span>
  • dest锛屽彲閫夊€煎寘鎷琽ut銆乪rr銆佹爣鍑嗙殑URI璺緞锛岃鍊煎喅瀹歋tatusConsoleListener杈撳嚭娴佺洰鐨勫湴锛岄粯璁ゆ槸System.out銆?/span>

鍦ㄦ湰椤圭洰鐨勬棩蹇楅厤缃枃浠朵腑鍙互鐪嬪埌骞舵病鏈夎缃瓹onfiguration鐨刣est灞炴€у€硷紝鎵€浠ユ棩蹇楃洿鎺ヨ緭鍑哄埌System.out銆?/span>

3.1.5 StatusLogger鏈変粈涔堢敤锛?/h4>

涓婃枃鎻愬埌StatusConsoleListener鏄敞鍐屽湪StatusLogger涓紝StatusLogger鍦ㄤ氦鐢盨tatusListener澶勭悊鏃ュ織鍓嶏紝浼氬垽鏂棩蹇楃骇鍒紝濡傛灉绾у埆鏉′欢涓嶆弧瓒筹紝鍒欏拷鐣ユ鏃ュ織锛孲tatusConsoleListener鐨勬棩蹇楃骇鍒粯璁ゆ槸ERROR銆?/span>

// org.apache.logging.log4j.status.StatusLogger

@Override
public void logMessage(final String fqcn, final Level level, final Marker marker, final Message msg,
final Throwable t) {
StackTraceElement element = null;
if (fqcn != null) {
element = getStackTraceElement(fqcn, Thread.currentThread().getStackTrace());
}
final StatusData data = new StatusData(element, level, msg, t, null);
msgLock.lock();
try {
messages.add(data);
} finally {
msgLock.unlock();
}

// 绯荤粺娉ㄥ唽浜?listener锛岀敱 StatusConsoleListener 澶勭悊鏃ュ織
if (listeners.size() > 0) {
for (final StatusListener listener : listeners) {
// 姣旇緝褰撳墠鏃ュ織鐨?leve 鍜?listener 鐨?level
if (data.getLevel().isMoreSpecificThan(listener.getStatusLevel())) {
listener.log(data);
}
}
} else {
logger.logMessage(fqcn, level, marker, msg, t);
}
}

鎴戜滑鍥炲ご鍐嶆潵鐪嬩笅StatusLogger锛孲tatusLogger閲囩敤鍗曚緥妯″紡瀹炵幇锛屽畠杈撳嚭鏃ュ織鍒癈onsole锛堝System.out鎴朣ystem.err锛夛紝浠庝笂鏂囧垎鏋愬彲鐭ワ紝鍦ㄩ珮骞跺彂鍦烘櫙涓嬮潪甯稿鏄撳鑷寸嚎绋婤lock锛岄偅涔堝畠鐨勫瓨鍦ㄦ湁浠€涔堟剰涔夊憿锛?/p>

鐪嬪畼鏂逛粙缁嶅ぇ鎰忔槸璇达紝鍦ㄦ棩蹇楀垵濮嬪寲瀹屾垚鍓嶏紝涔熸湁鎵撳嵃鏃ュ織璋冭瘯鐨勯渶姹傦紝StatusLogger灏辨槸涓轰簡瑙e喅杩欎釜闂鑰岀敓銆?/p>

Troubleshooting tip for the impatient:

From log4j-2.9 onward, log4j2 will print all internal logging to the console if system property log4j2.debug is defined (with any or no value).

Prior to log4j-2.9, there are two places where internal logging can be controlled:

Before a configuration is found, status logger level can be controlled with system property org.apache.logging.log4j.simplelog.StatusLogger.level.

After a configuration is found, status logger level can be controlled in the configuration file with the "status" attribute, for example: <Configuration status="trace">.

Just as it is desirable to be able to diagnose problems in applications, it is frequently necessary to be able to diagnose problems in the logging configuration or in the configured components. Since logging has not been configured, "normal" logging cannot be used during initialization. In addition, normal logging within appenders could create infinite recursion which Log4j will detect and cause the recursive events to be ignored. To accomodate this need, the Log4j 2 API includes a StatusLogger.

3.1.6 闂灏忕粨

鏃ュ織閲忚繃澶у鑷碅syncAppender鏃ュ織闃熷垪琚墦婊★紝鏂扮殑鏃ュ織浜嬩欢鏃犳硶鍏ラ槦锛岃繘鑰岀敱ErrorHandler澶勭悊鏃ュ織锛屽悓鏃剁敱浜嶦rrorHandler瀛樺湪绾跨▼瀹夊叏闂锛屽鑷村ぇ閲忔棩蹇楄緭鍑哄埌浜咰onsole锛岃€孋onsole鍦ㄨ緭鍑烘棩蹇楀埌PrintStream杈撳嚭娴佹椂锛屽瓨鍦╯ynchronized鍚屾浠g爜鍧楋紝鎵€浠ュ湪楂樺苟鍙戝満鏅笅瀵艰嚧绾跨▼Block銆?/span>

3.2 AsyncAppender瀵艰嚧绾跨▼Block

3.2.1 闂鐜板満

鏀跺埌鈥渏vm.thread.blocked.count鈥濆憡璀﹀悗绔嬪埢閫氳繃鐩戞帶骞冲彴鏌ョ湅绾跨▼鐩戞帶鎸囨爣锛屽綋鏃剁殑绾跨▼鍫嗘爤濡備笅鍥?鍜屽浘7鎵€绀恒€?/span>

鍥? 绛夊緟閿佺殑Blocked绾跨▼鍫嗘爤

鍥? 鎸佹湁閿佺殑Runnable绾跨▼鍫嗘爤

浠嶣locked绾跨▼鍫嗘爤涓嶉毦鐪嬪嚭鏄窡鏃ュ織鎵撳嵃鐩稿叧锛岀敱浜庢槸ERROR绾у埆鏃ュ織锛屾煡鐪嬪叿浣撴姤閿欐棩蹇楋紝鍙戠幇鏈変袱绉嶄笟鍔″紓甯革紝鍒嗗埆濡備笅鍥?鍜屽浘9鎵€绀猴細

鍥? 涓氬姟寮傚父鍫嗘爤涓€

鍥? 涓氬姟寮傚父鍫嗘爤浜?/span>

杩欎簺涓氬姟寮傚父浼氭槸瀵艰嚧绾跨▼Block鐨勫箷鍚庡厓鍑跺悧锛熸帴涓嬫潵鏈珷鑺傚皢缁撳悎濡備笅鍥?0鎵€绀虹殑璋冪敤閾捐矾娣卞叆鍒嗘瀽绾跨▼Block鐨勬牴鍥犮€?/span>

鍥?0 鏃ュ織璋冪敤閾捐矾

3.2.2 涓轰粈涔堜細Block绾跨▼锛?/h4>

浠嶣locked绾跨▼鍫嗘爤涓彲浠ョ湅鍑猴紝绾跨▼闃诲鍦ㄧ被鍔犺浇娴佺▼涓婏紝鏌ョ湅WebAppClassLoader鐩稿叧浠g爜鐗囨濡備笅鍥?1鎵€绀猴紝鍙戠幇鍔犺浇绫绘椂纭疄浼氭牴鎹被鍚嶆潵鍔爏ynchronized鍚屾鍧楋紝鍥犳鍒濇鐚滄祴鏄被鍔犺浇瀵艰嚧绾跨▼Block銆?/span>

鍥?1 WebAppClassLoader浣嗕笂杩扮寽娴嬭繕鏈変竴浜涘€煎緱鎺ㄦ暡鐨勫湴鏂癸細

  1. 椤圭洰浠g爜閲屽彧鏄櫘閫氬湴杈撳嚭涓€鏉RROR鏃ュ織鑰屽凡锛屼负浣曚細瑙﹀彂绫诲姞杞斤紵
  2. 閫氬父鎯呭喌涓嬬被鍔犺浇鍑犱箮涓嶄細瑙﹀彂绾跨▼Block锛屼笉鐒朵竴涓」鐩鍔犺浇鎴愬崈涓婁竾涓被锛屽鏋滃洜涓哄姞杞界被灏卞鑷碆lock锛岄偅椤圭洰灏辨病娉曟甯歌繍琛屼簡銆?/span>

3.2.3 涓轰粈涔堜細瑙﹀彂绫诲姞杞斤紵

缁х画浠嶣locked绾跨▼鍫嗘爤鐫€鎵嬪垎鏋愶紝鏌ョ湅鍫嗘爤涓殑ThrowableProxy鐩稿叧浠g爜锛屽彂鐜板叾鏋勯€犲嚱鏁颁細閬嶅巻鏁翠釜寮傚父鍫嗘爤涓殑鎵€鏈夊爢鏍堝厓绱狅紝鏈€缁堣幏鍙栨墍鏈夊爢鏍堝厓绱犵被鎵€鍦ㄧ殑JAR鍚嶇О鍜岀増鏈俊鎭€傚叿浣撴祦绋嬪涓嬶細

  1. 棣栧厛鑾峰彇鍫嗘爤鍏冪礌鐨勭被鍚嶇О銆?/span>
  2. 鍐嶉€氳繃loadClass鐨勬柟寮忚幏鍙栧搴旂殑Class瀵硅薄銆?/span>
  3. 杩涗竴姝ヨ幏鍙栬绫绘墍鍦ㄧ殑JAR淇℃伅锛屼粠CodeSource涓幏鍙朖AR鍚嶇О锛屼粠Package涓幏鍙朖AR鐗堟湰銆?/span>
// org.apache.logging.log4j.core.impl.ThrowableProxy

private ThrowableProxy(final Throwable throwable, final Set<Throwable> visited) {
this.throwable = throwable;
this.name = throwable.getClass().getName();
this.message = throwable.getMessage();
this.localizedMessage = throwable.getLocalizedMessage();
final Map<String, CacheEntry> map = new HashMap<>();
final Stack<Class<?>> stack = ReflectionUtil.getCurrentStackTrace();
// 鑾峰彇鍫嗘爤鎵╁睍淇℃伅
this.extendedStackTrace = this.toExtendedStackTrace(stack, map, null, throwable.getStackTrace());
final Throwable throwableCause = throwable.getCause();
final Set<Throwable> causeVisited = new HashSet<>(1);
this.causeProxy = throwableCause == null ? null : new ThrowableProxy(throwable, stack, map, throwableCause,
visited, causeVisited);
this.suppressedProxies = this.toSuppressedProxies(throwable, visited);
}

ExtendedStackTraceElement[] toExtendedStackTrace(final Stack<Class<?>> stack, final Map<String, CacheEntry> map,
final StackTraceElement[] rootTrace,
final StackTraceElement[] stackTrace) {
int stackLength;
if (rootTrace != null) {
int rootIndex = rootTrace.length - 1;
int stackIndex = stackTrace.length - 1;
while (rootIndex >= 0 && stackIndex >= 0 && rootTrace[rootIndex].equals(stackTrace[stackIndex])) {
--rootIndex;
--stackIndex;
}
this.commonElementCount = stackTrace.length - 1 - stackIndex;
stackLength = stackIndex + 1;
} else {
this.commonElementCount = 0;
stackLength = stackTrace.length;
}
final ExtendedStackTraceElement[] extStackTrace = new ExtendedStackTraceElement[stackLength];
Class<?> clazz = stack.isEmpty() ? null : stack.peek();
ClassLoader lastLoader = null;
for (int i = stackLength - 1; i >= 0; --i) {
// 閬嶅巻 StackTraceElement
final StackTraceElement stackTraceElement = stackTrace[i];
// 鑾峰彇鍫嗘爤鍏冪礌瀵瑰簲鐨勭被鍚嶇О
final String className = stackTraceElement.getClassName();
// The stack returned from getCurrentStack may be missing entries for java.lang.reflect.Method.invoke()
// and its implementation. The Throwable might also contain stack entries that are no longer
// present as those methods have returned.
ExtendedClassInfo extClassInfo;
if (clazz != null && className.equals(clazz.getName())) {
final CacheEntry entry = this.toCacheEntry(stackTraceElement, clazz, true);
extClassInfo = entry.element;
lastLoader = entry.loader;
stack.pop();
clazz = stack.isEmpty() ? null : stack.peek();
} else {
// 瀵瑰姞杞借繃鐨?className 杩涜缂撳瓨锛岄伩鍏嶉噸澶嶅姞杞?/span>
final CacheEntry cacheEntry = map.get(className);
if (cacheEntry != null) {
final CacheEntry entry = cacheEntry;
extClassInfo = entry.element;
if (entry.loader != null) {
lastLoader = entry.loader;
}
} else {
// 閫氳繃鍔犺浇绫绘潵鑾峰彇绫荤殑鎵╁睍淇℃伅锛屽 location 鍜?version 绛?/span>
final CacheEntry entry = this.toCacheEntry(stackTraceElement,
// 鑾峰彇 Class 瀵硅薄
this.loadClass(lastLoader, className), false);
extClassInfo = entry.element;
map.put(stackTraceElement.toString(), entry);
if (entry.loader != null) {
lastLoader = entry.loader;
}
}
}
extStackTrace[i] = new ExtendedStackTraceElement(stackTraceElement, extClassInfo);
}
return extStackTrace;
}

/**
* Construct the CacheEntry from the Class's information.
*
* @param stackTraceElement The stack trace element
* @param callerClass The Class.
* @param exact True if the class was obtained via Reflection.getCallerClass.
* @return The CacheEntry.
*/
private CacheEntry toCacheEntry(final StackTraceElement stackTraceElement, final Class<?> callerClass,
final boolean exact) {
String location = "?";
String version = "?";
ClassLoader lastLoader = null;
if (callerClass != null) {
try {
// 鑾峰彇 jar 鏂囦欢淇℃伅
final CodeSource source = callerClass.getProtectionDomain().getCodeSource();
if (source != null) {
final URL locationURL = source.getLocation();
if (locationURL != null) {
final String str = locationURL.toString().replace('\\', '/');
int index = str.lastIndexOf("/");
if (index >= 0 && index == str.length() - 1) {
index = str.lastIndexOf("/", index - 1);
location = str.substring(index + 1);
} else {
location = str.substring(index + 1);
}
}
}
} catch (final Exception ex) {
// Ignore the exception.
}
// 鑾峰彇绫绘墍鍦?jar 鐗堟湰淇℃伅
final Package pkg = callerClass.getPackage();
if (pkg != null) {
final String ver = pkg.getImplementationVersion();
if (ver != null) {
version = ver;
}
}
lastLoader = callerClass.getClassLoader();
}
return new CacheEntry(new ExtendedClassInfo(exact, location, version), lastLoader);
}

浠庝笂杩颁唬鐮佷腑鍙互鐪嬪埌锛孴hrowableProxy#toExtendedStackTrace鏂规硶閫氳繃Map<String, CacheEntry>缂撳瓨褰撳墠鍫嗘爤鍏冪礌绫诲搴旂殑CacheEntry锛屾潵閬垮厤閲嶅瑙f瀽CacheEntry锛屼絾鏄敱浜嶮ap缂撳瓨put鎿嶄綔浣跨敤鐨刱ey鏉ヨ嚜浜嶴tackTraceElement.toString鏂规硶锛岃€実et鎿嶄綔浣跨敤鐨刱ey鍗存潵鑷簬StackTraceElement.getClassName鏂规硶锛屽嵆浣垮浜庡悓涓€涓猄tackTraceElement鑰岃█锛屽叾toString鍜実etClassName鏂规硶瀵瑰簲鐨勮繑鍥炵粨鏋滀篃涓嶄竴鏍凤紝鎵€浠ユmap褰㈠悓铏氳銆?/p>

// java.lang.StackTraceElement

public String getClassName() {
return declaringClass;
}

public String toString() {
return getClassName() + "." + methodName +
(isNativeMethod() ? "(Native Method)" :
(fileName != null && lineNumber >= 0 ?
"(" + fileName + ":" + lineNumber + ")" :
(fileName != null ? "("+fileName+")" : "(Unknown Source)")));

璇ラ棶棰樺凡鏈夌浉鍏矷ssue: fix the CacheEntry map in ThrowableProxy#toExtendedStackTrace to be put and gotten with same key鍙嶉缁欑ぞ鍖猴紝骞跺湪2.11.1鐗堟湰涓慨澶嶄簡璇ラ棶棰樸€傝櫧鐒堕€氳繃璁ゞet/put鏂规硶浣跨敤鍚屼竴涓猭ey鏉ヤ慨澶嶇紦瀛樼殑鏈夋晥鎬ч棶棰橈紝浣嗙敱浜嶵hrowableProxy瀵规瘡涓猅hrowable閮戒細鍒涘缓涓€涓叏鏂扮殑Map锛岃€屼笉鏄娇鐢ㄥ叏灞€Map锛屽洜姝ゅ叾缂撳瓨涔熶粎浠呭鍗曚釜Throwable鐢熸晥锛屼綔鐢ㄨ寖鍥撮潪甯告湁闄愶紝椋熶箣鏃犲懗锛屽純涔嬪彲鎯溿€?/p>

瑷€褰掓浼狅紝閫氬父鎯呭喌涓嬩竴涓被鍔犺浇鍣ㄥ浜庝竴涓被鍙細鍔犺浇涓€娆★紝绫诲姞杞藉櫒鍐呴儴淇濆瓨鏈夌被缂撳瓨锛屾棤闇€閲嶅鍔犺浇锛屼絾鐩墠鐨勭幇璞″嵈鏄敱浜庣被鍔犺浇鑰屽鑷寸嚎绋嬪ぇ閲廈lock锛屽洜姝ゅ繀鐒舵槸鏈変簺绫诲姞杞戒笉浜嗭紝涓斾笉鏂噸澶嶅皾璇曞姞杞斤紝閭e埌搴曟槸浠€涔堢被鏃犳硶鍔犺浇鍛紵

3.2.4 鍒板簳浠€涔堢被鍔犺浇涓嶄簡锛?/h4>

瑕佹壘鍒板叿浣撴槸浠€涔堢被鏃犳硶鍔犺浇锛屽綊鏍圭粨搴曡繕鏄鍒嗘瀽涓氬姟寮傚父鐨勫叿浣撳爢鏍堛€?/span>

鍥?2 涓氬姟寮傚父鍫嗘爤涓€

鍥?3 涓氬姟寮傚父鍫嗘爤浜?/span>

瀵规瘮濡傚浘12鍜屽浘13鎵€绀虹殑涓や唤涓氬姟寮傚父鍫嗘爤锛屾垜浠彲浠ョ湅鍒颁袱浠藉爢鏍堝熀鏈浉浼硷紝涓斿ぇ澶氭暟绫婚兘鏄緢鏅€氱殑绫伙紝浣嗘槸鍞竴涓嶅悓鐨勫湴鏂瑰湪浜庯細

  1. sun.reflect.NativeMethodAccessorImpl锛?/span>鍙傝鍥?2锛夈€?/span>
  2. sun.reflect.GeneratedMethodAccessor261锛?span style="color: #888888;">鍙傝鍥?3锛夈€?/span>

浠庡瓧闈俊鎭腑涓嶉毦鐚滄祴鍑鸿繖涓庡弽灏勮皟鐢ㄧ浉鍏筹紝浣嗛棶棰樻槸杩欎袱浠藉爢鏍堝搴旂殑鍏跺疄鏄悓涓€浠戒笟鍔′唬鐮侊紝涓轰粈涔堜細浜х敓涓や唤涓嶅悓鐨勫紓甯稿爢鏍堬紵鏌ラ槄鐩稿叧璧勬枡寰楃煡锛岃繖涓嶫VM鍙嶅皠璋冪敤鐩稿叧锛孞VM瀵瑰弽灏勮皟鐢ㄥ垎涓ょ鎯呭喌锛?/span>

  1. 榛樿浣跨敤native鏂规硶杩涜鍙嶅皠鎿嶄綔銆?/span>
  2. 涓€瀹氭潯浠朵笅浼氱敓鎴愬瓧鑺傜爜杩涜鍙嶅皠鎿嶄綔锛屽嵆鐢熸垚sun.reflect.GeneratedMethodAccessor<N>绫伙紝瀹冩槸涓€涓弽灏勮皟鐢ㄦ柟娉曠殑鍖呰绫伙紝浠g悊涓嶅悓鐨勬柟娉曪紝绫诲悗缂€搴忓彿閫掑銆?/span>

JVM鍙嶅皠璋冪敤鐨勪富瑕佹祦绋嬫槸鑾峰彇MethodAccessor锛屽苟鐢盡ethodAccessor鎵цinvoke璋冪敤锛岀浉鍏充唬鐮佸涓嬶細

// java.lang.reflect.Method  

@CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}

MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
// 鑾峰彇 MethodAccessor
ma = acquireMethodAccessor();
}
// 閫氳繃 MethodAccessor 璋冪敤
return ma.invoke(obj, args);
}

private MethodAccessor acquireMethodAccessor() {
MethodAccessor tmp = null;
if (root != null) tmp = root.getMethodAccessor();
if (tmp != null) {
methodAccessor = tmp;
} else {
// 閫氳繃 ReflectionFactory 鍒涘缓 MethodAccessor
tmp = reflectionFactory.newMethodAccessor(this);
setMethodAccessor(tmp);
}

return tmp;
}

褰搉oInflation涓篺alse锛堥粯璁や负false锛夋垨鑰呭弽灏勬柟娉曟墍鍦ㄧ被鏄疺M鍖垮悕绫伙紙绫诲悕涓寘鎷枩鏉犫€?鈥濓級鐨勬儏鍐典笅锛孯eflectionFactory浼氳繑鍥炰竴涓狹ethodAccessor浠g悊绫伙紝鍗矰elegatingMethodAccessorImpl銆?/p>

// sun.reflect.ReflectionFactory

public MethodAccessor newMethodAccessor(Method method) {
// 閫氳繃鍚姩鍙傛暟鑾峰彇骞惰В鏋?noInflation 鍜?inflationThreshold 鍊?/span>
// noInflation 榛樿涓?false
// inflationThreshold 榛樿涓?5
checkInitted();

if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
return new MethodAccessorGenerator().
generateMethod(method.getDeclaringClass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
method.getModifiers());
} else {
NativeMethodAccessorImpl acc =
new NativeMethodAccessorImpl(method);
DelegatingMethodAccessorImpl res =
new DelegatingMethodAccessorImpl(acc);
acc.setParent(res);

// 杩斿洖浠g悊 DelegatingMethodAccessorImpl
return res;
}
}

private static void checkInitted() {
if (initted) return;
AccessController.doPrivileged(
new PrivilegedAction<Void>() {
public Void run() {
// Tests to ensure the system properties table is fully
// initialized. This is needed because reflection code is
// called very early in the initialization process (before
// command-line arguments have been parsed and therefore
// these user-settable properties installed.) We assume that
// if System.out is non-null then the System class has been
// fully initialized and that the bulk of the startup code
// has been run.

if (System.out == null) {
// java.lang.System not yet fully initialized
return null;
}

String val = System.getProperty("sun.reflect.noInflation");
if (val != null && val.equals("true")) {
noInflation = true;
}

val = System.getProperty("sun.reflect.inflationThreshold");
if (val != null) {
try {
inflationThreshold = Integer.parseInt(val);
} catch (NumberFormatException e) {
throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", e);
}
}

initted = true;
return null;
}
});
}

榛樿鎯呭喌涓婦elegatingMethodAccessorImpl浠g悊浜哊ativeMethodAccessorImpl锛屼絾鏄殢鐫€鍙嶅皠璋冪敤娆℃暟鐨勫鍔狅紝褰撲竴涓柟娉曡鍙嶅皠璋冪敤鐨勬鏁拌秴杩囦竴瀹氱殑闃€鍊兼椂锛?span style="color: #888888;">inflationThreshold锛岄粯璁ゅ€兼槸15锛夛紝NativeMethodAccessorImpl浼氶€氳繃瀛楄妭鐮佺敓鎴愭妧鏈紝鑷姩鐢熸垚MethodAccessorImpl瀹炵幇绫伙紝骞朵慨鏀笵elegatingMethodAccessorImpl鐨勫唴閮ㄤ唬鐞嗗璞℃寚鍚戝瓧鑺傜爜鐢熸垚绫诲疄渚嬶紝浠庤€屾敼鍙樺悗缁弽灏勮皟鐢ㄩ€昏緫銆?/span>

鍥?4 MethodAccessor鍏崇郴鍥?/span>

// sun.reflect.DelegatingMethodAccessorImpl

class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
// 鍐呴儴浠g悊 MethodAccessorImpl
private MethodAccessorImpl delegate;

DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {
setDelegate(delegate);
}

public Object invoke(Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException
{
return delegate.invoke(obj, args);
}

void setDelegate(MethodAccessorImpl delegate) {
this.delegate = delegate;
}
}
// sun.reflect.NativeMethodAccessorImpl

class NativeMethodAccessorImpl extends MethodAccessorImpl {
private final Method method;
private DelegatingMethodAccessorImpl parent;
private int numInvocations;

NativeMethodAccessorImpl(Method method) {
this.method = method;
}

public Object invoke(Object obj, Object[] args)
throws IllegalArgumentException, InvocationTargetException
{
// We can't inflate methods belonging to vm-anonymous classes because
// that kind of class can't be referred to by name, hence can't be
// found from the generated bytecode.

// 姣忔璋冪敤鏃?numInvocations 閮戒細鑷鍔?锛屽鏋滆秴杩囬槇鍊硷紙榛樿鏄?5娆★級锛屽氨浼氫慨鏀圭埗绫荤殑浠g悊瀵硅薄锛屼粠鑰屾敼鍙樿皟鐢ㄩ摼璺?/span>
if (++numInvocations > ReflectionFactory.inflationThreshold()
&& !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
MethodAccessorImpl acc = (MethodAccessorImpl)
// 鍔ㄦ€佺敓鎴愬瓧鑺傜爜锛屼紭鍖栧弽灏勮皟鐢ㄩ€熷害
new MethodAccessorGenerator().
generateMethod(method.getDeclaringClass(),
method.getName(),
method.getParameterTypes(),
method.getReturnType(),
method.getExceptionTypes(),
method.getModifiers());
// 淇敼鐖朵唬鐞嗙被鐨勪唬鐞嗗璞?/span>
parent.setDelegate(acc);
}

return invoke0(method, obj, args);
}

void setParent(DelegatingMethodAccessorImpl parent) {
this.parent = parent;
}

private static native Object invoke0(Method m, Object obj, Object[] args);
}

浠嶮ethodAccessorGenerator#generateName鏂规硶鍙互鐪嬪埌锛屽瓧鑺傜爜鐢熸垚鐨勭被鍚嶇О瑙勫垯鏄痵un.reflect.GeneratedConstructorAccessor<N>锛屽叾涓璑鏄粠0寮€濮嬬殑閫掑鏁板瓧锛屼笖鐢熸垚绫绘槸鐢盌elegatingClassLoader绫诲姞杞藉櫒瀹氫箟锛屾墍浠ュ叾浠栫被鍔犺浇鍣ㄦ棤娉曞姞杞借绫伙紝涔熷氨鏃犳硶鐢熸垚绫荤紦瀛樻暟鎹紝浠庤€屽鑷存瘡娆″姞杞界被鏃堕兘闇€瑕侀亶鍘咼arFile锛屾瀬澶у湴闄嶄綆浜嗙被鏌ユ壘閫熷害锛屼笖绫诲姞杞借繃绋嬫槸synchronized鍚屾璋冪敤锛屽湪楂樺苟鍙戞儏鍐典笅浼氭洿鍔犳伓鍖栵紝浠庤€屽鑷寸嚎绋婤lock銆?/p>

// sun.reflect.MethodAccessorGenerator

public MethodAccessor generateMethod(Class<?> declaringClass,
String name,
Class<?>[] parameterTypes,
Class<?> returnType,
Class<?>[] checkedExceptions,
int modifiers)
{
return (MethodAccessor) generate(declaringClass,
name,
parameterTypes,
returnType,
checkedExceptions,
modifiers,
false,
false,
null);
}

private MagicAccessorImpl generate(final Class<?> declaringClass,
String name,
Class<?>[] parameterTypes,
Class<?> returnType,
Class<?>[] checkedExceptions,
int modifiers,
boolean isConstructor,
boolean forSerialization,
Class<?> serializationTargetClass)
{

final String generatedName = generateName(isConstructor, forSerialization);

// 蹇界暐浠ヤ笂浠g爜

return AccessController.doPrivileged(
new PrivilegedAction<MagicAccessorImpl>() {
public MagicAccessorImpl run() {
try {
return (MagicAccessorImpl)
ClassDefiner.defineClass
(generatedName,
bytes,
0,
bytes.length,
declaringClass.getClassLoader()).newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new InternalError(e);
}
}
});
}

// 鐢熸垚鍙嶅皠绫诲悕锛岀湅鍒颁簡鐔熸倝鐨?sun.reflect.GeneratedConstructorAccessor<N>
private static synchronized String generateName(boolean isConstructor, boolean forSerialization)
{
if (isConstructor) {
if (forSerialization) {
int num = ++serializationConstructorSymnum;
return "sun/reflect/GeneratedSerializationConstructorAccessor" + num;
} else {
int num = ++constructorSymnum;
return "sun/reflect/GeneratedConstructorAccessor" + num;
}
} else {
int num = ++methodSymnum;
return "sun/reflect/GeneratedMethodAccessor" + num;
}
}
// sun.reflect.ClassDefiner

static Class<?> defineClass(String name, byte[] bytes, int off, int len,
final ClassLoader parentClassLoader)
{
ClassLoader newLoader = AccessController.doPrivileged(
new PrivilegedAction<ClassLoader>() {
public ClassLoader run() {
return new DelegatingClassLoader(parentClassLoader);
}
});
// 閫氳繃 DelegatingClassLoader 绫诲姞杞藉櫒瀹氫箟鐢熸垚绫?/span>
return unsafe.defineClass(name, bytes, off, len, newLoader, null);
}

閭d箞锛孞VM鍙嶅皠璋冪敤涓轰粈涔堣鍋氳繖涔堝仛锛?/p>

鍏跺疄杩欐槸JVM瀵瑰弽灏勮皟鐢ㄧ殑涓€绉嶄紭鍖栨墜娈碉紝鍦╯un.reflect.ReflectionFactory鐨勬枃妗f敞閲婇噷瀵规鍋氫簡瑙i噴锛岃繖鏄竴绉嶁€淚nflation鈥濇満鍒讹紝鍔犺浇瀛楄妭鐮佺殑璋冪敤鏂瑰紡鍦ㄧ涓€娆¤皟鐢ㄦ椂浼氭瘮Native璋冪敤鐨勯€熷害瑕佹參3~4鍊嶏紝浣嗘槸鍦ㄥ悗缁皟鐢ㄦ椂浼氭瘮Native璋冪敤閫熷害蹇?0澶氬€嶃€備负浜嗛伩鍏嶅弽灏勮皟鐢ㄥ奖鍝嶅簲鐢ㄧ殑鍚姩閫熷害锛屾墍浠ュ湪鍙嶅皠璋冪敤鐨勫墠鍑犳閫氳繃Native鏂瑰紡璋冪敤锛屽綋瓒呰繃涓€瀹氳皟鐢ㄦ鏁板悗浣跨敤瀛楄妭鐮佹柟寮忚皟鐢紝鎻愬崌鍙嶅皠璋冪敤鎬ц兘銆?/p>

"Inflation" mechanism. Loading bytecodes to implement Method.invoke() and Constructor.newInstance() currently costs 3-4x more than an invocation via native code for the first invocation (though subsequent invocations have been benchmarked to be over 20x faster). Unfortunately this cost increases startup time for certain applications that use reflection intensively (but only once per class) to bootstrap themselves. To avoid this penalty we reuse the existing JVM entry points for the first few invocations of Methods and Constructors and then switch to the bytecode-based implementations.

鑷虫锛屾€荤畻鐞嗘竻浜嗙被鍔犺浇瀵艰嚧绾跨▼Block鐨勭洿鎺ュ師鍥狅紝浣嗚繖骞堕潪鏍瑰洜锛屼笟鍔′唬鐮佷腑鏅櫘閫氶€氬湴鎵撳嵃涓€鏉RROR鏃ュ織锛屼负浣曚細瀵艰嚧瑙f瀽銆佸姞杞藉紓甯稿爢鏍堢被锛?/span>

3.2.5 涓轰粈涔堣瑙f瀽寮傚父鍫嗘爤锛?/h4>

鍥?5 AsyncAppender澶勭悊鏃ュ織娴佺▼

AsyncAppender澶勭悊鏃ュ織绠€瑕佹祦绋嬪涓婂浘15鎵€绀猴紝鍦ㄥ叾鍐呴儴缁存姢涓€涓狟lockingQueue闃熷垪鍜屼竴涓狝syncThread绾跨▼锛屽鐞嗘棩蹇楁椂鍏堟妸鏃ュ織杞崲鎴怢og4jLogEvent蹇収鐒跺悗鍏ラ槦锛屽悓鏃禔syncThread绾跨▼璐熻矗浠庨槦鍒楅噷鑾峰彇鍏冪礌鏉ュ紓姝ュ鐞嗘棩蹇椾簨浠躲€?/span>

// org.apache.logging.log4j.core.appender.AsyncAppender

@Override
public void append(final LogEvent logEvent) {
if (!isStarted()) {
throw new IllegalStateException("AsyncAppender " + getName() + " is not active");
}
if (!Constants.FORMAT_MESSAGES_IN_BACKGROUND) { // LOG4J2-898: user may choose
logEvent.getMessage().getFormattedMessage(); // LOG4J2-763: ask message to freeze parameters
}
// 鍒涘缓 鏃ュ織鏁版嵁蹇収
final Log4jLogEvent memento = Log4jLogEvent.createMemento(logEvent, includeLocation);
// 鏀惧叆 BlockingQueue 涓?/span>
if (!transfer(memento)) {
if (blocking) {
// delegate to the event router (which may discard, enqueue and block, or log in current thread)
final EventRoute route = asyncQueueFullPolicy.getRoute(thread.getId(), memento.getLevel());
route.logMessage(this, memento);
} else {
error("Appender " + getName() + " is unable to write primary appenders. queue is full");
logToErrorAppenderIfNecessary(false, memento);
}
}
}

AsyncAppender鍦ㄧ敓鎴怢ogEvent鐨勫揩鐓og4jLogEvent鏃讹紝浼氬厛瀵筁ogEvent搴忓垪鍖栧鐞嗙粺涓€杞崲涓篖ogEventProxy锛屾鏃朵笉鍚岀被鍨嬬殑LogEvent鐨勫鐞嗘儏鍐电◢鏈夊樊寮傦細

  • Log4jLogEvent绫诲瀷锛屽厛鎵цLog4jLogEvent#getThrownProxy鏂规硶锛岃Е鍙戝垱寤篢hrowableProxy瀹炰緥銆?/li>
  • MutableLogEvent绫诲瀷锛屽厛鍒涘缓LogEventProxy瀹炰緥锛屽湪鏋勯€犲嚱鏁板唴鎵цMutableLogEvent#getThrownProxy鏂规硶锛岃Е鍙戝垱寤篢hrowableProxy瀹炰緥銆?/li>

缁间笂锛屼笉绠ogEvent鐨勫疄闄呯被鍨嬫槸MutableLogEvent杩樻槸Log4jLogEvent锛屾渶缁堥兘浼氳Е鍙戝垱寤篢hrowableProxy瀹炰緥锛屽苟鍦═hrowableProxy鏋勯€犲嚱鏁板唴瑙﹀彂浜嗚В鏋愩€佸姞杞藉紓甯稿爢鏍堢被銆?/p>

// org.apache.logging.log4j.core.impl.Log4jLogEvent

// 鐢熸垚Log4jLogEvent蹇収
public static Log4jLogEvent createMemento(final LogEvent event, final boolean includeLocation) {
// TODO implement Log4jLogEvent.createMemento()
return deserialize(serialize(event, includeLocation));
}

public static Serializable serialize(final LogEvent event, final boolean includeLocation) {
if (event instanceof Log4jLogEvent) {
// 纭繚 ThrowableProxy 宸插畬鎴愬垵濮嬪寲
event.getThrownProxy(); // ensure ThrowableProxy is initialized
// 鍒涘缓 LogEventProxy
return new LogEventProxy((Log4jLogEvent) event, includeLocation);
}
// 鍒涘缓 LogEventProxy
return new LogEventProxy(event, includeLocation);
}

@Override
public ThrowableProxy getThrownProxy() {
if (thrownProxy == null && thrown != null) {
thrownProxy = new ThrowableProxy(thrown);
}
return thrownProxy;
}

public LogEventProxy(final LogEvent event, final boolean includeLocation) {
this.loggerFQCN = event.getLoggerFqcn();
this.marker = event.getMarker();
this.level = event.getLevel();
this.loggerName = event.getLoggerName();

final Message msg = event.getMessage();
this.message = msg instanceof ReusableMessage
? memento((ReusableMessage) msg)
: msg;
this.timeMillis = event.getTimeMillis();
this.thrown = event.getThrown();
// 鍒涘缓 ThrownProxy 瀹炰緥
this.thrownProxy = event.getThrownProxy();
this.contextData = memento(event.getContextData());
this.contextStack = event.getContextStack();
this.source = includeLocation ? event.getSource() : null;
this.threadId = event.getThreadId();
this.threadName = event.getThreadName();
this.threadPriority = event.getThreadPriority();
this.isLocationRequired = includeLocation;
this.isEndOfBatch = event.isEndOfBatch();
this.nanoTime = event.getNanoTime();
}
// org.apache.logging.log4j.core.impl.MutableLogEvent

@Override
public ThrowableProxy getThrownProxy() {
if (thrownProxy == null && thrown != null) {
// 鏋勯€?ThrowableProxy 鏃舵墦鍗板紓甯稿爢鏍?/span>
thrownProxy = new ThrowableProxy(thrown);
}
return thrownProxy;
}

3.2.6 闂灏忕粨

Log4j2鎵撳嵃寮傚父鏃ュ織鏃讹紝AsyncAppender浼氬厛鍒涘缓鏃ュ織浜嬩欢蹇収锛屽苟杩涗竴姝ヨЕ鍙戣В鏋愩€佸姞杞藉紓甯稿爢鏍堢被銆侸VM閫氳繃鐢熸垚瀛楄妭鐮佺殑鏂瑰紡浼樺寲鍙嶅皠璋冪敤鎬ц兘锛屼絾璇ュ姩鎬佺敓鎴愮殑绫绘棤娉曡WebAppClassLoader绫诲姞杞藉櫒鍔犺浇锛屽洜姝ゅ綋澶ч噺鍖呭惈鍙嶅皠璋冪敤鐨勫紓甯稿爢鏍堣杈撳嚭鍒版棩蹇楁椂锛屼細棰戠箒鍦拌Е鍙戠被鍔犺浇锛岀敱浜庣被鍔犺浇杩囩▼鏄痵ynchronized鍚屾鍔犻攣鐨勶紝涓旀瘡娆″姞杞介兘闇€瑕佽鍙栨枃浠讹紝閫熷害杈冩參锛屼粠鑰屽鑷寸嚎绋婤lock銆?/span>

3.3 Lambda琛ㄨ揪寮忓鑷寸嚎绋婤lock

3.3.1 闂鐜板満

鏀跺埌鈥渏vm.thread.blocked.count鈥濆憡璀﹀悗锛岀珛鍒婚€氳繃鐩戞帶骞冲彴鏌ョ湅绾跨▼鐩戞帶鎸囨爣锛屽綋鏃剁殑绾跨▼鍫嗘爤濡備笅鍥?6鍜屽浘17鎵€绀猴細

鍥?6 绛夊緟閿佺殑Blocked绾跨▼鍫嗘爤

鍥?7 鎸佹湁閿佺殑Runnable绾跨▼鍫嗘爤

浠嶣locked绾跨▼鍫嗘爤涓嶉毦鐪嬪嚭鏄拰鏃ュ織鎵撳嵃鐩稿叧锛岀敱浜庢槸ERROR绾у埆鏃ュ織锛屾煡鐪嬪叿浣撴姤閿欐棩蹇楋紝鍙戠幇濡備笅鍥?8鎵€绀虹殑涓氬姟寮傚父銆?/span>

鍥?8 涓氬姟寮傚父鍫嗘爤

鏈渚嬬殑Blocked绾跨▼鍫嗘爤鍜屼笂杩扳€淎syncAppender瀵艰嚧绾跨▼Block鈥濇渚嬩竴鏍凤紝閭d箞瀵艰嚧绾跨▼Block鐨勭姜榄佺ジ棣栦細鏄笟鍔″紓甯稿悧锛熸帴涓嬫潵鏈珷鑺傚皢缁撳悎涓嬪浘19鎵€绀虹殑璋冪敤閾捐矾娣卞叆鍒嗘瀽绾跨▼Block鐨勬牴鍥犮€?/span>

鍥?9 鏃ュ織璋冪敤閾捐矾

3.3.2 涓轰粈涔堜細Block绾跨▼锛?/h4>

浠嶣locked绾跨▼鍫嗘爤涓彲浠ョ湅鍑猴紝绾跨▼闃诲鍦ㄧ被鍔犺浇涓婏紝璇ョ嚎绋嬪爢鏍堝拰涓婅堪鈥淎syncAppender瀵艰嚧绾跨▼Block鈥濇渚嬬浉浼硷紝杩欓噷涓嶅啀閲嶅鍒嗘瀽銆?/span>

3.3.3 涓轰粈涔堜細瑙﹀彂绫诲姞杞斤紵

鍘熷洜鍜屼笂杩扳€淎syncAppender瀵艰嚧绾跨▼Block鈥濇渚嬬浉浼硷紝杩欓噷涓嶅啀閲嶅鍒嗘瀽銆?/span>

3.3.4 鍒板簳浠€涔堢被鍔犺浇涓嶄簡锛?/h4>

涓婅堪鈥淎syncAppender瀵艰嚧绾跨▼Block鈥濇渚嬩腑锛岀被鍔犺浇鍣ㄦ棤娉曞姞杞界敱JVM閽堝鍙嶅皠璋冪敤浼樺寲鎵€鐢熸垚鐨勫瓧鑺傜爜绫伙紝鏈渚嬫槸鍚︿篃鏄鍘熷洜瀵艰嚧锛岃繕寰呰繘涓€姝ュ叿浣撳垎鏋愩€傝鎵惧埌鍏蜂綋鏄粈涔堢被鏃犳硶鍔犺浇锛屽綊鏍圭粨搴曡繕鏄鍒嗘瀽涓氬姟寮傚父鐨勫叿浣撳爢鏍堛€備粠涓氬姟鍫嗘爤涓彲浠ユ槑鏄剧湅鍑烘潵锛屾病鏈変换浣曞爢鏍堝厓绱犲拰JVM鍙嶅皠璋冪敤鐩稿叧锛屽洜姝ゆ帓闄VM鍙嶅皠璋冪敤鍘熷洜锛屼絾濡備笅鐨勭壒娈婂爢鏍堜俊鎭紩璧蜂簡娉ㄦ剰锛?/span>

com.sankuai.shepherd.core.process.ProcessHandlerFactory$$Lambda$35/1331430278

浠庡爢鏍堢殑鍏抽敭瀛?$Lambda$澶ц嚧鑳界寽娴嬪嚭杩欐槸浠g爜閲屼娇鐢ㄤ簡Lambda琛ㄨ揪寮忕殑缂樻晠锛屾煡鐪嬩唬鐮佺‘瀹炵浉鍏抽儴鍒嗕娇鐢ㄤ簡Lambda琛ㄨ揪寮忥紝缁忚繃鏂偣璋冭瘯锛岃瘉瀹炵殑纭棤娉曞姞杞借绫汇€傞偅涔堬紝杩欐牱鐨勭被鏄€庝箞鏉ョ殑锛熸煡闃呯浉鍏宠祫鏂欏緱鐭ワ紝Lambda琛ㄨ揪寮忓尯鍒簬鍖垮悕鍐呴儴绫诲疄鐜帮紝鍦ㄦ瀯寤烘椂涓嶄細鐢熸垚class鏂囦欢锛岃€屾槸鍦ㄨ繍琛屾椂閫氳繃invokeDynamic鎸囦护鍔ㄦ€佽皟鐢紝Lambda琛ㄨ揪寮忕殑鍐呭浼氳灏佽鍦ㄤ竴涓潤鎬佹柟娉曞唴锛孞VM閫氳繃ASM瀛楄妭鐮佹妧鏈潵鍔ㄦ€佺敓鎴愯皟鐢ㄧ被锛屼篃灏辨槸$$Lambda$杩欑褰㈠紡鐨勭被锛岀敓鎴愮被绀轰緥濡備笅鍥?0鎵€绀猴細

鍥?0 Lambda鐢熸垚绫荤ず渚?/span>

Lambda琛ㄨ揪寮忕殑瀹炵幇鍘熺悊涓嶆槸鏈枃閲嶇偣鍐呭锛屽湪姝や笉鍋氳繃澶氫粙缁嶃€傞」鐩唬鐮佷腑浣跨敤Lambda琛ㄨ揪寮忔槸鍐嶆櫘閫氫笉杩囩殑浜嬫儏锛屼絾鏄叧浜庢绫荤殑妗堜緥鍗村苟涓嶅瑙侊紝瀹炲湪浠や汉闅句互缃俊銆傜户缁煡闃匧ambda琛ㄨ揪寮忕浉鍏虫枃妗o紝鍙戠幇寮傚父鍫嗘爤绫诲悕鍖呭惈$$Lambda$杩欐牱鐨勫叧閿瓧锛屽叾瀹炴槸JDK鐨勪竴涓狟ug锛岀浉鍏矷ssue鍙弬鑰?

  • 鈥?a href="https://bugs.openjdk.java.net/browse/JDK-8145964">鈥?span style="font-size: 15px;">NoClassDefFound error in transforming lambdas鈥?/a>鈥?/li>
  • 鈥?a href="https://bugs.openjdk.java.net/browse/JDK-8158475">鈥?span style="font-size: 15px;">JVMTI RedefineClasses doesn't handle anonymous classes properly鈥?/a>鈥?/li>

鍊煎緱涓€鎻愮殑鏄紝璇ug鍦↗DK9鐗堟湰宸茬粡淇锛屽疄闄呮祴璇曚腑鍙戠幇锛屽湪JDK8鐨勯珮鐗堟湰濡?U171绛夊凡淇璇ug锛屽紓甯稿爢鏍堜腑涓嶄細鏈夌被浼?$Lambda$鐨勫爢鏍堜俊鎭紝绀轰緥濡備笅鍥?1鎵€绀猴細

鍥?1 JDK8U171鐗堟湰涓婰ambda寮傚父鍫嗘爤绀轰緥

3.3.5 涓轰粈涔堣瑙f瀽寮傚父鍫嗘爤锛?/h4>

鍘熷洜鍜屼笂杩扳€淎syncAppender瀵艰嚧绾跨▼Block鈥濇渚嬬浉浼硷紝涓嶅啀閲嶅鍒嗘瀽銆?/span>

3.3.6 闂灏忕粨

Log4j2鎵撳嵃寮傚父鏃ュ織鏃讹紝AsyncAppender浼氬厛鍒涘缓鏃ュ織浜嬩欢蹇収锛屽苟杩涗竴姝ヨЕ鍙戣В鏋愩€佸姞杞藉紓甯稿爢鏍堢被銆侸DK 8浣庣増鏈腑浣跨敤Lambda琛ㄨ揪寮忔墍鐢熸垚鐨勫紓甯稿爢鏍堢被鏃犳硶琚玏ebAppClassLoader绫诲姞杞藉櫒鍔犺浇锛屽洜姝わ紝褰撳ぇ閲忓寘鍚獿ambda琛ㄨ揪寮忚皟鐢ㄧ殑寮傚父鍫嗘爤琚緭鍑哄埌鏃ュ織鏃讹紝浼氶绻佸湴瑙﹀彂绫诲姞杞斤紝鐢变簬绫诲姞杞借繃绋嬫槸synchronized鍚屾鍔犻攣鐨勶紝涓旀瘡娆″姞杞介兘闇€瑕佽鍙栨枃浠讹紝閫熷害杈冩參锛屼粠鑰屽鑷翠簡绾跨▼Block銆?/span>

3.4 AsyncLoggerConfig瀵艰嚧绾跨▼Block

3.4.1 闂鐜板満

鏀跺埌鈥渏vm.thread.blocked.count鈥濆憡璀﹀悗绔嬪埢閫氳繃鐩戞帶骞冲彴鏌ョ湅绾跨▼鐩戞帶鎸囨爣锛屽綋鏃剁殑绾跨▼鍫嗘爤濡備笅鍥?2鍜屽浘23鎵€绀恒€?/span>

鍥?2 绛夊緟閿佺殑Blocked绾跨▼鍫嗘爤

鍥?3 鎸佹湁閿佺殑Runnable绾跨▼鍫嗘爤

浠嶣locked绾跨▼鍫嗘爤涓嶉毦鐪嬪嚭鏄拰鏃ュ織鎵撳嵃鐩稿叧锛屾湰妗堜緥鐨勪笟鍔″紓甯稿拰涓婅堪鈥淎syncAppender瀵艰嚧绾跨▼Block鈥濈殑涓氬姟寮傚父涓€鏍凤紝杩欓噷涓嶅啀閲嶅浠嬬粛銆傞偅涔堬紝鍒板簳鏄粈涔堝師鍥犲鑷寸嚎绋婤lock鍛紵鎺ヤ笅鏉ユ湰绔犺妭灏嗙粨鍚堜笅鍥?4鎵€绀虹殑璋冪敤閾捐矾娣卞叆鍒嗘瀽绾跨▼Block鐨勬牴鍥犮€?/span>

鍥?4 鏃ュ織璋冪敤閾捐矾

3.4.2 涓轰粈涔堜細Block绾跨▼锛?/h4>

鍘熷洜鍜屼笂杩扳€淎syncAppender瀵艰嚧绾跨▼Block鈥濇渚嬬浉浼硷紝杩欓噷涓嶅啀閲嶅鍒嗘瀽銆?/span>

3.4.3 涓轰粈涔堜細瑙﹀彂绫诲姞杞斤紵

鍘熷洜鍜屼笂杩扳€淎syncAppender瀵艰嚧绾跨▼Block鈥濇渚嬬浉浼硷紝杩欓噷涓嶅啀閲嶅鍒嗘瀽銆?/span>

3.4.4 鍒板簳鏄粈涔堢被鍔犺浇涓嶄簡锛?/h4>

鍘熷洜鍜屼笂杩扳€淎syncAppender瀵艰嚧绾跨▼Block鈥濇渚嬬浉浼硷紝杩欓噷涓嶅啀閲嶅鍒嗘瀽銆?/span>

3.4.5 涓轰粈涔堣瑙f瀽寮傚父鍫嗘爤锛?/h4>

鍦ㄥ紑濮嬪垎鏋愬師鍥犱箣鍓嶏紝鍏堢悊娓呮Log4j2鍏充簬鏃ュ織鐨勫嚑涓噸瑕佹蹇碉細

  • <Logger>锛屾棩蹇楅厤缃爣绛撅紝鐢ㄤ簬XML鏃ュ織閰嶇疆鏂囦欢涓紝瀵瑰簲Log4j2妗嗘灦涓殑LoggerConfig绫伙紝鍚屾鍒嗗彂鏃ュ織浜嬩欢鍒板搴擜ppender銆?/span>
  • <AsyncLogger>锛屾棩蹇楅厤缃爣绛撅紝鐢ㄤ簬XML鏃ュ織閰嶇疆鏂囦欢涓紝瀵瑰簲Log4j2妗嗘灦涓殑AsyncLoggerConfig绫伙紝鍐呴儴浣跨敤Disruptor闃熷垪寮傛鍒嗗彂鏃ュ織浜嬩欢鍒板搴擜ppender銆?/span>
  • Logger锛屽悓姝ユ棩蹇楃被锛岀敤浜庡垱寤哄悓姝ユ棩蹇楀疄渚嬶紝鍚屾璋冪敤ReliabilityStrategy澶勭悊鏃ュ織銆?/span>
  • AsyncLogger锛屽紓姝ユ棩蹇楃被锛岀敤浜庡垱寤哄紓姝ユ棩蹇楀疄渚嬶紝鍐呴儴浣跨敤Disruptor闃熷垪瀹炵幇寮傛璋冪敤ReliabilityStrategy澶勭悊鏃ュ織銆?/span>

鎬荤殑鏉ヨ锛?lt;Logger>鏍囩鍜孡ogger绫绘槸瀹屽叏涓嶅悓鐨勪袱涓蹇碉紝<AsyncLogger>鏍囩鍜孉syncLogger绫讳篃鏄畬鍏ㄤ笉鍚岀殑涓や釜姒傚康锛屼笉鍙贩娣嗐€傜敱浜庨」鐩苟鏈厤缃甃og4jContextSelector鍙傛暟锛屾墍浠ヤ娇鐢ㄧ殑鏄悓姝ogger锛屽嵆閫氳繃LoggerFactory.getLogger鏂规硶鑾峰彇鐨勬槸Logger绫诲疄渚嬭€屼笉鏄疉syncLogger绫诲疄渚嬶紝鍚屾椂鐢变簬椤圭洰鐨刲og4j2.xml閰嶇疆鏂囦欢閲岄厤缃簡<AsyncLogger>鏍囩锛屾墍浠ュ叾搴曞眰鏄疞ogger鍜孉syncLoggerConfig缁勫悎銆侫syncLoggerConfig澶勭悊鏃ュ織浜嬩欢绠€瑕佹祦绋嬪涓嬪浘25鎵€绀猴紝鍐呴儴浣跨敤Disruptor闃熷垪锛屽湪鐢熸垚闃熷垪鍏冪礌鏃讹紝鐢眛ranslator鏉ヨ礋璐e~鍏呭厓绱犲瓧娈碉紝骞舵妸濉厖鍚庣殑鍏冪礌鏀惧叆RingBuffer涓紝浜庢鍚屾椂锛岀嫭绔嬬殑寮傛绾跨▼浠嶳ingBuffer涓秷璐逛簨浠讹紝骞惰皟鐢ㄩ厤缃湪璇syncLoggerConfig涓婄殑Appender澶勭悊鏃ュ織璇锋眰銆?/span>

鍥?5 AsyncLoggerConfig澶勭悊娴佺▼

AsyncLoggerConfig鎻愪緵浜嗗甫鏈塂isruptor闃熷垪瀹炵幇鐨勪唬鐞嗙被鍗矨syncLoggerConfigDisruptor锛屽湪鏃ュ織浜嬩欢杩涘叆RingBuffer鏃讹紝鐢变簬椤圭洰浣跨敤鐨勬槸ReusableLogEventFactory锛屾墍浠ョ敱MUTABLE_TRANSLATOR璐熻矗鍒濆鍖栨棩蹇椾簨浠讹紝鍦ㄦ杩囩▼涓細璋冪敤getThrownProxy鏂规硶鍒涘缓ThrowableProxy瀹炰緥锛岃繘鑰屽湪ThrowableProxy鏋勯€犲嚱鏁板唴閮ㄨЕ鍙戣В鏋愩€佸姞杞藉紓甯稿爢鏍堢被銆?/span>

// org.apache.logging.log4j.core.async.AsyncLoggerConfigDisruptor$EventTranslatorTwoArg

/**
* Object responsible for passing on data to a RingBuffer event with a MutableLogEvent.
*/
private static final EventTranslatorTwoArg<Log4jEventWrapper, LogEvent, AsyncLoggerConfig> MUTABLE_TRANSLATOR =
new EventTranslatorTwoArg<Log4jEventWrapper, LogEvent, AsyncLoggerConfig>() {

@Override
public void translateTo(final Log4jEventWrapper ringBufferElement, final long sequence,
final LogEvent logEvent, final AsyncLoggerConfig loggerConfig) {
// 鍒濆鍖?Disruptor RingBuffer 鏃ュ織鍏冪礌瀛楁
((MutableLogEvent) ringBufferElement.event).initFrom(logEvent);
ringBufferElement.loggerConfig = loggerConfig;
}
};
// org.apache.logging.log4j.core.impl.MutableLogEvent

public void initFrom(final LogEvent event) {
this.loggerFqcn = event.getLoggerFqcn();
this.marker = event.getMarker();
this.level = event.getLevel();
this.loggerName = event.getLoggerName();
this.timeMillis = event.getTimeMillis();
this.thrown = event.getThrown();
// 瑙﹀彂鍒涘缓 ThrowableProxy 瀹炰緥
this.thrownProxy = event.getThrownProxy();

// NOTE: this ringbuffer event SHOULD NOT keep a reference to the specified
// thread-local MutableLogEvent's context data, because then two threads would call
// ReadOnlyStringMap.clear() on the same shared instance, resulting in data corruption.
this.contextData.putAll(event.getContextData());

this.contextStack = event.getContextStack();
this.source = event.isIncludeLocation() ? event.getSource() : null;
this.threadId = event.getThreadId();
this.threadName = event.getThreadName();
this.threadPriority = event.getThreadPriority();
this.endOfBatch = event.isEndOfBatch();
this.includeLocation = event.isIncludeLocation();
this.nanoTime = event.getNanoTime();
setMessage(event.getMessage());
}

@Override
public ThrowableProxy getThrownProxy() {
if (thrownProxy == null && thrown != null) {
// 鏋勯€?ThrowableProxy 鏃舵墦鍗板紓甯稿爢鏍?/span>
thrownProxy = new ThrowableProxy(thrown);
}
return thrownProxy;
}

3.4.6 闂灏忕粨

Log4j2鎵撳嵃寮傚父鏃ュ織鏃讹紝AsyncLoggerConfig浼氬垵濮嬪寲Disruptor RingBuffer鏃ュ織鍏冪礌瀛楁锛屽苟杩涗竴姝ヨЕ鍙戣В鏋愩€佸姞杞藉紓甯稿爢鏍堢被銆侸VM閫氳繃鐢熸垚瀛楄妭鐮佺殑鏂瑰紡浼樺寲鍙嶅皠璋冪敤鎬ц兘锛屼絾璇ュ姩鎬佺敓鎴愮殑绫绘棤娉曡WebAppClassLoader绫诲姞杞藉櫒鍔犺浇锛屽洜姝ゅ綋澶ч噺鍖呭惈鍙嶅皠璋冪敤鐨勫紓甯稿爢鏍堣杈撳嚭鍒版棩蹇楁椂锛屼細棰戠箒鍦拌Е鍙戠被鍔犺浇锛岀敱浜庣被鍔犺浇杩囩▼鏄痵ynchronized鍚屾鍔犻攣鐨勶紝涓旀瘡娆″姞杞介兘闇€瑕佽鍙栨枃浠讹紝閫熷害杈冩參锛屼粠鑰屽鑷寸嚎绋婤lock銆?/span>

4. 閬垮潙鎸囧崡

鏈珷鑺備富瑕佸涓婅堪妗堜緥涓鑷寸嚎绋婤lock鐨勫師鍥犺繘琛屾眹鎬诲垎鏋愶紝骞剁粰鍑虹浉搴旂殑瑙e喅鏂规銆?/span>

4.1 闂鎬荤粨

鍥?6 鏃ュ織寮傛澶勭悊娴佺▼

鏃ュ織寮傛澶勭悊娴佺▼绀烘剰濡傚浘26鎵€绀猴紝鏁翠綋姝ラ濡備笅锛?/span>

  1. 涓氬姟绾跨▼缁勮鏃ュ織浜嬩欢瀵硅薄锛屽鍒涘缓鏃ュ織蹇収鎴栬€呭垵濮嬪寲鏃ュ織瀛楁绛夈€?/span>
  2. 鏃ュ織浜嬩欢瀵硅薄鍏ラ槦锛屽BlockingQueue闃熷垪鎴朌isruptor RingBuffer闃熷垪绛夈€?/span>
  3. 鏃ュ織寮傛绾跨▼浠庨槦鍒楄幏鍙栨棩蹇椾簨浠跺璞★紝骞惰緭鍑鸿嚦鐩殑鍦帮紝濡傛湰鍦扮鐩樻枃浠舵垨杩滅▼鏃ュ織涓績绛夈€?/span>

瀵瑰簲鍦帮紝Log4j2瀵艰嚧绾跨▼Block鐨勪富瑕佹綔鍦ㄩ闄╃偣濡備笅锛?/span>

  1. 濡備笂鍥炬爣鍙封憼鎵€绀猴紝鏃ュ織浜嬩欢瀵硅薄鍦ㄥ叆闃熷墠锛岀粍瑁呮棩蹇椾簨浠舵椂瑙﹀彂浜嗗紓甯稿爢鏍堢被瑙f瀽銆佸姞杞斤紝浠庤€屽紩鍙戠嚎绋婤lock銆?/span>
  2. 濡備笂鍥炬爣鍙封憽鎵€绀猴紝鏃ュ織浜嬩欢瀵硅薄鍦ㄥ叆闃熸椂锛岀敱浜庨槦鍒楁弧锛屾棤娉曞叆闃燂紝浠庤€屽紩鍙戠嚎绋婤lock銆?/span>
  3. 濡備笂鍥炬爣鍙封憿鎵€绀猴紝鏃ュ織浜嬩欢瀵硅薄鍦ㄥ嚭闃熷悗锛屽鏃ュ織鍐呭杩涜鏍煎紡鍖栧鐞嗘椂瑙﹀彂浜嗗紓甯稿爢鏍堢被瑙f瀽銆佸姞杞斤紝浠庤€屽紩鍙戠嚎绋?Block銆?/span>

浠庝笂杩板垎鏋愪笉闅剧湅鍑猴細

  1. 鏍囧彿鈶犲拰鈶″濡傛灉鍙戠敓绾跨▼Block锛岄偅涔堜細鐩存帴褰卞搷涓氬姟绾跨▼姹犲唴鐨勬墍鏈夌嚎绋嬨€?/span>
  2. 鏍囧彿鈶㈠嚭濡傛灉鍙戠敓绾跨▼Block锛岄偅涔堜細褰卞搷鏃ュ織寮傛绾跨▼锛岃绾跨▼閫氬父涓哄崟绾跨▼銆?/span>

鏍囧彿鈶犲拰鈶″鍙戠敓绾跨▼Block鐨勫奖鍝嶈寖鍥磋繙姣旀爣鍙封憿鏇村ぇ锛屽洜姝ゆ牳蹇冩槸瑕侀伩鍏嶆棩蹇椾簨浠跺湪鍏ラ槦鎿嶄綔瀹屾垚鍓嶈Е鍙戠嚎绋婤lock銆傚叾瀹炴棩蹇楀紓姝ョ嚎绋嬮€氬父鏄崟绾跨▼锛屽洜姝ゅ浜庡崟涓狝ppender鏉ヨ锛屼笉浼氬嚭鐜癇lock鐜拌薄锛岃嚦澶氫細瀵艰嚧寮傛绾跨▼澶勭悊閫熷害鍙樻參鑰屽凡锛屼絾濡傛灉瀛樺湪澶氫釜寮傛Appender锛岄偅涔堝涓棩蹇楀紓姝ョ嚎绋嬩粛鐒朵細鍑虹幇褰兼Block鐨勭幇璞°€?/span>

4.2 瀵圭棁涓嬭嵂

鎼炴竻妤氫簡鏃ュ織瀵艰嚧绾跨▼Block鐨勫師鍥犲悗锛岄棶棰樹篃灏变笉闅捐В鍐筹紝瑙e喅鏂规涓昏浠庢棩蹇椾簨浠垛€滃叆闃熷墠鈥濄€佲€滃叆闃熸椂鈥濆拰鈥滃嚭闃熷悗鈥濅笁鏂归潰灞曞紑銆?/span>

4.2.1 鍏ラ槦鍓嶉伩鍏嶇嚎绋婤lock

缁撳悎涓婃枃鍒嗘瀽鐨勨€淎syncAppender瀵艰嚧绾跨▼Block鈥濄€佲€淟ambda琛ㄨ揪寮忓鑷寸嚎绋婤lock鈥濆拰鈥淎syncLoggerConfig瀵艰嚧绾跨▼Block鈥濇渚嬶紝鏃ュ織浜嬩欢鍏ラ槦鍓嶉伩鍏嶇嚎绋婤lock鐨勮В鍐虫柟妗堝彲浠庡涓嬪嚑鏂归潰鑰冭檻锛?/span>

  1. 鏃ュ織浜嬩欢鍏ラ槦鍓嶉伩鍏嶈Е鍙戝紓甯稿爢鏍堢被瑙f瀽銆佸姞杞芥搷浣溿€?/span>
  2. 绂佺敤JVM鍙嶅皠璋冪敤浼樺寲銆?/span>
  3. 鍗囩骇JDK鐗堟湰淇Lambda绫籅ug銆?/span>

鍏堣鏂规缁撹锛?/span>

  1. 鑷畾涔堿ppender瀹炵幇锛屽垱寤烘棩蹇椾簨浠跺揩鐓ф椂閬垮厤瑙﹀彂寮傚父鍫嗘爤绫昏В鏋愩€佸姞杞斤紝缇庡洟鍐呴儴Scribe-Log鎻愪緵鐨凙syncScribeAppender鍗虫槸濡傛銆?/span>
  2. 鏃ュ織閰嶇疆鏂囦欢涓笉浣跨敤<AsyncLogger>鏍囩锛屽彲浠ヤ娇鐢?lt;Logger>鏍囩鏉ヤ唬鏇裤€?/span>

涓嬮潰鍏蜂綋鍒嗘瀽鏂规鍙鎬э細1. 鏃ュ織浜嬩欢鍏ラ槦鍓嶉伩鍏嶈Е鍙戝紓甯稿爢鏍堢被瑙f瀽銆佸姞杞芥搷浣?/strong>濡傛灉鍦ㄦ棩蹇椾簨浠跺叆闃熷墠锛岃兘閬垮厤寮傚父鍫嗘爤绫昏В鏋愩€佸姞杞芥搷浣滐紝鍒欏彲浠庢牴鏈笂瑙e喅璇ラ棶棰橈紝浣嗗湪Log4j2鐨?.17.1鐗堟湰涓瑼syncAppender鍜孉syncLoggerConfig浠嶅瓨鍦ㄨ闂锛屾鏃讹細

  • 瀵逛簬AsyncAppender鍦烘櫙鏉ヨ锛屽彲浠ラ€氳繃鑷畾涔堿ppender瀹炵幇锛屽湪鐢熸垚鏃ュ織浜嬩欢蹇収鏃堕伩鍏嶈Е鍙戣В鏋愩€佸姞杞藉紓甯稿爢鏍堢被锛屽苟鍦ㄩ厤缃枃浠朵腑浣跨敤鑷畾涔夌殑Appender浠f浛Log4j2鎻愪緵鐨凙syncAppender銆傝嚜瀹氫箟AsyncScribeAppender鐩稿叧浠g爜鐗囨濡備笅銆?/span>
// org.apache.logging.log4j.scribe.appender.AsyncScribeAppender

@Override
public void append(final LogEvent logEvent) {
// ... 浠ヤ笂閮ㄥ垎蹇界暐 ...
Log4jLogEvent.Builder builder = new Log4jLogEvent.Builder(event);
builder.setIncludeLocation(includeLocation);
// 鍒涘缓鏃ュ織蹇収锛岄伩鍏嶈В鏋愩€佸姞杞藉紓甯稿爢鏍堢被
final Log4jLogEvent memento = builder.build();
// ... 浠ヤ笅閮ㄥ垎蹇界暐 ...
}
  • 瀵逛簬AsyncLoggerConfig鍦烘櫙鏉ヨ锛屽彲浠ヨ€冭檻浣跨敤闈濺eusableLogEventFactory绫诲瀷鐨凩ogEventFactory鏉ヨ閬胯闂锛岄櫎姝や箣澶栦篃鍙互鑰冭檻鎹㈢敤LoggerConfig鏉ラ伩鍏嶈闂銆?/span>

2. 绂佺敤JVM鍙嶅皠璋冪敤浼樺寲璋冨ぇinflationThreshold锛?span style="color: #888888;">鍏剁被鍨嬩负 int锛夊€煎埌int鏈€澶у€硷紝濡傛锛岃櫧鐒朵竴瀹氳寖鍥村唴锛?span style="color: #888888;">鍙嶅皠璋冪敤娆℃暟涓嶈秴杩噄nt鏈€澶у€兼椂锛夐伩鍏嶄簡绫诲姞杞紹lock闂锛屼絾鎹熷け浜嗗弽灏勮皟鐢ㄦ€ц兘锛岄【姝ゅけ褰硷紝涓旀棤娉曟牴娌汇€傚彟澶栵紝瀵逛簬闈炲弽灏勭被闂浠嶇劧鏃犳硶瑙e喅锛屽涓婃枃鎵€杩扮殑Lambda琛ㄨ揪寮忛棶棰樼瓑銆?strong>3. 鍗囩骇JDK鐗堟湰淇Lambda绫籅ug鍗囩骇JDK鐗堟湰鐨勭‘鍙互瑙e喅Lambda琛ㄨ揪寮忛棶棰橈紝浣嗗苟涓嶈兘褰诲簳瑙e喅绾跨▼Block闂锛屽涓婃枃鎵€杩扮殑鍙嶅皠璋冪敤绛夈€?/span>

4.2.2 鍏ラ槦鏃堕伩鍏嶇嚎绋婤lock

缁撳悎涓婃枃鍒嗘瀽鐨勨€滄棩蹇楅槦鍒楁弧瀵艰嚧绾跨▼Block鈥濇渚嬶紝鏃ュ織浜嬩欢鍏ラ槦鏃堕伩鍏嶇嚎绋婤lock鐨勮В鍐虫柟妗堝彲浠庡涓嬪嚑鏂归潰鑰冭檻锛?/span>

  1. 鏃ュ織闃熷垪婊℃椂锛孉ppender蹇界暐璇ユ棩蹇椼€?/span>
  2. Appender浣跨敤鑷畾涔夌殑ErrorHandler瀹炵幇澶勭悊鏃ュ織銆?/span>
  3. 鍏抽棴StatusConfigListener鏃ュ織杈撳嚭銆?/span>

鍏堣鏂规缁撹锛?strong>鑷畾涔堿ppender瀹炵幇锛屾棩蹇椾簨浠跺叆闃熷け璐ユ椂蹇界暐閿欒鏃ュ織锛岀編鍥㈠唴閮⊿cribe-Log鎻愪緵鐨凙syncScribeAppender鍗虫槸濡傛銆備笅闈㈠叿浣撳垎鏋愭柟妗堝彲琛屾€э細1. 鏃ュ織闃熷垪婊℃椂Appender蹇界暐璇ユ棩蹇?/strong>鏃ュ織闃熷垪婊★紝鏌愮绋嬪害涓婅鏄庢棩蹇楃嚎绋嬬殑澶勭悊鑳藉姏涓嶈冻锛屽湪鐜版湁鏈哄櫒璧勬簮涓嶅彉鐨勬儏鍐典笅闇€瑕佸仛涓€瀹氬彇鑸嶏紝濡傛灉鏃ュ織涓嶆槸鐗瑰埆閲嶈閫氬父鍙涪寮冭鏃ュ織锛屾鏃讹細

  • 瀵逛簬AsyncAppender鍦╞locking鍦烘櫙鏉ヨ锛屽彲浠ラ€氳繃閰嶇疆log4j2.AsyncQueueFullPolicy=Discard鏉ヤ娇鐢―ISCARD绛栫暐蹇界暐鏃ュ織銆?/span>
  • 瀵逛簬AsyncAppender鍦ㄩ潪blocking鍦烘櫙鏉ヨ锛屽彲浠ラ€氳繃鑷畾涔堿ppender瀹炵幇锛屽湪鏃ュ織浜嬩欢鍏ラ槦澶辫触鍚庣洿鎺ュ拷鐣ラ敊璇棩蹇楋紝骞跺湪閰嶇疆鏂囦欢涓娇鐢ㄨ嚜瀹氫箟鐨凙ppender浠f浛Log4j2鎻愪緵鐨凙syncAppender銆傝嚜瀹氫箟AsyncScribeAppender鐩稿叧浠g爜鐗囨濡備笅銆?/span>
// org.apache.logging.log4j.scribe.appender.AsyncScribeAppender

@Override
public void append(final LogEvent logEvent) {
// ... 浠ヤ笂閮ㄥ垎蹇界暐 ...
if (!transfer(memento)) {
if (blocking) {
// delegate to the event router (which may discard, enqueue and block, or log in current thread)
final EventRouteAsyncScribe route = asyncScribeQueueFullPolicy.getRoute(processingThread.getId(), memento.getLevel());
route.logMessage(this, memento);
} else {
// 鑷畾涔塸rintDebugInfo鍙傛暟锛屾帶鍒舵槸鍚﹁緭鍑篹rror淇℃伅锛岄粯璁や负false
if (printDebugInfo) {
error("Appender " + getName() + " is unable to write primary appenders. queue is full");
}
logToErrorAppenderIfNecessary(false, memento);
}
}
// ... 浠ヤ笅閮ㄥ垎蹇界暐 ...
}

2. Appender浣跨敤鑷畾涔夌殑ErrorHandler瀹炵幇澶勭悊鏃ュ織鑷畾涔塃rrorHandler锛孉ppender鍐呰缃甴andler涓鸿嚜瀹氫箟ErrorHandler瀹炰緥鍗冲彲锛屼絾璇ユ柟寮忎粎閫傜敤浜庨€氳繃Log4j2 API鏂瑰紡鍒涘缓鐨凩ogger锛屼笉鏀寔鏃ュ織閰嶇疆鏂囦欢鐨勪娇鐢ㄦ柟寮忋€傜敱浜庡ぇ澶氭暟鐢ㄦ埛閮戒娇鐢ㄩ厤缃枃浠舵柟寮忥紝鎵€浠ヨ鏂规浣跨敤鍦烘櫙鏈夐檺锛屼笉杩囧彲浠ユ湡寰呭悗缁棩蹇楁鏋舵敮鎸侀厤缃枃浠惰嚜瀹氫箟ErrorHandler锛屽凡鏈夌浉鍏?/span>鈥?a href="https://issues.apache.org/jira/browse/LOG4J2-2927">鈥?span style="font-size: 15px;">Issue: ErrorHandlers on Appenders cannot be configured鈥?/a>鈥?span style="font-size: 15px;">鍙嶉缁欑ぞ鍖恒€?strong>3. 鍏抽棴StatusConfigListener鏃ュ織杈撳嚭

  • 閰嶇疆鏂囦欢涓缃瓹onfiguration鐨剆tatus灞炴€у€间负off锛屽垯涓嶄細鍒涘缓StatusConfigListener锛屼絾姝ゆ椂StatusLogger浼氳皟鐢⊿impleLogger鏉ヨ緭鍑烘棩蹇楀埌System.err锛屼粛涓嶈В鍐抽棶棰樸€?/span>
  • 閰嶇疆鏂囦欢涓缃瓹onfiguration鐨剆tatus灞炴€у€间负fatal锛屽垯鍙湁fatal绾у埆鐨勬棩蹇楁墠浼氳緭鍑猴紝鏅€氱殑error鏃ュ織鐩存帴蹇界暐锛屼絾fatal鏉′欢杩囦簬涓ヨ嫑锛屽彲鑳戒細蹇界暐涓€浜涢噸瑕佺殑error鏃ュ織銆?/span>

4.2.3 鍑洪槦鍚庨伩鍏嶇嚎绋婤lock

鏃ュ織浜嬩欢鍑洪槦鍚庝細鎸夌収鐢ㄦ埛閰嶇疆鐨勮緭鍑烘牱寮忥紝瀵规棩蹇楀唴瀹硅繘琛屾牸寮忓寲杞崲锛屾鏃朵粛鐒跺彲鑳借Е鍙戣В鏋愩€佸姞杞藉紓甯稿爢鏍堢被銆傚洜姝わ紝鏃ュ織鍑洪槦鍚庨伩鍏嶇嚎绋婤lock鐨勬牴鏈В鍐虫柟娉曟槸鍦ㄥ紓甯告牸寮忓寲杞崲鏃堕伩鍏嶈В鏋愩€佸姞杞藉紓甯稿爢鏍堢被銆傚厛璇存柟妗堢粨璁猴細鏄惧紡閰嶇疆鏃ュ織杈撳嚭鏍峰紡%ex鏉ヤ唬鏇块粯璁ょ殑%xEx锛岄伩鍏嶅鏃ュ織鍐呭鏍煎紡鍖栨椂瑙f瀽銆佸姞杞藉紓甯稿爢鏍堢被銆備笅闈㈤€氳繃鍒嗘瀽鏃ュ織鍐呭鏍煎紡鍖栧鐞嗘祦绋嬫潵浠嬬粛瑙e喅鏂规銆備互PatternLayout涓轰緥锛屾棩蹇楀唴瀹规牸寮忓寲杞崲娴佺▼閾捐矾涓猴細Layout->PatternFormatter->LogEventPatternConverter銆傚叾涓璍ogEventPatternConverter鏄釜鎶借薄绫伙紝鏈変袱涓鐞嗗紓甯哥殑鏍煎紡鍖栬浆鎹㈠叿浣撳疄鐜扮被锛屽垎鍒槸ThrowablePatternConverter鍜孍xtendedThrowablePatternConverter銆?/span>

// org.apache.logging.log4j.core.layout.PatternLayout

// 灏?LogEvent 杞崲涓哄彲浠ヨ緭鍑虹殑 String
@Override
public String toSerializable(final LogEvent event) {
// 鐢?PatternSerializer 瀵规棩蹇椾簨浠舵牸寮忓寲澶勭悊
return eventSerializer.toSerializable(event);
}
// org.apache.logging.log4j.core.layout.PatternLayout.PatternSerializer

@Override
public String toSerializable(final LogEvent event) {
final StringBuilder sb = getStringBuilder();
try {
return toSerializable(event, sb).toString();
} finally {
trimToMaxSize(sb);
}
}

@Override
public StringBuilder toSerializable(final LogEvent event, final StringBuilder buffer) {
final int len = formatters.length;
for (int i = 0; i < len; i++) {
// 鐢?PatternFormatter 瀵规棩蹇椾簨浠舵牸寮忓寲澶勭悊
formatters[i].format(event, buffer);
}
if (replace != null) { // creates temporary objects
String str = buffer.toString();
str = replace.format(str);
buffer.setLength(0);
buffer.append(str);
}
return buffer;
}
// org.apache.logging.log4j.core.pattern.PatternFormatter

public void format(final LogEvent event, final StringBuilder buf) {
if (skipFormattingInfo) {
// 鐢?LogEventPatternConverter 瀵规棩蹇椾簨浠惰繘琛屾牸寮忓寲澶勭悊
converter.format(event, buf);
} else {
formatWithInfo(event, buf);
}
}

private void formatWithInfo(final LogEvent event, final StringBuilder buf) {
final int startField = buf.length();
// 鐢?LogEventPatternConverter 瀵规棩蹇椾簨浠惰繘琛屾牸寮忓寲澶勭悊
converter.format(event, buf);
field.format(startField, buf);
}
// org.apache.logging.log4j.core.pattern.LogEventPatternConverter

public abstract class LogEventPatternConverter extends AbstractPatternConverter {

/**
* 灏嗘棩蹇椾簨浠?LogEvent 杞崲涓?String
* Formats an event into a string buffer.
*
* @param event event to format, may not be null.
* @param toAppendTo string buffer to which the formatted event will be appended. May not be null.
*/
public abstract void format(final LogEvent event, final StringBuilder toAppendTo);

}

鏃ュ織妗嗘灦瀵瑰紓甯歌繘琛屾牸寮忓寲杞崲鏃讹紝鏈夊涓嬩袱涓厤缃」鍙弬鑰冿紝榛樿閰嶇疆鏄?xEx銆?strong>1. %ex锛屼粎杈撳嚭寮傚父淇℃伅锛屼笉鑾峰彇鎵╁睍淇℃伅锛圝AR鏂囦欢鍚嶇О鍜岀増鏈俊鎭級瀵瑰簲鐨勬牸寮忚浆鍖栫被鏄疶hrowablePatternConverter锛屽湪format鏂规硶鍐呴儴骞舵病鏈夎幏鍙朤hrowableProxy瀵硅薄锛屾墍浠ヤ笉浼氳Е鍙戣В鏋愩€佸姞杞藉紓甯稿爢鏍堢被銆?/span>

// org.apache.logging.log4j.core.pattern.ThrowablePatternConverter

@Plugin(name = "ThrowablePatternConverter", category = PatternConverter.CATEGORY)
@ConverterKeys({ "ex", "throwable", "exception" })
public class ThrowablePatternConverter extends LogEventPatternConverter {

/**
* {@inheritDoc}
*/
@Override
public void format(final LogEvent event, final StringBuilder buffer) {
final Throwable t = event.getThrown();

if (isSubShortOption()) {
formatSubShortOption(t, getSuffix(event), buffer);
}
else if (t != null && options.anyLines()) {
formatOption(t, getSuffix(event), buffer);
}
}

private boolean isSubShortOption() {
return ThrowableFormatOptions.MESSAGE.equalsIgnoreCase(rawOption) ||
ThrowableFormatOptions.LOCALIZED_MESSAGE.equalsIgnoreCase(rawOption) ||
ThrowableFormatOptions.FILE_NAME.equalsIgnoreCase(rawOption) ||
ThrowableFormatOptions.LINE_NUMBER.equalsIgnoreCase(rawOption) ||
ThrowableFormatOptions.METHOD_NAME.equalsIgnoreCase(rawOption) ||
ThrowableFormatOptions.CLASS_NAME.equalsIgnoreCase(rawOption);
}

private void formatSubShortOption(final Throwable t, final String suffix, final StringBuilder buffer) {
StackTraceElement[] trace;
StackTraceElement throwingMethod = null;
int len;

if (t != null) {
trace = t.getStackTrace();
if (trace !=null && trace.length > 0) {
throwingMethod = trace[0];
}
}

if (t != null && throwingMethod != null) {
String toAppend = Strings.EMPTY;

if (ThrowableFormatOptions.CLASS_NAME.equalsIgnoreCase(rawOption)) {
toAppend = throwingMethod.getClassName();
}
else if (ThrowableFormatOptions.METHOD_NAME.equalsIgnoreCase(rawOption)) {
toAppend = throwingMethod.getMethodName();
}
else if (ThrowableFormatOptions.LINE_NUMBER.equalsIgnoreCase(rawOption)) {
toAppend = String.valueOf(throwingMethod.getLineNumber());
}
else if (ThrowableFormatOptions.MESSAGE.equalsIgnoreCase(rawOption)) {
toAppend = t.getMessage();
}
else if (ThrowableFormatOptions.LOCALIZED_MESSAGE.equalsIgnoreCase(rawOption)) {
toAppend = t.getLocalizedMessage();
}
else if (ThrowableFormatOptions.FILE_NAME.equalsIgnoreCase(rawOption)) {
toAppend = throwingMethod.getFileName();
}

len = buffer.length();
if (len > 0 && !Character.isWhitespace(buffer.charAt(len - 1))) {
buffer.append(' ');
}
buffer.append(toAppend);

if (Strings.isNotBlank(suffix)) {
buffer.append(' ');
buffer.append(suffix);
}
}
}

private void formatOption(final Throwable throwable, final String suffix, final StringBuilder buffer) {
final StringWriter w = new StringWriter();

throwable.printStackTrace(new PrintWriter(w));
final int len = buffer.length();
if (len > 0 && !Character.isWhitespace(buffer.charAt(len - 1))) {
buffer.append(' ');
}
if (!options.allLines() || !Strings.LINE_SEPARATOR.equals(options.getSeparator()) || Strings.isNotBlank(suffix)) {
final StringBuilder sb = new StringBuilder();
final String[] array = w.toString().split(Strings.LINE_SEPARATOR);
final int limit = options.minLines(array.length) - 1;
final boolean suffixNotBlank = Strings.isNotBlank(suffix);
for (int i = 0; i <= limit; ++i) {
sb.append(array[i]);
if (suffixNotBlank) {
sb.append(' ');
sb.append(suffix);
}
if (i < limit) {
sb.append(options.getSeparator());
}
}
buffer.append(sb.toString());

} else {
buffer.append(w.toString());
}
}

/**
* This converter obviously handles throwables.
*
* @return true.
*/
@Override
public boolean handlesThrowable() {
return true;
}

protected String getSuffix(final LogEvent event) {
//noinspection ForLoopReplaceableByForEach
final StringBuilder toAppendTo = new StringBuilder();
for (int i = 0, size = formatters.size(); i < size; i++) {
formatters.get(i).format(event, toAppendTo);
}
return toAppendTo.toString();
}

public ThrowableFormatOptions getOptions() {
return options;
}
}

2. %xEx锛屼笉浠呰緭鍑哄紓甯镐俊鎭紝鍚屾椂鑾峰彇鎵╁睍淇℃伅瀵瑰簲鐨勬牸寮忚浆鍖栫被鏄疎xtendedThrowablePatternConverter锛屽湪format鏂规硶鍐呴儴鑾峰彇浜員hrowableProxy瀵硅薄锛屾鏃朵竴瀹氫細瑙﹀彂瑙f瀽銆佸姞杞藉紓甯稿爢鏍堢被銆?/span>

// org.apache.logging.log4j.core.pattern.ExtendedThrowablePatternConverter

@Plugin(name = "ExtendedThrowablePatternConverter", category = PatternConverter.CATEGORY)
@ConverterKeys({ "xEx", "xThrowable", "xException" })
public final class ExtendedThrowablePatternConverter extends ThrowablePatternConverter {

/**
* {@inheritDoc}
*/
@Override
public void format(final LogEvent event, final StringBuilder toAppendTo) {
// 鑾峰彇 ThrowableProxy 瀵硅薄锛岃Е鍙戣В鏋愩€佸姞杞藉紓甯稿爢鏍堢被
final ThrowableProxy proxy = event.getThrownProxy();
final Throwable throwable = event.getThrown();
if ((throwable != null || proxy != null) && options.anyLines()) {
if (proxy == null) {
super.format(event, toAppendTo);
return;
}
final String extStackTrace = proxy.getExtendedStackTraceAsString(options.getIgnorePackages(),
options.getTextRenderer(), getSuffix(event), options.getSeparator());
final int len = toAppendTo.length();
if (len > 0 && !Character.isWhitespace(toAppendTo.charAt(len - 1))) {
toAppendTo.append(' ');
}
toAppendTo.append(extStackTrace);
}
}

}

5. 鏈€浣冲疄璺?/span>

鏈珷鑺備富瑕佺粨鍚堥」鐩湪鏃ュ織浣跨敤鏂归潰鐨勪竴绯诲垪韪╁潙缁忓巻鍜屽疄璺电粡楠岋紝鎬荤粨浜嗕竴浠藉叧浜庢棩蹇楅厤缃殑鏈€浣冲疄璺碉紝渚涘ぇ瀹跺弬鑰冦€?/span>

  1. 寤鸿鏃ュ織閰嶇疆鏂囦欢涓鎵€鏈堿ppender鐨凱atternLayout閮藉鍔?ex閰嶇疆锛屽洜涓哄鏋滄病鏈夋樉寮忛厤缃?ex锛屽垯寮傚父鏍煎紡鍖栬緭鍑虹殑榛樿閰嶇疆鏄?xEx锛屾鏃朵細鎵撳嵃寮傚父鐨勬墿灞曚俊鎭紙JAR鍚嶇О鍜岀増鏈?/span>锛夛紝鍙兘瀵艰嚧涓氬姟绾跨▼Block銆?/span>
  2. 涓嶅缓璁棩蹇楅厤缃枃浠朵腑浣跨敤AsyncAppender锛屽缓璁嚜瀹氫箟Appender瀹炵幇锛屽洜涓篈syncAppender鏄棩蹇楁鏋堕粯璁ゆ彁渚涚殑锛岀洰鍓嶆渶鏂扮増鏈腑浠嶇劧瀛樺湪鏃ュ織浜嬩欢鍏ラ槦鍓嶅氨瑙﹀彂鍔犺浇寮傚父鍫嗘爤绫荤殑闂锛屽彲鑳藉鑷翠笟鍔$嚎绋婤lock銆?/span>
  3. 涓嶅缓璁敓浜х幆澧冧娇鐢–onsoleAppender锛屽洜涓鸿緭鍑烘棩蹇楀埌Console鏃舵湁synchronized鍚屾鎿嶄綔锛岄珮骞跺彂鍦烘櫙涓嬮潪甯稿鏄撳鑷翠笟鍔$嚎绋婤lock銆?/span>
  4. 涓嶅缓璁湪閰嶇疆鏂囦欢涓娇鐢?lt;AsyncLogger>鏍囩锛屽洜涓烘棩蹇椾簨浠跺厓绱犲湪鍏ラ槦鍓嶅氨浼氳Е鍙戝姞杞藉紓甯稿爢鏍堢被锛屽彲鑳藉鑷翠笟鍔$嚎绋婤lock銆傚鏋滃笇鏈涗娇鐢↙og4j2鎻愪緵鐨勫紓姝ユ棩蹇桝syncLogger锛屽缓璁厤缃甃og4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector鍙傛暟锛屽紑鍚紓姝ユ棩蹇椼€?/span>

涓嬮潰鎻愪緵涓€浠絣og4j2.xml閰嶇疆绀轰緥锛?/span>

<configuration status="warn">
<appenders>
<Console name="Console" target="SYSTEM_OUT" follow="true">
<PatternLayout pattern="%d{yyyy/MM/dd HH:mm:ss.SSS} %t [%p] %c{1} (%F:%L) %msg%n %ex" />
</Console>

<XMDFile name="ShepherdLog" fileName="shepherd.log">
<PatternLayout pattern="%d{yyyy/MM/dd HH:mm:ss.SSS} %t [%p] %c{1} (%F:%L) %msg%n %ex" />
</XMDFile>

<!--XMDFile寮傛纾佺洏鏃ュ織閰嶇疆绀轰緥-->
<!--榛樿鎸夊ぉ&鎸?12M鏂囦欢澶у皬鍒囧垎鏃ュ織锛岄粯璁ゆ渶澶氫繚鐣?0涓棩蹇楁枃浠躲€?->
<!--娉ㄦ剰锛歠ileName鍓嶄細鑷姩澧炲姞鏂囦欢璺緞锛屽彧閰嶇疆鏂囦欢鍚嶅嵆鍙?->
<XMDFile name="LocalServiceLog" fileName="request.log">
<PatternLayout pattern="%d{yyyy/MM/dd HH:mm:ss.SSS} %t [%p] %c{1} (%F:%L) %msg%n %ex" />
</XMDFile>

<!-- 浣跨敤鑷畾涔夌殑AsyncScribeAppender浠f浛鍘熸湁鐨凙sycncAppender -->
<AsyncScribe name="LogCenterAsync" blocking="false">
<!-- 鍦ㄦ寚瀹氭棩蹇楀悕鏂归潰锛宻cribeCategory 鍜?appkey 涓よ€呰嚦灏戝瓨鍦ㄤ竴绉嶏紝涓?scribeCategory 楂樹簬 appkey銆?->
<!-- <Property name="scribeCategory">data_update_test_lc</Property> -->
<LcLayout/>
</AsyncScribe>
</appenders>

<loggers>
<logger name="com.sankuai.shepherd" level="info" additivity="false">
<AppenderRef ref="ShepherdLog" level="warn"/>
<AppenderRef ref="LogCenterAsync" level="info"/>
</logger>

<root level="info">
<!--Console鏃ュ織鏄悓姝ャ€侀樆濉炵殑锛屾帹鑽愬彧鍦ㄦ湰鍦拌皟璇曟椂浣跨敤锛岀嚎涓婂皢璇ラ厤缃幓鎺?->
<!--appender-ref ref="Console" /-->
<appender-ref ref="LocalServiceLog"/>
<appender-ref ref="LogCenterAsync"/>
</root>
</loggers>
</configuration>

6. 浣滆€呯畝浠?/span>

蹇楁磱銆侀檲瓒呫€佹潕鏁忋€佸嚡鏅栥€佹鐞︾瓑锛屽潎鏉ヨ嚜缇庡洟鍩虹鎶€鏈儴-搴旂敤涓棿浠跺洟闃熴€?/span>

有关鏃ュ織瀵艰嚧绾跨▼Block鐨勮繖浜涘潙锛屼綘涓嶅緱涓嶉槻的更多相关文章

  1. ruby - RSpec - 使用测试替身作为 block 参数 - 2

    我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

  2. ruby-on-rails - Enumerator.new 如何处理已通过的 block ? - 2

    我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m

  3. ruby - 在匿名 block 中产生 - 2

    我没有理解以下行为(另请参阅inthisSOthread):defdef_testputs'def_test.in'yieldifblock_given?puts'def_test.out'enddef_testdoputs'def_testok'endblock_test=procdo|&block|puts'block_test.in'block.callifblockputs'block_test.out'endblock_test.calldoputs'block_test'endproc_test=procdoputs'proc_test.in'yieldifblock_gi

  4. ruby - Ruby 中的单 block AES 解密 - 2

    我需要尝试一些AES片段。我有一些密文c和一个keyk。密文已使用AES-CBC加密,并在前面加上IV。不存在填充,纯文本的长度是16的倍数。所以我这样做:aes=OpenSSL::Cipher::Cipher.new("AES-128-CCB")aes.decryptaes.key=kaes.iv=c[0..15]aes.update(c[16..63])+aes.final它工作得很好。现在我需要手动执行CBC模式,所以我需要单个block的“普通”AES解密。我正在尝试这个:aes=OpenSSL::Cipher::Cipher.new("AES-128-ECB")aes.dec

  5. ruby-on-rails - 无法在 Rails 助手中捕获 block 的输出 - 2

    我在使用自定义RailsFormBuilder时遇到了问题,从昨天晚上开始我就发疯了。基本上我想对我的构建器方法之一有一个可选block,以便我可以在我的主要content_tag中显示其他内容。:defform_field(method,&block)content_tag(:div,class:'field')doconcatlabel(method,"Label#{method}")concattext_field(method)capture(&block)ifblock_given?endend当我在我的一个Slim模板中调用该方法时,如下所示:=f.form_field:e

  6. ruby - 具有两个参数的 block - 2

    我从用户Hirolau那里找到了这段代码:defsum_to_n?(a,n)a.combination(2).find{|x,y|x+y==n}enda=[1,2,3,4,5]sum_to_n?(a,9)#=>[4,5]sum_to_n?(a,11)#=>nil我如何知道何时可以将两个参数发送到预定义方法(如find)?我不清楚,因为有时它不起作用。这是重新定义的东西吗? 最佳答案 如果您查看Enumerable#find的文档,您会发现它只接受一个block参数。您可以将它发送两次的原因是因为Ruby可以方便地让您根据它的“并行赋

  7. ruby - 在参数为 `yield self` 的方法中使用 `&block` 和在没有参数 `yield self` 的方法中使用 `&block` 有什么区别吗? - 2

    我明白了defa(&block)block.call(self)end和defa()yieldselfend导致相同的结果,如果我假设有这样一个blocka{}。我的问题是-因为我偶然发现了一些这样的代码,它是否有任何区别或者是否有任何优势(如果我不使用变量/引用block):defa(&block)yieldselfend这是一个我不理解&block用法的具体案例:defrule(code,name,&block)@rules=[]if@rules.nil?@rules 最佳答案 我能想到的唯一优点就是自省(introspecti

  8. ruby-on-rails - 连接字符串时如何在 <%=%> block 内输出 html_safe? - 2

    考虑一下:现在这些情况:#output:http://domain.com/?foo=1&bar=2#output:http://domain.com/?foo=1&bar=2#output:http://domain.com/?foo=1&bar=2#output:http://domain.com/?foo=1&bar=2我需要用其他字符串输出URL。我如何保证&符号不会被转义?由于我无法控制的原因,我无法发送&。求助!把我的头发拉到这里:\编辑:为了澄清,我实际上有一个像这样的数组:@images=[{:id=>"fooid",:url=>"http://

  9. ruby - 从 sinatra 中的 before do block 返回不同的值 - 2

    有没有办法在sinatra的beforedoblock中停止执行并返回不同的值?beforedo#codeishere#Iwouldliketo'return"Message"'#Iwouldlike"/home"tonotgetcalled.end//restofthecodeget'/home'doend 最佳答案 beforedohalt401,{'Content-Type'=>'text/plain'},'Message!'end如果你愿意,你可以只指定状态,这里有状态、标题和正文的例子

  10. ruby - 为什么 return 关键字会导致我的 'if block' 出现问题? - 2

    下面的代码工作正常:person={:a=>:A,:b=>:B,:c=>:C}berson={:a=>:A1,:b=>:B1,:c=>:C1}kerson=person.merge(berson)do|key,oldv,newv|ifkey==:aoldvelsifkey==:bnewvelsekeyendendputskerson.inspect但是如果我在“ifblock”中添加return,我会得到一个错误:person={:a=>:A,:b=>:B,:c=>:C}berson={:a=>:A1,:b=>:B1,:c=>:C1}kerson=person.merge(berson

随机推荐