Add instrumentation for OpenTelemetry API itself
MODIFY
instrumentation/instrumentation.gradle
instrumentation/instrumentation.gradle
@@ -144,5 +144,8 @@ shadowJar {
relocate "io.opentelemetry.internal", "io.opentelemetry.auto.shaded.io.opentelemetry.internal"
relocate "io.opentelemetry.metrics", "io.opentelemetry.auto.shaded.io.opentelemetry.metrics"
relocate "io.opentelemetry.trace", "io.opentelemetry.auto.shaded.io.opentelemetry.trace"
+
+ // this is for instrumentation on opentelemetry-api itself
+ relocate "unshaded.io.opentelemetry", "io.opentelemetry"
}

ADD
instrumentation/opentelemetry-api-0.2/opentelemetry-api-0.2.gradle
+apply from: "${rootDir}/gradle/java.gradle"
+
+dependencies {
+ // this instrumentation needs to be able to be able to reference both the OpenTelemetry API
+ // that is shaded in the bootstrap class loader (for sending telemetry to the agent),
+ // and the OpenTelemetry API that the user brings (in order to capture that telemetry)
+ //
+ // since (all) instrumentation already uses OpenTelemetry API for sending telemetry to the agent,
+ // this instrumentation uses a "temporarily shaded" OpenTelemetry API to represent the
+ // OpenTelemetry API that the user brings
+ //
+ // then later, after the OpenTelemetry API in the bootstrap class loader is shaded,
+ // the "temporarily shaded" OpenTelemetry API is unshaded, so that it will apply to the
+ // OpenTelemetry API that the user brings
+ //
+ // so in the code "unshaded.io.opentelemetry.*" refers to the (unshaded) OpenTelemetry API that
+ // the user brings (as those references will be translated during the build to remove the
+ // "unshaded." prefix)
+ //
+ // and in the code "io.opentelemetry.*" refers to the (shaded) OpenTelemetry API that is in the
+ // bootstrap class loader (as those references will later be shaded)
+ compileOnly project(path: ':opentelemetry-api-shaded-for-instrumenting', configuration: 'shadow')
+
+ testCompile project(path: ':opentelemetry-api-shaded-for-instrumenting', configuration: 'shadow')
+}

ADD
instrumentation/opentelemetry-api-0.2/src/main/java/io/opentelemetry/auto/instrumentation/otapi/Bridging.java
+package io.opentelemetry.auto.instrumentation.otapi;
+
+import java.util.HashMap;
+import java.util.Map;
+import lombok.extern.slf4j.Slf4j;
+import unshaded.io.opentelemetry.trace.AttributeValue;
+import unshaded.io.opentelemetry.trace.EndSpanOptions;
+import unshaded.io.opentelemetry.trace.Span;
+import unshaded.io.opentelemetry.trace.SpanContext;
+import unshaded.io.opentelemetry.trace.SpanId;
+import unshaded.io.opentelemetry.trace.Status;
+import unshaded.io.opentelemetry.trace.TraceFlags;
+import unshaded.io.opentelemetry.trace.TraceId;
+import unshaded.io.opentelemetry.trace.Tracestate;
+
+/**
+ * This class translates between the (unshaded) OpenTelemetry API that the user brings and the
+ * (shaded) OpenTelemetry API that is in the bootstrap class loader and used to report telemetry to
+ * the agent.
+ *
+ * <p>"unshaded.io.opentelemetry.*" refers to the (unshaded) OpenTelemetry API that the user brings
+ * (as those references will be translated during the build to remove the "unshaded." prefix).
+ *
+ * <p>"io.opentelemetry.*" refers to the (shaded) OpenTelemetry API that is in the bootstrap class
+ * loader (as those references will later be shaded).
+ *
+ * <p>Also see comments in this module's gradle file.
+ */
+@Slf4j
+public class Bridging {
+
+ // this is just an optimization to save some byte array allocations
+ public static final ThreadLocal<byte[]> BUFFER = new ThreadLocal<>();
+
+ public static SpanContext toUnshaded(final io.opentelemetry.trace.SpanContext shadedContext) {
+ if (shadedContext.isRemote()) {
+ return SpanContext.createFromRemoteParent(
+ toUnshaded(shadedContext.getTraceId()),
+ toUnshaded(shadedContext.getSpanId()),
+ toUnshaded(shadedContext.getTraceFlags()),
+ toUnshaded(shadedContext.getTracestate()));
+ } else {
+ return SpanContext.create(
+ toUnshaded(shadedContext.getTraceId()),
+ toUnshaded(shadedContext.getSpanId()),
+ toUnshaded(shadedContext.getTraceFlags()),
+ toUnshaded(shadedContext.getTracestate()));
+ }
+ }
+
+ public static io.opentelemetry.trace.SpanContext toShaded(final SpanContext unshadedContext) {
+ if (unshadedContext.isRemote()) {
+ return io.opentelemetry.trace.SpanContext.createFromRemoteParent(
+ toShaded(unshadedContext.getTraceId()),
+ toShaded(unshadedContext.getSpanId()),
+ toShaded(unshadedContext.getTraceFlags()),
+ toShaded(unshadedContext.getTracestate()));
+ } else {
+ return io.opentelemetry.trace.SpanContext.create(
+ toShaded(unshadedContext.getTraceId()),
+ toShaded(unshadedContext.getSpanId()),
+ toShaded(unshadedContext.getTraceFlags()),
+ toShaded(unshadedContext.getTracestate()));
+ }
+ }
+
+ public static Map<String, io.opentelemetry.trace.AttributeValue> toShaded(
+ final Map<String, AttributeValue> unshadedAttributes) {
+ final Map<String, io.opentelemetry.trace.AttributeValue> shadedAttributes = new HashMap<>();
+ for (final Map.Entry<String, AttributeValue> entry : unshadedAttributes.entrySet()) {
+ final AttributeValue value = entry.getValue();
+ final io.opentelemetry.trace.AttributeValue shadedValue = toShadedOrNull(value);
+ if (shadedValue != null) {
+ shadedAttributes.put(entry.getKey(), shadedValue);
+ }
+ }
+ return shadedAttributes;
+ }
+
+ public static io.opentelemetry.trace.AttributeValue toShadedOrNull(
+ final AttributeValue unshadedValue) {
+ switch (unshadedValue.getType()) {
+ case STRING:
+ return io.opentelemetry.trace.AttributeValue.stringAttributeValue(
+ unshadedValue.getStringValue());
+ case LONG:
+ return io.opentelemetry.trace.AttributeValue.longAttributeValue(
+ unshadedValue.getLongValue());
+ case BOOLEAN:
+ return io.opentelemetry.trace.AttributeValue.booleanAttributeValue(
+ unshadedValue.getBooleanValue());
+ case DOUBLE:
+ return io.opentelemetry.trace.AttributeValue.doubleAttributeValue(
+ unshadedValue.getDoubleValue());
+ default:
+ log.debug("unexpected attribute type: {}", unshadedValue.getType());
+ return null;
+ }
+ }
+
+ public static io.opentelemetry.trace.Status toShadedOrNull(final Status unshadedStatus) {
+ final io.opentelemetry.trace.Status.CanonicalCode canonicalCode;
+ try {
+ canonicalCode =
+ io.opentelemetry.trace.Status.CanonicalCode.valueOf(
+ unshadedStatus.getCanonicalCode().name());
+ } catch (final IllegalArgumentException e) {
+ log.debug("unexpected status canonical code: {}", unshadedStatus.getCanonicalCode().name());
+ return null;
+ }
+ return canonicalCode.toStatus().withDescription(unshadedStatus.getDescription());
+ }
+
+ public static io.opentelemetry.trace.Span.Kind toShadedOrNull(final Span.Kind unshadedSpanKind) {
+ try {
+ return io.opentelemetry.trace.Span.Kind.valueOf(unshadedSpanKind.name());
+ } catch (final IllegalArgumentException e) {
+ log.debug("unexpected span kind: {}", unshadedSpanKind.name());
+ return null;
+ }
+ }
+
+ public static io.opentelemetry.trace.EndSpanOptions toShaded(
+ final EndSpanOptions unshadedEndSpanOptions) {
+ return io.opentelemetry.trace.EndSpanOptions.builder()
+ .setEndTimestamp(unshadedEndSpanOptions.getEndTimestamp())
+ .build();
+ }
+
+ private static TraceId toUnshaded(final io.opentelemetry.trace.TraceId shadedTraceId) {
+ final byte[] bytes = getBuffer();
+ shadedTraceId.copyBytesTo(bytes, 0);
+ return TraceId.fromBytes(bytes, 0);
+ }
+
+ private static SpanId toUnshaded(final io.opentelemetry.trace.SpanId shadedSpanId) {
+ final byte[] bytes = getBuffer();
+ shadedSpanId.copyBytesTo(bytes, 0);
+ return SpanId.fromBytes(bytes, 0);
+ }
+
+ private static TraceFlags toUnshaded(final io.opentelemetry.trace.TraceFlags shadedTraceFlags) {
+ return TraceFlags.fromByte(shadedTraceFlags.getByte());
+ }
+
+ private static Tracestate toUnshaded(final io.opentelemetry.trace.Tracestate shadedTraceState) {
+ final Tracestate.Builder builder = Tracestate.builder();
+ for (final io.opentelemetry.trace.Tracestate.Entry entry : shadedTraceState.getEntries()) {
+ builder.set(entry.getKey(), entry.getValue());
+ }
+ return builder.build();
+ }
+
+ private static io.opentelemetry.trace.TraceId toShaded(final TraceId unshadedTraceId) {
+ final byte[] bytes = getBuffer();
+ unshadedTraceId.copyBytesTo(bytes, 0);
+ return io.opentelemetry.trace.TraceId.fromBytes(bytes, 0);
+ }
+
+ private static io.opentelemetry.trace.SpanId toShaded(final SpanId unshadedSpanId) {
+ final byte[] bytes = getBuffer();
+ unshadedSpanId.copyBytesTo(bytes, 0);
+ return io.opentelemetry.trace.SpanId.fromBytes(bytes, 0);
+ }
+
+ private static io.opentelemetry.trace.TraceFlags toShaded(final TraceFlags unshadedTraceFlags) {
+ return io.opentelemetry.trace.TraceFlags.fromByte(unshadedTraceFlags.getByte());
+ }
+
+ private static io.opentelemetry.trace.Tracestate toShaded(final Tracestate unshadedTraceState) {
+ final io.opentelemetry.trace.Tracestate.Builder builder =
+ io.opentelemetry.trace.Tracestate.builder();
+ for (final Tracestate.Entry entry : unshadedTraceState.getEntries()) {
+ builder.set(entry.getKey(), entry.getValue());
+ }
+ return builder.build();
+ }
+
+ private static byte[] getBuffer() {
+ byte[] bytes = BUFFER.get();
+ if (bytes == null) {
+ bytes = new byte[16];
+ BUFFER.set(bytes);
+ }
+ return bytes;
+ }
+}

ADD
instrumentation/opentelemetry-api-0.2/src/main/java/io/opentelemetry/auto/instrumentation/otapi/OpenTelemetryApiInstrumentation.java
+package io.opentelemetry.auto.instrumentation.otapi;
+
+import static io.opentelemetry.auto.tooling.ByteBuddyElementMatchers.safeHasSuperType;
+import static net.bytebuddy.matcher.ElementMatchers.isMethod;
+import static net.bytebuddy.matcher.ElementMatchers.isPublic;
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
+
+import com.google.auto.service.AutoService;
+import io.opentelemetry.auto.tooling.Instrumenter;
+import java.util.HashMap;
+import java.util.Map;
+import net.bytebuddy.asm.Advice;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+@AutoService(Instrumenter.class)
+public class OpenTelemetryApiInstrumentation extends Instrumenter.Default {
+ public OpenTelemetryApiInstrumentation() {
+ super("opentelemetry-api");
+ }
+
+ @Override
+ public ElementMatcher<? super TypeDescription> typeMatcher() {
+ return safeHasSuperType(named("unshaded.io.opentelemetry.OpenTelemetry"));
+ }
+
+ @Override
+ public String[] helperClassNames() {
+ return new String[] {
+ packageName + ".Bridging",
+ packageName + ".Bridging$1",
+ packageName + ".UnshadedBinaryFormat",
+ packageName + ".UnshadedHttpTextFormat",
+ packageName + ".UnshadedHttpTextFormat$UnshadedSetter",
+ packageName + ".UnshadedHttpTextFormat$UnshadedGetter",
+ packageName + ".UnshadedScope",
+ packageName + ".UnshadedSpan",
+ packageName + ".UnshadedSpanBuilder",
+ packageName + ".UnshadedTracer",
+ packageName + ".UnshadedTracerFactory"
+ };
+ }
+
+ @Override
+ public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
+ final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
+ transformers.put(
+ isMethod().and(isPublic()).and(named("getTracerFactory")).and(takesArguments(0)),
+ OpenTelemetryApiInstrumentation.class.getName() + "$GetTracerFactoryAdvice");
+ return transformers;
+ }
+
+ public static class GetTracerFactoryAdvice {
+
+ @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
+ public static void methodExit(
+ @Advice.Return(readOnly = false)
+ unshaded.io.opentelemetry.trace.TracerFactory tracerFactory) {
+ tracerFactory = new UnshadedTracerFactory();
+ }
+ }
+}

ADD
instrumentation/opentelemetry-api-0.2/src/main/java/io/opentelemetry/auto/instrumentation/otapi/UnshadedBinaryFormat.java
+package io.opentelemetry.auto.instrumentation.otapi;
+
+import static io.opentelemetry.auto.instrumentation.otapi.Bridging.toShaded;
+import static io.opentelemetry.auto.instrumentation.otapi.Bridging.toUnshaded;
+
+import unshaded.io.opentelemetry.context.propagation.BinaryFormat;
+import unshaded.io.opentelemetry.trace.SpanContext;
+
+public class UnshadedBinaryFormat implements BinaryFormat<SpanContext> {
+
+ private final io.opentelemetry.context.propagation.BinaryFormat<
+ io.opentelemetry.trace.SpanContext>
+ shadedBinaryFormat;
+
+ public UnshadedBinaryFormat(
+ final io.opentelemetry.context.propagation.BinaryFormat<io.opentelemetry.trace.SpanContext>
+ shadedBinaryFormat) {
+ this.shadedBinaryFormat = shadedBinaryFormat;
+ }
+
+ @Override
+ public byte[] toByteArray(final SpanContext spanContext) {
+ return shadedBinaryFormat.toByteArray(toShaded(spanContext));
+ }
+
+ @Override
+ public SpanContext fromByteArray(final byte[] bytes) {
+ return toUnshaded(shadedBinaryFormat.fromByteArray(bytes));
+ }
+}

ADD
instrumentation/opentelemetry-api-0.2/src/main/java/io/opentelemetry/auto/instrumentation/otapi/UnshadedHttpTextFormat.java
+package io.opentelemetry.auto.instrumentation.otapi;
+
+import static io.opentelemetry.auto.instrumentation.otapi.Bridging.toShaded;
+import static io.opentelemetry.auto.instrumentation.otapi.Bridging.toUnshaded;
+
+import java.util.List;
+import unshaded.io.opentelemetry.context.propagation.HttpTextFormat;
+import unshaded.io.opentelemetry.trace.SpanContext;
+
+public class UnshadedHttpTextFormat implements HttpTextFormat<SpanContext> {
+
+ private final io.opentelemetry.context.propagation.HttpTextFormat<
+ io.opentelemetry.trace.SpanContext>
+ shadedHttpTextFormat;
+
+ public UnshadedHttpTextFormat(
+ final io.opentelemetry.context.propagation.HttpTextFormat<io.opentelemetry.trace.SpanContext>
+ shadedHttpTextFormat) {
+ this.shadedHttpTextFormat = shadedHttpTextFormat;
+ }
+
+ @Override
+ public List<String> fields() {
+ return shadedHttpTextFormat.fields();
+ }
+
+ @Override
+ public <C> SpanContext extract(final C carrier, final HttpTextFormat.Getter<C> getter) {
+ return toUnshaded(shadedHttpTextFormat.extract(carrier, new UnshadedGetter<>(getter)));
+ }
+
+ @Override
+ public <C> void inject(
+ final SpanContext value, final C carrier, final HttpTextFormat.Setter<C> setter) {
+ shadedHttpTextFormat.inject(toShaded(value), carrier, new UnshadedSetter<>(setter));
+ }
+
+ public static class UnshadedGetter<C>
+ implements io.opentelemetry.context.propagation.HttpTextFormat.Getter<C> {
+
+ private final HttpTextFormat.Getter<C> shadedGetter;
+
+ public UnshadedGetter(final HttpTextFormat.Getter<C> shadedGetter) {
+ this.shadedGetter = shadedGetter;
+ }
+
+ @Override
+ public String get(final C carrier, final String key) {
+ return shadedGetter.get(carrier, key);
+ }
+ }
+
+ public static class UnshadedSetter<C>
+ implements io.opentelemetry.context.propagation.HttpTextFormat.Setter<C> {
+
+ private final HttpTextFormat.Setter<C> shadedSetter;
+
+ public UnshadedSetter(final Setter<C> shadedSetter) {
+ this.shadedSetter = shadedSetter;
+ }
+
+ @Override
+ public void put(final C carrier, final String key, final String value) {
+ shadedSetter.put(carrier, key, value);
+ }
+ }
+}

ADD
instrumentation/opentelemetry-api-0.2/src/main/java/io/opentelemetry/auto/instrumentation/otapi/UnshadedScope.java
+package io.opentelemetry.auto.instrumentation.otapi;
+
+import unshaded.io.opentelemetry.context.Scope;
+
+public class UnshadedScope implements Scope {
+
+ private final io.opentelemetry.context.Scope shadedScope;
+
+ public UnshadedScope(final io.opentelemetry.context.Scope shadedScope) {
+ this.shadedScope = shadedScope;
+ }
+
+ @Override
+ public void close() {
+ shadedScope.close();
+ }
+}

ADD
instrumentation/opentelemetry-api-0.2/src/main/java/io/opentelemetry/auto/instrumentation/otapi/UnshadedSpan.java
+package io.opentelemetry.auto.instrumentation.otapi;
+
+import static io.opentelemetry.auto.instrumentation.otapi.Bridging.toShaded;
+import static io.opentelemetry.auto.instrumentation.otapi.Bridging.toShadedOrNull;
+
+import java.util.Map;
+import unshaded.io.opentelemetry.trace.AttributeValue;
+import unshaded.io.opentelemetry.trace.EndSpanOptions;
+import unshaded.io.opentelemetry.trace.Event;
+import unshaded.io.opentelemetry.trace.Span;
+import unshaded.io.opentelemetry.trace.SpanContext;
+import unshaded.io.opentelemetry.trace.Status;
+
+public class UnshadedSpan implements Span {
+
+ private final io.opentelemetry.trace.Span shadedSpan;
+
+ public UnshadedSpan(final io.opentelemetry.trace.Span shadedSpan) {
+ this.shadedSpan = shadedSpan;
+ }
+
+ io.opentelemetry.trace.Span getShadedSpan() {
+ return shadedSpan;
+ }
+
+ @Override
+ public void setAttribute(final String key, final String value) {
+ shadedSpan.setAttribute(key, value);
+ }
+
+ @Override
+ public void setAttribute(final String key, final long value) {
+ shadedSpan.setAttribute(key, value);
+ }
+
+ @Override
+ public void setAttribute(final String key, final double value) {
+ shadedSpan.setAttribute(key, value);
+ }
+
+ @Override
+ public void setAttribute(final String key, final boolean value) {
+ shadedSpan.setAttribute(key, value);
+ }
+
+ @Override
+ public void setAttribute(final String key, final AttributeValue value) {
+ final io.opentelemetry.trace.AttributeValue convertedValue = Bridging.toShadedOrNull(value);
+ if (convertedValue != null) {
+ shadedSpan.setAttribute(key, convertedValue);
+ }
+ }
+
+ @Override
+ public void addEvent(final String name) {
+ shadedSpan.addEvent(name);
+ }
+
+ @Override
+ public void addEvent(final String name, final long timestamp) {
+ shadedSpan.addEvent(name, timestamp);
+ }
+
+ @Override
+ public void addEvent(final String name, final Map<String, AttributeValue> attributes) {
+ shadedSpan.addEvent(name, toShaded(attributes));
+ }
+
+ @Override
+ public void addEvent(
+ final String name, final Map<String, AttributeValue> attributes, final long timestamp) {
+ shadedSpan.addEvent(name, toShaded(attributes), timestamp);
+ }
+
+ @Override
+ public void addEvent(final Event event) {
+ addEvent(event.getName(), event.getAttributes());
+ }
+
+ @Override
+ public void addEvent(final Event event, final long timestamp) {
+ addEvent(event.getName(), event.getAttributes(), timestamp);
+ }
+
+ @Override
+ public void setStatus(final Status status) {
+ final io.opentelemetry.trace.Status shadedStatus = toShadedOrNull(status);
+ if (shadedStatus != null) {
+ shadedSpan.setStatus(shadedStatus);
+ }
+ }
+
+ @Override
+ public void updateName(final String name) {
+ shadedSpan.updateName(name);
+ }
+
+ @Override
+ public void end() {
+ shadedSpan.end();
+ }
+
+ @Override
+ public void end(final EndSpanOptions endOptions) {
+ shadedSpan.end(toShaded(endOptions));
+ }
+
+ @Override
+ public SpanContext getContext() {
+ return Bridging.toUnshaded(shadedSpan.getContext());
+ }
+
+ @Override
+ public boolean isRecording() {
+ return shadedSpan.isRecording();
+ }
+}

ADD
instrumentation/opentelemetry-api-0.2/src/main/java/io/opentelemetry/auto/instrumentation/otapi/UnshadedSpanBuilder.java
+package io.opentelemetry.auto.instrumentation.otapi;
+
+import static io.opentelemetry.auto.instrumentation.otapi.Bridging.toShaded;
+import static io.opentelemetry.auto.instrumentation.otapi.Bridging.toShadedOrNull;
+
+import java.util.Map;
+import lombok.extern.slf4j.Slf4j;
+import unshaded.io.opentelemetry.trace.AttributeValue;
+import unshaded.io.opentelemetry.trace.Link;
+import unshaded.io.opentelemetry.trace.Span;
+import unshaded.io.opentelemetry.trace.SpanContext;
+
+@Slf4j
+public class UnshadedSpanBuilder implements Span.Builder {
+
+ private final io.opentelemetry.trace.Span.Builder shadedBuilder;
+
+ public UnshadedSpanBuilder(final io.opentelemetry.trace.Span.Builder shadedBuilder) {
+ this.shadedBuilder = shadedBuilder;
+ }
+
+ @Override
+ public Span.Builder setParent(final Span parent) {
+ if (parent instanceof UnshadedSpan) {
+ shadedBuilder.setParent(((UnshadedSpan) parent).getShadedSpan());
+ } else {
+ log.debug("unexpected parent span: {}", parent);
+ }
+ return this;
+ }
+
+ @Override
+ public Span.Builder setParent(final SpanContext remoteParent) {
+ shadedBuilder.setParent(toShaded(remoteParent));
+ return this;
+ }
+
+ @Override
+ public Span.Builder setNoParent() {
+ shadedBuilder.setNoParent();
+ return this;
+ }
+
+ @Override
+ public Span.Builder addLink(final SpanContext spanContext) {
+ shadedBuilder.addLink(toShaded(spanContext));
+ return this;
+ }
+
+ @Override
+ public Span.Builder addLink(
+ final SpanContext spanContext, final Map<String, AttributeValue> attributes) {
+ shadedBuilder.addLink(toShaded(spanContext));
+ return this;
+ }
+
+ @Override
+ public Span.Builder addLink(final Link link) {
+ shadedBuilder.addLink(toShaded(link.getContext()), toShaded(link.getAttributes()));
+ return this;
+ }
+
+ @Override
+ public Span.Builder setSpanKind(final Span.Kind spanKind) {
+ final io.opentelemetry.trace.Span.Kind shadedSpanKind = toShadedOrNull(spanKind);
+ if (shadedSpanKind != null) {
+ shadedBuilder.setSpanKind(shadedSpanKind);
+ }
+ return this;
+ }
+
+ @Override
+ public Span.Builder setStartTimestamp(final long startTimestamp) {
+ shadedBuilder.setStartTimestamp(startTimestamp);
+ return this;
+ }
+
+ @Override
+ public Span startSpan() {
+ return new UnshadedSpan(shadedBuilder.startSpan());
+ }
+}

ADD
instrumentation/opentelemetry-api-0.2/src/main/java/io/opentelemetry/auto/instrumentation/otapi/UnshadedTracer.java
+package io.opentelemetry.auto.instrumentation.otapi;
+
+import lombok.extern.slf4j.Slf4j;
+import unshaded.io.opentelemetry.context.NoopScope;
+import unshaded.io.opentelemetry.context.Scope;
+import unshaded.io.opentelemetry.context.propagation.BinaryFormat;
+import unshaded.io.opentelemetry.context.propagation.HttpTextFormat;
+import unshaded.io.opentelemetry.trace.Span;
+import unshaded.io.opentelemetry.trace.SpanContext;
+import unshaded.io.opentelemetry.trace.Tracer;
+
+@Slf4j
+public class UnshadedTracer implements Tracer {
+
+ private final io.opentelemetry.trace.Tracer shadedTracer;
+
+ public UnshadedTracer(final io.opentelemetry.trace.Tracer shadedTracer) {
+ this.shadedTracer = shadedTracer;
+ }
+
+ @Override
+ public Span getCurrentSpan() {
+ return new UnshadedSpan(shadedTracer.getCurrentSpan());
+ }
+
+ @Override
+ public Scope withSpan(final Span span) {
+ if (span instanceof UnshadedSpan) {
+ return new UnshadedScope(shadedTracer.withSpan(((UnshadedSpan) span).getShadedSpan()));
+ } else {
+ log.debug("unexpected span: {}", span);
+ return NoopScope.getInstance();
+ }
+ }
+
+ @Override
+ public Span.Builder spanBuilder(final String spanName) {
+ return new UnshadedSpanBuilder(shadedTracer.spanBuilder(spanName));
+ }
+
+ @Override
+ public BinaryFormat<SpanContext> getBinaryFormat() {
+ return new UnshadedBinaryFormat(shadedTracer.getBinaryFormat());
+ }
+
+ @Override
+ public HttpTextFormat<SpanContext> getHttpTextFormat() {
+ return new UnshadedHttpTextFormat(shadedTracer.getHttpTextFormat());
+ }
+}

ADD
instrumentation/opentelemetry-api-0.2/src/main/java/io/opentelemetry/auto/instrumentation/otapi/UnshadedTracerFactory.java
+package io.opentelemetry.auto.instrumentation.otapi;
+
+import unshaded.io.opentelemetry.trace.Tracer;
+import unshaded.io.opentelemetry.trace.TracerFactory;
+
+public class UnshadedTracerFactory implements TracerFactory {
+
+ @Override
+ public Tracer get(final String instrumentationName) {
+ return new UnshadedTracer(
+ io.opentelemetry.OpenTelemetry.getTracerFactory().get(instrumentationName));
+ }
+
+ @Override
+ public Tracer get(final String instrumentationName, final String instrumentationVersion) {
+ return new UnshadedTracer(
+ io.opentelemetry.OpenTelemetry.getTracerFactory()
+ .get(instrumentationName, instrumentationVersion));
+ }
+}

ADD
instrumentation/opentelemetry-api-0.2/src/test/groovy/OpenTelemetryApiTest.groovy
+import io.opentelemetry.auto.test.AgentTestRunner
+import unshaded.io.opentelemetry.OpenTelemetry
+import unshaded.io.opentelemetry.trace.Status
+
+import static unshaded.io.opentelemetry.trace.Span.Kind.PRODUCER
+
+class OpenTelemetryApiTest extends AgentTestRunner {
+
+ def "capture span, kind, attributes, and status"() {
+ when:
+ def tracer = OpenTelemetry.getTracerFactory().get("test")
+ def testSpan = tracer.spanBuilder("test").setSpanKind(PRODUCER).startSpan()
+ testSpan.setAttribute("string", "1")
+ testSpan.setAttribute("long", 2)
+ testSpan.setAttribute("double", 3.0)
+ testSpan.setAttribute("boolean", true)
+ testSpan.setStatus(Status.UNKNOWN)
+ testSpan.end()
+
+ then:
+ assertTraces(1) {
+ trace(0, 1) {
+ span(0) {
+ operationName "test"
+ spanKind io.opentelemetry.trace.Span.Kind.PRODUCER
+ parent()
+ status io.opentelemetry.trace.Status.UNKNOWN
+ tags {
+ "string" "1"
+ "long" 2
+ "double" 3.0
+ "boolean" true
+ }
+ }
+ }
+ }
+ }
+
+ def "capture span with implicit parent"() {
+ when:
+ def tracer = OpenTelemetry.getTracerFactory().get("test")
+ def parentSpan = tracer.spanBuilder("parent").startSpan()
+ def parentScope = tracer.withSpan(parentSpan)
+ def testSpan = tracer.spanBuilder("test").startSpan()
+ testSpan.end()
+ parentSpan.end()
+ parentScope.close()
+
+ then:
+ assertTraces(1) {
+ trace(0, 2) {
+ span(0) {
+ operationName "parent"
+ parent()
+ tags {
+ }
+ }
+ span(1) {
+ operationName "test"
+ childOf span(0)
+ tags {
+ }
+ }
+ }
+ }
+ }
+
+ def "capture span with explicit parent"() {
+ when:
+ def tracer = OpenTelemetry.getTracerFactory().get("test")
+ def parentSpan = tracer.spanBuilder("parent").startSpan()
+ def testSpan = tracer.spanBuilder("test").setParent(parentSpan).startSpan()
+ testSpan.end()
+ parentSpan.end()
+
+ then:
+ assertTraces(1) {
+ trace(0, 2) {
+ span(0) {
+ operationName "parent"
+ parent()
+ tags {
+ }
+ }
+ span(1) {
+ operationName "test"
+ childOf span(0)
+ tags {
+ }
+ }
+ }
+ }
+ }
+
+ def "capture span with explicit no parent"() {
+ when:
+ def tracer = OpenTelemetry.getTracerFactory().get("test")
+ def parentSpan = tracer.spanBuilder("parent").startSpan()
+ def parentScope = tracer.withSpan(parentSpan)
+ def testSpan = tracer.spanBuilder("test").setNoParent().startSpan()
+ testSpan.end()
+ parentSpan.end()
+ parentScope.close()
+
+ then:
+ assertTraces(2) {
+ trace(0, 1) {
+ span(0) {
+ operationName "parent"
+ parent()
+ tags {
+ }
+ }
+ }
+ trace(1, 1) {
+ span(0) {
+ operationName "test"
+ parent()
+ tags {
+ }
+ }
+ }
+ }
+ }
+
+ def "capture span with remote parent"() {
+ when:
+ def tracer = OpenTelemetry.getTracerFactory().get("test")
+ def parentSpan = tracer.spanBuilder("parent").startSpan()
+ def testSpan = tracer.spanBuilder("test").setParent(parentSpan.getContext()).startSpan()
+ testSpan.end()
+ parentSpan.end()
+
+ then:
+ assertTraces(1) {
+ trace(0, 2) {
+ span(0) {
+ operationName "parent"
+ parent()
+ tags {
+ }
+ }
+ span(1) {
+ operationName "test"
+ childOf span(0)
+ tags {
+ }
+ }
+ }
+ }
+ }
+
+ def "capture name update"() {
+ when:
+ def tracer = OpenTelemetry.getTracerFactory().get("test")
+ def testSpan = tracer.spanBuilder("test").startSpan()
+ testSpan.updateName("test2")
+ testSpan.end()
+
+ then:
+ assertTraces(1) {
+ trace(0, 1) {
+ span(0) {
+ operationName "test2"
+ parent()
+ tags {
+ }
+ }
+ }
+ }
+ }
+}

ADD
opentelemetry-api-shaded-for-instrumenting/opentelemetry-api-shaded-for-instrumenting.gradle
+plugins {
+ id "com.github.johnrengelman.shadow"
+}
+
+apply from: "${rootDir}/gradle/java.gradle"
+
+dependencies {
+ compile deps.opentelemetryApi
+}
+
+// OpenTelemetry API shaded so that it can be used in opentelemetry-api instrumentation, and then its usage can be
+// unshaded after OpenTelemetry API is shaded (see more explanation in opentelemetry-api-0.2.gradle)
+shadowJar {
+
+ relocate "io.opentelemetry", "unshaded.io.opentelemetry"
+}

MODIFY
settings.gradle
settings.gradle
@@ -21,6 +21,7 @@ rootProject.name = 'trace-java'
// agent projects
include ':java-agent'
include ':opentelemetry-sdk-shaded-for-testing'
+include ':opentelemetry-api-shaded-for-instrumenting'
include ':agent-bootstrap'
include ':agent-tooling'
include ':load-generator'
@@ -109,6 +110,7 @@ include ':instrumentation:mongo:driver-async-3.3'
include ':instrumentation:netty-4.0'
include ':instrumentation:netty-4.1'
include ':instrumentation:okhttp-3.0'
+include ':instrumentation:opentelemetry-api-0.2'
include ':instrumentation:play-2.4'
include ':instrumentation:play-2.6'
include ':instrumentation:play-ws-1.0'

MODIFY
testing/src/main/groovy/io/opentelemetry/auto/test/asserts/SpanAssert.groovy
testing/src/main/groovy/io/opentelemetry/auto/test/asserts/SpanAssert.groovy
@@ -97,6 +97,11 @@ class SpanAssert {
assert found
}
+ def status(Status status) {
+ assert span.status == status
+ checked.status = true
+ }
+
def errored(boolean errored) {
if (errored) {
assert span.status != Status.OK

Add instrumentation for OpenTelemetry API itself (#153)