浣滆€咃細蹇楁磱 闄堣秴 鏉庢晱绛?/p>
鐮斿彂浜哄憳鍦ㄩ」鐩紑鍙戜腑涓嶅彲閬垮厤鍦拌浣跨敤鏃ュ織锛岄€氳繃瀹冩潵璁板綍淇℃伅鍜屾帓鏌ラ棶棰樸€侫pache Log4j2鎻愪緵浜嗙伒娲讳笖寮哄ぇ鐨勬棩蹇楁鏋讹紝铏界劧涓婃墜姣旇緝蹇紝浣嗙◢鏈変笉鎱庝篃闈炲父瀹规槗韪┾€滃潙鈥濄€?/p>
鏃ュ織瀵圭▼搴忕殑閲嶈鎬т笉瑷€鑰屽柣銆傚畠寰堚€滃ぇ鈥濓紝鎴戜滑鍦ㄩ」鐩腑缁忓父閫氳繃鏃ュ織鏉ヨ褰曚俊鎭拰鎺掓煡闂锛岀浉鍏充唬鐮侀殢澶勫彲瑙併€傚畠涔熷緢鈥滃皬鈥濓紝浣滀负杈呭姪宸ュ叿锛屾棩蹇椾娇鐢ㄧ畝鍗曘€佷笂鎵嬪揩锛屾垜浠€氬父涓嶄細鑺辫垂杩囧绮惧姏鑰楀湪鏃ュ織涓娿€備絾鐪嬩技涓嶈捣鐪肩殑鏃ュ織涔熼殣钘忕潃鍚勭鍚勬牱鐨勨€滃潙鈥濓紝濡傛灉浣跨敤涓嶅綋锛屽畠涓嶄粎涓嶈兘甯姪鎴戜滑锛屽弽鑰岃繕鍙兘闄嶄綆鏈嶅姟鎬ц兘锛岀敋鑷虫嫋鍨垜浠殑鏈嶅姟銆?/p>
鏃ュ織瀵艰嚧绾跨▼Block鐨勯棶棰橈紝鐩镐俊浣犳垨璁稿凡缁忛亣鍒拌繃锛屽姝ゅ簲璇ユ繁鏈変綋浼氾紱鎴栬浣犺繕娌¢亣鍒拌繃锛屼絾涓嶄唬琛ㄦ病鏈夐棶棰橈紝鍙槸鍙兘杩樻病鏈夎Е鍙戣€屽凡銆傛湰鏂囦富瑕佷粙缁嶇編鍥㈢粺涓€API缃戝叧鏈嶅姟Shepherd锛堝弬瑙併€婄櫨浜胯妯PI缃戝叧鏈嶅姟Shepherd鐨勮璁′笌瀹炵幇銆嬩竴鏂囷級鍦ㄥ疄璺典腑鎵€韪╄繃鐨勫叧浜庢棩蹇楀鑷寸嚎绋婤lock鐨勯偅浜涒€滃潙鈥濓紝鐒跺悗鍐嶅垎浜竴浜涢伩鈥滃潙鈥濈粡楠屻€?/p>
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>
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>鏈珷鑺備富瑕佽褰曢」鐩繃鍘讳竴娈垫椂闂达紝鎴戜滑鎵€閬囧埌鐨勪竴绯诲垪鏃ュ織瀵艰嚧鐨勭嚎绋婤lock闂锛屽苟閫愪釜娣卞叆鍒嗘瀽闂鏍瑰洜銆?/span>
鏀跺埌鈥渏vm.thread.blocked.count鈥濆憡璀﹀悗绔嬪埢閫氳繃鐩戞帶骞冲彴鏌ョ湅绾跨▼鐩戞帶鎸囨爣锛屽綋鏃剁殑绾跨▼鍫嗘爤濡傚浘2鍜屽浘3鎵€绀恒€?/span>


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

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

鍥? PringStream浠g爜鐗囨
浣嗕笂杩扮寽娴嬩粛鏈変竴浜涘€煎緱鎺ㄦ暡鐨勫湴鏂癸細
缁х画娌跨潃绾跨▼鍫嗘爤璋冪敤閾捐矾鍒嗘瀽锛屽彲浠ョ湅鍑烘槸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>
鍦ㄦ湰椤圭洰鐨勬棩蹇楅厤缃枃浠朵腑鍙互鐪嬪埌锛孉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>
浣嗛」鐩墍鐢ㄦ棩蹇楁鏋剁増鏈殑榛樿瀹炵幇鐪嬭捣鏉ュ瓨鍦ㄤ竴浜涗笉澶悎鐞嗙殑鍦版柟锛?/p>
鎵€浠ワ紝鍦ㄥ绾跨▼鍦烘櫙涓嬶紝鍙兘鏈夊ぇ閲忓紓甯告棩蹇楀悓鏃惰DefaultErrorHandler澶勭悊锛屽甫鏉ョ嚎绋嬪畨鍏ㄩ棶棰樸€傚€煎緱涓€鎻愮殑鏄紝璇ラ棶棰樺凡鏈夌浉鍏矷ssue: DefaultErrorHandler can not share values across threads鍙嶉缁欑ぞ鍖猴紝骞跺湪2.15.0鐗堟湰涓繘琛屼簡淇銆?/p>
浠庝笂杩癉efaultErrorHandler浠g爜涓彲浠ョ湅鍒帮紝鐪熸璐熻矗澶勭悊鏃ュ織鐨勬槸StatusLogger锛岀户缁窡杩涗唬鐮佽繘鍏ogMessage鏂规硶锛屾柟娉曟墽琛岄€昏緫濡備笅锛?/p>
// 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鏄浣曡鍒涘缓鐨勶紵
閫氬父鏉ヨ锛屾瘡涓」鐩兘浼氭湁涓€涓棩蹇楅厤缃枃浠讹紙濡俵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>鏍囩鍙互閰嶇疆灞炴€у瓧娈碉紝閮ㄥ垎瀛楁濡備笅鎵€绀猴細
鍦ㄦ湰椤圭洰鐨勬棩蹇楅厤缃枃浠朵腑鍙互鐪嬪埌骞舵病鏈夎缃瓹onfiguration鐨刣est灞炴€у€硷紝鎵€浠ユ棩蹇楃洿鎺ヨ緭鍑哄埌System.out銆?/span>
涓婃枃鎻愬埌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.
鏃ュ織閲忚繃澶у鑷碅syncAppender鏃ュ織闃熷垪琚墦婊★紝鏂扮殑鏃ュ織浜嬩欢鏃犳硶鍏ラ槦锛岃繘鑰岀敱ErrorHandler澶勭悊鏃ュ織锛屽悓鏃剁敱浜嶦rrorHandler瀛樺湪绾跨▼瀹夊叏闂锛屽鑷村ぇ閲忔棩蹇楄緭鍑哄埌浜咰onsole锛岃€孋onsole鍦ㄨ緭鍑烘棩蹇楀埌PrintStream杈撳嚭娴佹椂锛屽瓨鍦╯ynchronized鍚屾浠g爜鍧楋紝鎵€浠ュ湪楂樺苟鍙戝満鏅笅瀵艰嚧绾跨▼Block銆?/span>
鏀跺埌鈥渏vm.thread.blocked.count鈥濆憡璀﹀悗绔嬪埢閫氳繃鐩戞帶骞冲彴鏌ョ湅绾跨▼鐩戞帶鎸囨爣锛屽綋鏃剁殑绾跨▼鍫嗘爤濡備笅鍥?鍜屽浘7鎵€绀恒€?/span>

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

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

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

鍥? 涓氬姟寮傚父鍫嗘爤浜?/span>
杩欎簺涓氬姟寮傚父浼氭槸瀵艰嚧绾跨▼Block鐨勫箷鍚庡厓鍑跺悧锛熸帴涓嬫潵鏈珷鑺傚皢缁撳悎濡備笅鍥?0鎵€绀虹殑璋冪敤閾捐矾娣卞叆鍒嗘瀽绾跨▼Block鐨勬牴鍥犮€?/span>
鍥?0 鏃ュ織璋冪敤閾捐矾
浠嶣locked绾跨▼鍫嗘爤涓彲浠ョ湅鍑猴紝绾跨▼闃诲鍦ㄧ被鍔犺浇娴佺▼涓婏紝鏌ョ湅WebAppClassLoader鐩稿叧浠g爜鐗囨濡備笅鍥?1鎵€绀猴紝鍙戠幇鍔犺浇绫绘椂纭疄浼氭牴鎹被鍚嶆潵鍔爏ynchronized鍚屾鍧楋紝鍥犳鍒濇鐚滄祴鏄被鍔犺浇瀵艰嚧绾跨▼Block銆?/span>

鍥?1 WebAppClassLoader浣嗕笂杩扮寽娴嬭繕鏈変竴浜涘€煎緱鎺ㄦ暡鐨勫湴鏂癸細
缁х画浠嶣locked绾跨▼鍫嗘爤鐫€鎵嬪垎鏋愶紝鏌ョ湅鍫嗘爤涓殑ThrowableProxy鐩稿叧浠g爜锛屽彂鐜板叾鏋勯€犲嚱鏁颁細閬嶅巻鏁翠釜寮傚父鍫嗘爤涓殑鎵€鏈夊爢鏍堝厓绱狅紝鏈€缁堣幏鍙栨墍鏈夊爢鏍堝厓绱犵被鎵€鍦ㄧ殑JAR鍚嶇О鍜岀増鏈俊鎭€傚叿浣撴祦绋嬪涓嬶細
// 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埌搴曟槸浠€涔堢被鏃犳硶鍔犺浇鍛紵
瑕佹壘鍒板叿浣撴槸浠€涔堢被鏃犳硶鍔犺浇锛屽綊鏍圭粨搴曡繕鏄鍒嗘瀽涓氬姟寮傚父鐨勫叿浣撳爢鏍堛€?/span>

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

鍥?3 涓氬姟寮傚父鍫嗘爤浜?/span>
瀵规瘮濡傚浘12鍜屽浘13鎵€绀虹殑涓や唤涓氬姟寮傚父鍫嗘爤锛屾垜浠彲浠ョ湅鍒颁袱浠藉爢鏍堝熀鏈浉浼硷紝涓斿ぇ澶氭暟绫婚兘鏄緢鏅€氱殑绫伙紝浣嗘槸鍞竴涓嶅悓鐨勫湴鏂瑰湪浜庯細
浠庡瓧闈俊鎭腑涓嶉毦鐚滄祴鍑鸿繖涓庡弽灏勮皟鐢ㄧ浉鍏筹紝浣嗛棶棰樻槸杩欎袱浠藉爢鏍堝搴旂殑鍏跺疄鏄悓涓€浠戒笟鍔′唬鐮侊紝涓轰粈涔堜細浜х敓涓や唤涓嶅悓鐨勫紓甯稿爢鏍堬紵鏌ラ槄鐩稿叧璧勬枡寰楃煡锛岃繖涓嶫VM鍙嶅皠璋冪敤鐩稿叧锛孞VM瀵瑰弽灏勮皟鐢ㄥ垎涓ょ鎯呭喌锛?/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>

鍥?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鐨勫鐞嗘儏鍐电◢鏈夊樊寮傦細
缁间笂锛屼笉绠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;
}
Log4j2鎵撳嵃寮傚父鏃ュ織鏃讹紝AsyncAppender浼氬厛鍒涘缓鏃ュ織浜嬩欢蹇収锛屽苟杩涗竴姝ヨЕ鍙戣В鏋愩€佸姞杞藉紓甯稿爢鏍堢被銆侸VM閫氳繃鐢熸垚瀛楄妭鐮佺殑鏂瑰紡浼樺寲鍙嶅皠璋冪敤鎬ц兘锛屼絾璇ュ姩鎬佺敓鎴愮殑绫绘棤娉曡WebAppClassLoader绫诲姞杞藉櫒鍔犺浇锛屽洜姝ゅ綋澶ч噺鍖呭惈鍙嶅皠璋冪敤鐨勫紓甯稿爢鏍堣杈撳嚭鍒版棩蹇楁椂锛屼細棰戠箒鍦拌Е鍙戠被鍔犺浇锛岀敱浜庣被鍔犺浇杩囩▼鏄痵ynchronized鍚屾鍔犻攣鐨勶紝涓旀瘡娆″姞杞介兘闇€瑕佽鍙栨枃浠讹紝閫熷害杈冩參锛屼粠鑰屽鑷寸嚎绋婤lock銆?/span>
鏀跺埌鈥渏vm.thread.blocked.count鈥濆憡璀﹀悗锛岀珛鍒婚€氳繃鐩戞帶骞冲彴鏌ョ湅绾跨▼鐩戞帶鎸囨爣锛屽綋鏃剁殑绾跨▼鍫嗘爤濡備笅鍥?6鍜屽浘17鎵€绀猴細

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

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

鍥?9 鏃ュ織璋冪敤閾捐矾
浠嶣locked绾跨▼鍫嗘爤涓彲浠ョ湅鍑猴紝绾跨▼闃诲鍦ㄧ被鍔犺浇涓婏紝璇ョ嚎绋嬪爢鏍堝拰涓婅堪鈥淎syncAppender瀵艰嚧绾跨▼Block鈥濇渚嬬浉浼硷紝杩欓噷涓嶅啀閲嶅鍒嗘瀽銆?/span>
鍘熷洜鍜屼笂杩扳€淎syncAppender瀵艰嚧绾跨▼Block鈥濇渚嬬浉浼硷紝杩欓噷涓嶅啀閲嶅鍒嗘瀽銆?/span>
涓婅堪鈥淎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鍙弬鑰?
鍊煎緱涓€鎻愮殑鏄紝璇ug鍦↗DK9鐗堟湰宸茬粡淇锛屽疄闄呮祴璇曚腑鍙戠幇锛屽湪JDK8鐨勯珮鐗堟湰濡?U171绛夊凡淇璇ug锛屽紓甯稿爢鏍堜腑涓嶄細鏈夌被浼?$Lambda$鐨勫爢鏍堜俊鎭紝绀轰緥濡備笅鍥?1鎵€绀猴細

鍥?1 JDK8U171鐗堟湰涓婰ambda寮傚父鍫嗘爤绀轰緥
鍘熷洜鍜屼笂杩扳€淎syncAppender瀵艰嚧绾跨▼Block鈥濇渚嬬浉浼硷紝涓嶅啀閲嶅鍒嗘瀽銆?/span>
Log4j2鎵撳嵃寮傚父鏃ュ織鏃讹紝AsyncAppender浼氬厛鍒涘缓鏃ュ織浜嬩欢蹇収锛屽苟杩涗竴姝ヨЕ鍙戣В鏋愩€佸姞杞藉紓甯稿爢鏍堢被銆侸DK 8浣庣増鏈腑浣跨敤Lambda琛ㄨ揪寮忔墍鐢熸垚鐨勫紓甯稿爢鏍堢被鏃犳硶琚玏ebAppClassLoader绫诲姞杞藉櫒鍔犺浇锛屽洜姝わ紝褰撳ぇ閲忓寘鍚獿ambda琛ㄨ揪寮忚皟鐢ㄧ殑寮傚父鍫嗘爤琚緭鍑哄埌鏃ュ織鏃讹紝浼氶绻佸湴瑙﹀彂绫诲姞杞斤紝鐢变簬绫诲姞杞借繃绋嬫槸synchronized鍚屾鍔犻攣鐨勶紝涓旀瘡娆″姞杞介兘闇€瑕佽鍙栨枃浠讹紝閫熷害杈冩參锛屼粠鑰屽鑷翠簡绾跨▼Block銆?/span>
鏀跺埌鈥渏vm.thread.blocked.count鈥濆憡璀﹀悗绔嬪埢閫氳繃鐩戞帶骞冲彴鏌ョ湅绾跨▼鐩戞帶鎸囨爣锛屽綋鏃剁殑绾跨▼鍫嗘爤濡備笅鍥?2鍜屽浘23鎵€绀恒€?/span>


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

鍥?4 鏃ュ織璋冪敤閾捐矾
鍘熷洜鍜屼笂杩扳€淎syncAppender瀵艰嚧绾跨▼Block鈥濇渚嬬浉浼硷紝杩欓噷涓嶅啀閲嶅鍒嗘瀽銆?/span>
鍘熷洜鍜屼笂杩扳€淎syncAppender瀵艰嚧绾跨▼Block鈥濇渚嬬浉浼硷紝杩欓噷涓嶅啀閲嶅鍒嗘瀽銆?/span>
鍘熷洜鍜屼笂杩扳€淎syncAppender瀵艰嚧绾跨▼Block鈥濇渚嬬浉浼硷紝杩欓噷涓嶅啀閲嶅鍒嗘瀽銆?/span>
鍦ㄥ紑濮嬪垎鏋愬師鍥犱箣鍓嶏紝鍏堢悊娓呮Log4j2鍏充簬鏃ュ織鐨勫嚑涓噸瑕佹蹇碉細
鎬荤殑鏉ヨ锛?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;
}Log4j2鎵撳嵃寮傚父鏃ュ織鏃讹紝AsyncLoggerConfig浼氬垵濮嬪寲Disruptor RingBuffer鏃ュ織鍏冪礌瀛楁锛屽苟杩涗竴姝ヨЕ鍙戣В鏋愩€佸姞杞藉紓甯稿爢鏍堢被銆侸VM閫氳繃鐢熸垚瀛楄妭鐮佺殑鏂瑰紡浼樺寲鍙嶅皠璋冪敤鎬ц兘锛屼絾璇ュ姩鎬佺敓鎴愮殑绫绘棤娉曡WebAppClassLoader绫诲姞杞藉櫒鍔犺浇锛屽洜姝ゅ綋澶ч噺鍖呭惈鍙嶅皠璋冪敤鐨勫紓甯稿爢鏍堣杈撳嚭鍒版棩蹇楁椂锛屼細棰戠箒鍦拌Е鍙戠被鍔犺浇锛岀敱浜庣被鍔犺浇杩囩▼鏄痵ynchronized鍚屾鍔犻攣鐨勶紝涓旀瘡娆″姞杞介兘闇€瑕佽鍙栨枃浠讹紝閫熷害杈冩參锛屼粠鑰屽鑷寸嚎绋婤lock銆?/span>
鏈珷鑺備富瑕佸涓婅堪妗堜緥涓鑷寸嚎绋婤lock鐨勫師鍥犺繘琛屾眹鎬诲垎鏋愶紝骞剁粰鍑虹浉搴旂殑瑙e喅鏂规銆?/span>

鍥?6 鏃ュ織寮傛澶勭悊娴佺▼
鏃ュ織寮傛澶勭悊娴佺▼绀烘剰濡傚浘26鎵€绀猴紝鏁翠綋姝ラ濡備笅锛?/span>
瀵瑰簲鍦帮紝Log4j2瀵艰嚧绾跨▼Block鐨勪富瑕佹綔鍦ㄩ闄╃偣濡備笅锛?/span>
浠庝笂杩板垎鏋愪笉闅剧湅鍑猴細
鏍囧彿鈶犲拰鈶″鍙戠敓绾跨▼Block鐨勫奖鍝嶈寖鍥磋繙姣旀爣鍙封憿鏇村ぇ锛屽洜姝ゆ牳蹇冩槸瑕侀伩鍏嶆棩蹇椾簨浠跺湪鍏ラ槦鎿嶄綔瀹屾垚鍓嶈Е鍙戠嚎绋婤lock銆傚叾瀹炴棩蹇楀紓姝ョ嚎绋嬮€氬父鏄崟绾跨▼锛屽洜姝ゅ浜庡崟涓狝ppender鏉ヨ锛屼笉浼氬嚭鐜癇lock鐜拌薄锛岃嚦澶氫細瀵艰嚧寮傛绾跨▼澶勭悊閫熷害鍙樻參鑰屽凡锛屼絾濡傛灉瀛樺湪澶氫釜寮傛Appender锛岄偅涔堝涓棩蹇楀紓姝ョ嚎绋嬩粛鐒朵細鍑虹幇褰兼Block鐨勭幇璞°€?/span>
鎼炴竻妤氫簡鏃ュ織瀵艰嚧绾跨▼Block鐨勫師鍥犲悗锛岄棶棰樹篃灏变笉闅捐В鍐筹紝瑙e喅鏂规涓昏浠庢棩蹇椾簨浠垛€滃叆闃熷墠鈥濄€佲€滃叆闃熸椂鈥濆拰鈥滃嚭闃熷悗鈥濅笁鏂归潰灞曞紑銆?/span>
缁撳悎涓婃枃鍒嗘瀽鐨勨€淎syncAppender瀵艰嚧绾跨▼Block鈥濄€佲€淟ambda琛ㄨ揪寮忓鑷寸嚎绋婤lock鈥濆拰鈥淎syncLoggerConfig瀵艰嚧绾跨▼Block鈥濇渚嬶紝鏃ュ織浜嬩欢鍏ラ槦鍓嶉伩鍏嶇嚎绋婤lock鐨勮В鍐虫柟妗堝彲浠庡涓嬪嚑鏂归潰鑰冭檻锛?/span>
鍏堣鏂规缁撹锛?/span>
涓嬮潰鍏蜂綋鍒嗘瀽鏂规鍙鎬э細1. 鏃ュ織浜嬩欢鍏ラ槦鍓嶉伩鍏嶈Е鍙戝紓甯稿爢鏍堢被瑙f瀽銆佸姞杞芥搷浣?/strong>濡傛灉鍦ㄦ棩蹇椾簨浠跺叆闃熷墠锛岃兘閬垮厤寮傚父鍫嗘爤绫昏В鏋愩€佸姞杞芥搷浣滐紝鍒欏彲浠庢牴鏈笂瑙e喅璇ラ棶棰橈紝浣嗗湪Log4j2鐨?.17.1鐗堟湰涓瑼syncAppender鍜孉syncLoggerConfig浠嶅瓨鍦ㄨ闂锛屾鏃讹細
// 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();
// ... 浠ヤ笅閮ㄥ垎蹇界暐 ...
}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>
缁撳悎涓婃枃鍒嗘瀽鐨勨€滄棩蹇楅槦鍒楁弧瀵艰嚧绾跨▼Block鈥濇渚嬶紝鏃ュ織浜嬩欢鍏ラ槦鏃堕伩鍏嶇嚎绋婤lock鐨勮В鍐虫柟妗堝彲浠庡涓嬪嚑鏂归潰鑰冭檻锛?/span>
鍏堣鏂规缁撹锛?strong>鑷畾涔堿ppender瀹炵幇锛屾棩蹇椾簨浠跺叆闃熷け璐ユ椂蹇界暐閿欒鏃ュ織锛岀編鍥㈠唴閮⊿cribe-Log鎻愪緵鐨凙syncScribeAppender鍗虫槸濡傛銆備笅闈㈠叿浣撳垎鏋愭柟妗堝彲琛屾€э細1. 鏃ュ織闃熷垪婊℃椂Appender蹇界暐璇ユ棩蹇?/strong>鏃ュ織闃熷垪婊★紝鏌愮绋嬪害涓婅鏄庢棩蹇楃嚎绋嬬殑澶勭悊鑳藉姏涓嶈冻锛屽湪鐜版湁鏈哄櫒璧勬簮涓嶅彉鐨勬儏鍐典笅闇€瑕佸仛涓€瀹氬彇鑸嶏紝濡傛灉鏃ュ織涓嶆槸鐗瑰埆閲嶈閫氬父鍙涪寮冭鏃ュ織锛屾鏃讹細
// 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鏃ュ織杈撳嚭
鏃ュ織浜嬩欢鍑洪槦鍚庝細鎸夌収鐢ㄦ埛閰嶇疆鐨勮緭鍑烘牱寮忥紝瀵规棩蹇楀唴瀹硅繘琛屾牸寮忓寲杞崲锛屾鏃朵粛鐒跺彲鑳借Е鍙戣В鏋愩€佸姞杞藉紓甯稿爢鏍堢被銆傚洜姝わ紝鏃ュ織鍑洪槦鍚庨伩鍏嶇嚎绋婤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);
}
}
}鏈珷鑺備富瑕佺粨鍚堥」鐩湪鏃ュ織浣跨敤鏂归潰鐨勪竴绯诲垪韪╁潙缁忓巻鍜屽疄璺电粡楠岋紝鎬荤粨浜嗕竴浠藉叧浜庢棩蹇楅厤缃殑鏈€浣冲疄璺碉紝渚涘ぇ瀹跺弬鑰冦€?/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>蹇楁磱銆侀檲瓒呫€佹潕鏁忋€佸嚡鏅栥€佹鐞︾瓑锛屽潎鏉ヨ嚜缇庡洟鍩虹鎶€鏈儴-搴旂敤涓棿浠跺洟闃熴€?/span>
我有一些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
我在理解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
我没有理解以下行为(另请参阅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
我需要尝试一些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
我在使用自定义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
我从用户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可以方便地让您根据它的“并行赋
我明白了defa(&block)block.call(self)end和defa()yieldselfend导致相同的结果,如果我假设有这样一个blocka{}。我的问题是-因为我偶然发现了一些这样的代码,它是否有任何区别或者是否有任何优势(如果我不使用变量/引用block):defa(&block)yieldselfend这是一个我不理解&block用法的具体案例:defrule(code,name,&block)@rules=[]if@rules.nil?@rules 最佳答案 我能想到的唯一优点就是自省(introspecti
考虑一下:现在这些情况:#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://
有没有办法在sinatra的beforedoblock中停止执行并返回不同的值?beforedo#codeishere#Iwouldliketo'return"Message"'#Iwouldlike"/home"tonotgetcalled.end//restofthecodeget'/home'doend 最佳答案 beforedohalt401,{'Content-Type'=>'text/plain'},'Message!'end如果你愿意,你可以只指定状态,这里有状态、标题和正文的例子
下面的代码工作正常: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