package org.stagemonitor.tracing.wrapper; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer; import io.opentracing.propagation.Format; /** * The purpose of this class is to make it possible to register {@link SpanEventListener}s which are created by * {@link SpanEventListenerFactory}s. The {@link SpanEventListener}s are called when certain methods of a * {@link SpanBuilder} or {@link Span} are called. */ public class SpanWrappingTracer implements Tracer { private final Tracer delegate; private final Collection<SpanEventListenerFactory> spanInterceptorFactories; public SpanWrappingTracer(Tracer delegate) { this(delegate, new CopyOnWriteArrayList<SpanEventListenerFactory>()); } public SpanWrappingTracer(Tracer delegate, Collection<SpanEventListenerFactory> spanInterceptorFactories) { this.delegate = delegate; this.spanInterceptorFactories = spanInterceptorFactories; } @Override public SpanBuilder buildSpan(String operationName) { return new SpanWrappingSpanBuilder(delegate.buildSpan(operationName), operationName, createSpanInterceptors()); } @Override public <C> void inject(SpanContext spanContext, Format<C> format, C carrier) { delegate.inject(spanContext, format, carrier); } @Override public <C> SpanContext extract(Format<C> format, C carrier) { return delegate.extract(format, carrier); } protected List<SpanEventListener> createSpanInterceptors() { List<SpanEventListener> spanEventListeners = new ArrayList<SpanEventListener>(spanInterceptorFactories.size()); for (SpanEventListenerFactory spanEventListenerFactory : spanInterceptorFactories) { spanEventListeners.add(spanEventListenerFactory.create()); } return spanEventListeners; } public void addSpanInterceptor(SpanEventListenerFactory spanEventListenerFactory) { spanInterceptorFactories.add(spanEventListenerFactory); } class SpanWrappingSpanBuilder implements SpanBuilder { private final String operationName; private final List<SpanEventListener> spanEventListeners; private SpanBuilder delegate; private long startTimestampNanos; private long startTimestampMillis; SpanWrappingSpanBuilder(SpanBuilder delegate, String operationName, List<SpanEventListener> spanEventListeners) { this.operationName = operationName; this.delegate = delegate; this.spanEventListeners = spanEventListeners; } public Iterable<Map.Entry<String, String>> baggageItems() { return delegate.baggageItems(); } public SpanBuilder asChildOf(SpanContext parent) { delegate = delegate.asChildOf(parent); return this; } public SpanBuilder asChildOf(Span parent) { delegate = delegate.asChildOf(parent); return this; } public SpanBuilder addReference(String referenceType, SpanContext referencedContext) { delegate = delegate.addReference(referenceType, referencedContext); return this; } public SpanBuilder withTag(String key, String value) { for (SpanEventListener spanEventListener : spanEventListeners) { value = spanEventListener.onSetTag(key, value); } if (value != null) { delegate = delegate.withTag(key, value); } return this; } public SpanBuilder withTag(String key, boolean value) { for (SpanEventListener spanEventListener : spanEventListeners) { value = spanEventListener.onSetTag(key, value); } delegate = delegate.withTag(key, value); return this; } public SpanBuilder withTag(String key, Number value) { for (SpanEventListener spanEventListener : spanEventListeners) { value = spanEventListener.onSetTag(key, value); } if (value != null) { delegate = delegate.withTag(key, value); } return this; } public SpanBuilder withStartTimestamp(long microseconds) { startTimestampNanos = TimeUnit.MICROSECONDS.toNanos(microseconds); startTimestampMillis = TimeUnit.MICROSECONDS.toMillis(microseconds); delegate = delegate.withStartTimestamp(microseconds); return this; } public Span start() { if (startTimestampNanos == 0) { startTimestampNanos = System.nanoTime(); startTimestampMillis = System.currentTimeMillis(); } final SpanWrapper spanWrapper = new SpanWrapper(delegate.start(), operationName, startTimestampNanos, startTimestampMillis, spanEventListeners); for (SpanEventListener spanEventListener : spanEventListeners) { spanEventListener.onStart(spanWrapper); } return spanWrapper; } } }