在<<微服務(wù)基于請(qǐng)求的日志跟蹤>>上設(shè)計(jì)了基于請(qǐng)求的微服務(wù)日志處理方法, 但是發(fā)現(xiàn)在log4j處于異步的情況下會(huì)失效, 原因是RequestId無(wú)法從原線程傳輸?shù)酱蛴∪罩镜木€程, 異步情況下(AsyncLoggerConfig), 日志先被enqueue到一個(gè)隊(duì)列,然后若干線程去消費(fèi)這個(gè)隊(duì)列, 因?yàn)榭缌司€程,所以不能通過(guò)線程變量傳遞過(guò)去.
查看了相關(guān)代碼, 發(fā)現(xiàn)log4j首先將message生成了LogEvent, 然后將LogEvent丟入隊(duì)列, 而LogEvent提供了一個(gè)ContenxtData的Map來(lái)攜帶屬性變量, 所以我們可以將RequestId放到這里面?zhèn)鬟f過(guò)去.
具體的代碼是:
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.impl.ContextDataFactory;
import org.apache.logging.log4j.core.impl.MutableLogEvent;
import org.apache.logging.log4j.core.impl.ReusableLogEventFactory;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.util.StringMap;
import java.util.List;
public class RequestIdLogEventFactory extends ReusableLogEventFactory {
@Override
public LogEvent createEvent(String loggerName, Marker marker, String fqcn, Level level, Message message, List<Property> properties, Throwable t) {
LogEvent event = super.createEvent(loggerName, marker, fqcn, level, message, properties, t);
if (event instanceof MutableLogEvent) {
StringMap contextData = ContextDataFactory.createContextData();
contextData.putAll(event.getContextData());
contextData.putValue("RequestId", LogRequestIdPlugin.getRequestId());
((MutableLogEvent) event).setContextData(contextData);
}
return event;
}
public static String getRequestId(LogEvent event) {
return event.getContextData().getValue("RequestId");
}
}
同時(shí)修改LogRequestIdPlugin:
@Override
public void format(LogEvent event, StringBuilder toAppendTo) {
toAppendTo.append(RequestIdLogEventFactory.getRequestId(event));
}
設(shè)置應(yīng)用的啟動(dòng)參數(shù), 指定logEvent工廠, 增加:
-DLog4jLogEventFactory=your_package.RequestIdLogEventFactory
這樣即使在異步輸出的時(shí)候,也能傳遞RequestId到日志文件了.