在<<微服務基于請求的日志跟蹤>>上設計了基于請求的微服務日志處理方法, 但是發(fā)現(xiàn)在log4j處于異步的情況下會失效, 原因是RequestId無法從原線程傳輸?shù)酱蛴∪罩镜木€程, 異步情況下(AsyncLoggerConfig), 日志先被enqueue到一個隊列,然后若干線程去消費這個隊列, 因為跨了線程,所以不能通過線程變量傳遞過去.
查看了相關(guān)代碼, 發(fā)現(xiàn)log4j首先將message生成了LogEvent, 然后將LogEvent丟入隊列, 而LogEvent提供了一個ContenxtData的Map來攜帶屬性變量, 所以我們可以將RequestId放到這里面?zhèn)鬟f過去.
具體的代碼是:
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");
}
}
同時修改LogRequestIdPlugin:
@Override
public void format(LogEvent event, StringBuilder toAppendTo) {
toAppendTo.append(RequestIdLogEventFactory.getRequestId(event));
}
設置應用的啟動參數(shù), 指定logEvent工廠, 增加:
-DLog4jLogEventFactory=your_package.RequestIdLogEventFactory
這樣即使在異步輸出的時候,也能傳遞RequestId到日志文件了.