您的位置:首页 > 编程语言 > Java开发

spring cloud sleuth在spring中创建span

2018-04-02 22:46 405 查看
在spring-cloud-sleuth的META-INF里的spring.factories里设置了一下:org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.sleuth.autoconfig.TraceEnvironmentPostProcessor
这样,TraceEnvironmentPostProcessor被配置在了ioc容器初始化之前。public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
Map<String, Object> map = new HashMap<String, Object>();
// This doesn't work with all logging systems but it's a useful default so you see
// traces in logs without having to configure it.
if (Boolean.parseBoolean(environment.getProperty("spring.sleuth.enabled", "true"))) {
map.put("logging.pattern.level",
"%5p [${spring.zipkin.service.name:${spring.application.name:-}},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]");
}
// TODO: Remove this in 2.0.x. For compatibility we always set to true
if (!environment.containsProperty(SPRING_AOP_PROXY_TARGET_CLASS)) {
map.put(SPRING_AOP_PROXY_TARGET_CLASS, "true");
}
addOrReplace(environment.getPropertySources(), map);
}在TraceEnvironmentPostProcessor的postProcessEnvironment()方法里保证了两件事情,设置了spring.aop.proxyTargetClass参数为true保证了cglib代理的开启,并加入了日志的追踪打印的模板。
 
而后在配置类TraceAutoConfiguration中生成了Tracer,默认实现为DefaultTraces,作用为正式创建一个工作单元span。
紧随其后的配置类为SleuthAnnotationAutoConfiguration,此处根据之前生成的Tracer进一步创建了其包装类SpanCreator,并最重要的是生成了代理的配置类SleuthAdvisorConfig。@PostConstruct
public void init() {
this.pointcut = buildPointcut();
this.advice = buildAdvice();
if (this.advice instanceof BeanFactoryAware) {
((BeanFactoryAware) this.advice).setBeanFactory(this.beanFactory);
}
}
其初始化方法中,完成了切面与切面增强的创建。
其buildPointcut()方法:private Pointcut buildPointcut() {
return new AnnotationClassOrMethodOrArgsPointcut();
}
private final class AnnotationClassOrMethodOrArgsPointcut extends
DynamicMethodMatcherPointcut {

@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
return getClassFilter().matches(targetClass);
}

@Override public ClassFilter getClassFilter() {
return new ClassFilter() {
@Override public boolean matches(Class<?> clazz) {
return new AnnotationClassOrMethodFilter(NewSpan.class).matches(clazz) ||
new AnnotationClassOrMethodFilter(ContinueSpan.class).matches(clazz);
}
};
}

}

private final class AnnotationC
4000
lassOrMethodFilter extends AnnotationClassFilter {

private final AnnotationMethodsResolver methodResolver;

AnnotationClassOrMethodFilter(Class<? extends Annotation> annotationType) {
super(annotationType, true);
this.methodResolver = new AnnotationMethodsResolver(annotationType);
}

@Override
public boolean matches(Class<?> clazz) {
return super.matches(clazz) || this.methodResolver.hasAnnotatedMethods(clazz);
}

}
显而易见,切面的匹配通过目标类是否满足使用了NewSpan或者ContinueSpan注解。
 
切面的增强则通过buildAdvice来构造Interceptor来通过代理使用invoke()方法来完成生成span的目的。private Advice buildAdvice() {
return new SleuthInterceptor();
}

@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
if (method == null) {
return invocation.proceed();
}
Method mostSpecificMethod = AopUtils
.getMostSpecificMethod(method, invocation.getThis().getClass());
NewSpan newSpan = SleuthAnnotationUtils.findAnnotation(mostSpecificMethod, NewSpan.class);
ContinueSpan continueSpan = SleuthAnnotationUtils.findAnnotation(mostSpecificMethod, ContinueSpan.class);
if (newSpan == null && continueSpan == null) {
return invocation.proceed();
}
Span span = tracer().getCurrentSpan();
String log = log(continueSpan);
boolean hasLog = StringUtils.hasText(log);
try {
if (newSpan != null) {
span = spanCreator().createSpan(invocation, newSpan);
}
if (hasLog) {
logEvent(span, log + ".before");
}
spanTagAnnotationHandler().addAnnotatedParameters(invocation);
addTags(invocation, span);
return invocation.proceed();
} catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.debug("Exception occurred while trying to continue the pointcut", e);
}
if (hasLog) {
logEvent(span, log + ".afterFailure");
}
errorParser().parseErrorTags(tracer().getCurrentSpan(), e);
throw e;
} finally {
if (span != null) {
if (hasLog) {
logEvent(span, log + ".after");
}
if (newSpan != null) {
tracer().close(span);
}
}
}
}

当通过代理走到此处的invoke()方法说明此时涉及到了与别的服务的调用,需要生成新的spanId,那么就在这里newSpan注解生成新的spanId,如果该方法实现了ContinueSpan注解,那么就在现有的spanId。
 
如果采用newSpan注解,那么这里需要通过之前的在配置类中生成的tracer的getCurrentSpan()方法获取当前的span。
具体的实现在SpanContextHolder的getCurrentSpan()方法中。static Span getCurrentSpan() {
return isTracing() ? CURRENT_SPAN.get().span : null;
}
通过ThreadLoacl来获得当前的span,也就是说,当新的trace请求到来时,可以通过ThreadLoacl来存储。
 
紧接着通过spanCreator的createSpan()方法来证实获得新的span。@Override public Span createSpan(MethodInvocation pjp, NewSpan newSpanAnnotation) {
String name = StringUtils.isEmpty(newSpanAnnotation.name()) ?
pjp.getMethod().getName() : newSpanAnnotation.name();
String changedName = SpanNameUtil.toLowerHyphen(name);
if (log.isDebugEnabled()) {
log.debug("For the class [" + pjp.getThis().getClass() + "] method "
+ "[" + pjp.getMethod().getName() + "] will name the span [" + changedName + "]");
}
return createSpan(changedName);
}

private Span createSpan(String name) {
if (this.tracer.isTracing()) {
return this.tracer.createSpan(name, this.tracer.getCurrentSpan());
}
return this.tracer.createSpan(name);
}
Span的名字在注解中的名字和方法名中有限选择前者,而后根据通过tracer的createSpan()来获得span。@Override
public Span createSpan(String name, Span parent) {
if (parent == null) {
return createSpan(name);
}
return continueSpan(createChild(parent, name));
}
如果此时没有任何span存在,那么直接通过createSpan().@Override
public Span createSpan(String name) {
return this.createSpan(name, this.defaultSampler);
}

@Override
public Span createSpan(String name, Sampler sampler) {
String shortenedName = SpanNameUtil.shorten(name);
Span span;
if (isTracing()) {
span = createChild(getCurrentSpan(), shortenedName);
}
else {
long id = createId();
span = Span.builder().name(shortenedName)
.traceIdHigh(this.traceId128 ? createTraceIdHigh() : 0L)
.traceId(id)
.spanId(id).build();
if (sampler == null) {
sampler = this.defaultSampler;
}
span = sampledSpan(span, sampler);
this.spanLogger.logStartedSpan(null, span);
}
return continueSpan(span);
}
这里可以看到spanId的构造,如果当时是首次构建spanId,那么首先会创建一个traceId,作为本次跟踪流 的id。并与第一次的spanID相同。
但是,此时若是已经存在span,也就是说这并不是第一次,那么就没有必要将traceId设为该次创建的spanId,而是在createChild()方法中,记录当前的traceId为原来收到的traceId,并将收到的spanId作为parentId,并将savedSpan指向原来的span,重新生成一个spanId,并将新的span作为当前的span。
 
在完成了span的创建后,则会经过sample的判断,此次是否要使用span记录,可以根据配置修改sample的类型,如果采用了百分比类型的,那么可能不会记录下来,完全复制一份span,但是把其exportable属性改为false。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: