本文针对Logback 集成 Skywalking Trace ID 后,日志中的Trace ID在agent中如何添加做讲解
skywalking agent 通过javaagent的方式集成到自身系统中,在对类加载之前进行拦截,然后通过字节码增强(bytebuddy)技术进行环绕增强,从而做到对类的字节码修改。
日志类字节码增加
如下:TraceIdPatternLogbackLayout该类便是日志Tid添加的入口
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
具体源码如下:
public class TraceIdPatternLogbackLayout extends PatternLayout {
public TraceIdPatternLogbackLayout() {
}
static {
defaultConverterMap.put("tid", LogbackPatternConverter.class.getName());
defaultConverterMap.put("sw_ctx", LogbackSkyWalkingContextPatternConverter.class.getName());
}
}
分析
可以看出该类会像defaultConverterMap中添加tid作为key ,LogbackPatternConverter类名作为value,那我们继续往下深入看。先点击defaultConverterMap,我截取PatternLayout 部分代码可以看出,这是所有pattern表达式的入口
public class PatternLayout extends PatternLayoutBase<ILoggingEvent> {
public static final Map<String, String> defaultConverterMap = new HashMap();
public static final String HEADER_PREFIX = "#logback.classic pattern: ";
public PatternLayout() {
this.postCompileProcessor = new EnsureExceptionHandling();
}
public Map<String, String> getDefaultConverterMap() {
return defaultConverterMap;
}
public String doLayout(ILoggingEvent event) {
return !this.isStarted() ? "" : this.writeLoopOnConverters(event);
}
protected String getPresentationHeaderPrefix() {
return "#logback.classic pattern: ";
}
static {
defaultConverterMap.putAll(Parser.DEFAULT_COMPOSITE_CONVERTER_MAP);
defaultConverterMap.put("d", DateConverter.class.getName());
defaultConverterMap.put("date", DateConverter.class.getName());
defaultConverterMap.put("r", RelativeTimeConverter.class.getName());
defaultConverterMap.put("relative", RelativeTimeConverter.class.getName());
defaultConverterMap.put("level", LevelConverter.class.getName());
defaultConverterMap.put("le", LevelConverter.class.getName());
defaultConverterMap.put("p", LevelConverter.class.getName());
defaultConverterMap.put("t", ThreadConverter.class.getName());
defaultConverterMap.put("thread", ThreadConverter.class.getName());
defaultConverterMap.put("lo", LoggerConverter.class.getName());
之后我们在点击LogbackPatternConverter该类,代码如下:
public class LogbackPatternConverter extends ClassicConverter {
public LogbackPatternConverter() {
}
public String convert(ILoggingEvent iLoggingEvent) {
return "TID: N/A";
}
}
分析
该类中的convert方法便是我们之后将要在agent中要增强的类。他会返回实际的TID值。
首先在agent端找到apm-toolkit-activation包然后找到apm-toolkit-logback-1.x-activation包。
之后进入包下找到LogbackPatternConverterActivation类,类源码如下:
public class LogbackPatternConverterActivation extends ClassInstanceMethodsEnhancePluginDefine {
//实际拦截的类
public static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.toolkit.activation.log.logback.v1.x.PrintTraceIdInterceptor";
//需要增强的类名
public static final String ENHANCE_CLASS = "org.apache.skywalking.apm.toolkit.log.logback.v1.x.LogbackPatternConverter";
//需要增强的类名
public static final String ENHANCE_METHOD = "convert";
/**
* @return the target class, which needs active.
*/
@Override
protected ClassMatch enhanceClass() {
return byName(ENHANCE_CLASS);
}
/**
* @return null, no need to intercept constructor of enhance class.
*/
@Override
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
return null;
}
/**
* @return the collection of {@link StaticMethodsInterceptPoint}, represent the intercepted methods and their
* interceptors.
*/
@Override
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
return new InstanceMethodsInterceptPoint[] {
new InstanceMethodsInterceptPoint() {
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
return named(ENHANCE_METHOD).and(takesArgumentWithType(0, "ch.qos.logback.classic.spi.ILoggingEvent"));
}
@Override
public String getMethodsInterceptor() {
return INTERCEPT_CLASS;
}
@Override
public boolean isOverrideArgs() {
return false;
}
}
};
}
}
从代码中可以看出要增强的类的方法和skywalking实际拦截的类PrintTraceIdInterceptor
public class PrintTraceIdInterceptor implements InstanceMethodsAroundInterceptor {
//方法之前
@Override
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
MethodInterceptResult result) throws Throwable {
}
//方法之后将在skywalking中实际产生的TraceId进行返回
@Override
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
Object ret) throws Throwable {
if (!ContextManager.isActive()) {
if (allArguments[0] instanceof EnhancedInstance) {
SkyWalkingContext skyWalkingContext = (SkyWalkingContext) ((EnhancedInstance) allArguments[0]).getSkyWalkingDynamicField();
if (skyWalkingContext != null) {
return "TID:" + skyWalkingContext.getTraceId();
}
}
}
return "TID:" + ContextManager.getGlobalTraceId();
}
//异常处理
@Override
public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
Class<?>[] argumentsTypes, Throwable t) {
}
}
skywalking agent 源码部分相对比较简单,主要就是通过字节码增强技术在写入TID换成skywalking的链路跟踪里的TraceID。
字节码增强技术byteBuddy