-
Notifications
You must be signed in to change notification settings - Fork 319
Experimental instrumentation for the OpenTelemetry Tracing API #1605
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
74e90dd
bf7fac3
a0517bc
b190956
1ac044c
0f9bbbc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| // Made this a variable so we can easily update to latest releases. | ||
| def otelVersion = '0.3.0' | ||
|
|
||
| muzzle { | ||
| pass { | ||
| module = 'opentelemetry-api' | ||
| group = 'io.opentelemetry' | ||
| versions = "[$otelVersion,]" | ||
| assertInverse = true | ||
| skipVersions = ['0.2.2', '0.2.3'] | ||
| } | ||
| } | ||
|
|
||
| apply from: "$rootDir/gradle/java.gradle" | ||
|
|
||
| apply plugin: 'org.unbroken-dome.test-sets' | ||
|
|
||
| testSets { | ||
| latestDepTest { | ||
| dirName = 'test' | ||
| } | ||
| } | ||
|
|
||
| dependencies { | ||
| compileOnly group: 'io.opentelemetry', name: 'opentelemetry-api', version: otelVersion | ||
|
|
||
| compileOnly group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2' | ||
| compileOnly group: 'com.google.auto.value', name: 'auto-value-annotations', version: '1.6.6' | ||
|
|
||
| testCompile group: 'io.opentelemetry', name: 'opentelemetry-api', version: otelVersion | ||
| latestDepTestCompile group: 'io.opentelemetry', name: 'opentelemetry-api', version: '+' | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| package datadog.trace.instrumentation.opentelemetry; | ||
|
|
||
| import static net.bytebuddy.matcher.ElementMatchers.named; | ||
| import static net.bytebuddy.matcher.ElementMatchers.returns; | ||
|
|
||
| import com.google.auto.service.AutoService; | ||
| import datadog.trace.agent.tooling.Instrumenter; | ||
| import io.opentelemetry.context.propagation.ContextPropagators; | ||
| import io.opentelemetry.trace.TracerProvider; | ||
| 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; | ||
|
|
||
| /** | ||
| * This is experimental instrumentation and should only be enabled for evaluation/testing purposes. | ||
| */ | ||
| @AutoService(Instrumenter.class) | ||
| public class OpenTelemetryInstrumentation extends Instrumenter.Default { | ||
| public OpenTelemetryInstrumentation() { | ||
| super("opentelemetry-beta"); | ||
| } | ||
|
|
||
| @Override | ||
| protected boolean defaultEnabled() { | ||
| return false; | ||
| } | ||
|
|
||
| @Override | ||
| public ElementMatcher<TypeDescription> typeMatcher() { | ||
| return named("io.opentelemetry.OpenTelemetry"); | ||
| } | ||
|
|
||
| @Override | ||
| public String[] helperClassNames() { | ||
| return new String[] { | ||
| packageName + ".OtelScope", | ||
| packageName + ".OtelSpan", | ||
| packageName + ".OtelSpan$1", // switch statement | ||
| packageName + ".OtelSpanContext", | ||
| packageName + ".OtelTracer", | ||
| packageName + ".OtelTracer$1", // switch statement | ||
| packageName + ".OtelTracerProvider", | ||
| packageName + ".OtelTracer$SpanBuilder", | ||
| packageName + ".OtelContextPropagators", | ||
| packageName + ".OtelContextPropagators$1", // switch statement | ||
| packageName + ".OtelContextPropagators$OtelHttpTextFormat", | ||
| packageName + ".OtelContextPropagators$OtelSetter", | ||
| packageName + ".OtelContextPropagators$OtelGetter", | ||
| packageName + ".TypeConverter", | ||
| }; | ||
| } | ||
|
|
||
| @Override | ||
| public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() { | ||
| final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>(); | ||
| transformers.put( | ||
| named("getTracerProvider").and(returns(named("io.opentelemetry.trace.TracerProvider"))), | ||
| OpenTelemetryInstrumentation.class.getName() + "$TracerProviderAdvice"); | ||
| transformers.put( | ||
| named("getPropagators") | ||
| .and(returns(named("io.opentelemetry.context.propagation.ContextPropagators"))), | ||
| OpenTelemetryInstrumentation.class.getName() + "$ContextPropagatorsAdvice"); | ||
| return transformers; | ||
| } | ||
|
|
||
| public static class TracerProviderAdvice { | ||
| @Advice.OnMethodExit(suppress = Throwable.class) | ||
| public static void returnProvider(@Advice.Return(readOnly = false) TracerProvider result) { | ||
| result = OtelTracerProvider.INSTANCE; | ||
| } | ||
| } | ||
|
|
||
| public static class ContextPropagatorsAdvice { | ||
| @Advice.OnMethodExit(suppress = Throwable.class) | ||
| public static void returnProvider(@Advice.Return(readOnly = false) ContextPropagators result) { | ||
| result = OtelContextPropagators.INSTANCE; | ||
| } | ||
|
|
||
| // Muzzle doesn't detect the advice method's argument type, so we have to help it a bit. | ||
| public static void muzzleCheck(final ContextPropagators propagators) { | ||
| propagators.getHttpTextFormat(); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| package datadog.trace.instrumentation.opentelemetry; | ||
|
|
||
| import datadog.trace.bootstrap.instrumentation.api.AgentPropagation; | ||
| import datadog.trace.bootstrap.instrumentation.api.AgentSpan; | ||
| import datadog.trace.bootstrap.instrumentation.api.AgentTracer; | ||
| import io.grpc.Context; | ||
| import io.opentelemetry.context.propagation.ContextPropagators; | ||
| import io.opentelemetry.context.propagation.HttpTextFormat; | ||
| import io.opentelemetry.trace.DefaultSpan; | ||
| import io.opentelemetry.trace.Span; | ||
| import io.opentelemetry.trace.TracingContextUtils; | ||
| import java.util.Arrays; | ||
| import java.util.List; | ||
|
|
||
| public class OtelContextPropagators implements ContextPropagators { | ||
| public static final OtelContextPropagators INSTANCE = new OtelContextPropagators(); | ||
tylerbenson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| private OtelContextPropagators() {} | ||
|
|
||
| @Override | ||
| public HttpTextFormat getHttpTextFormat() { | ||
| return OtelHttpTextFormat.INSTANCE; | ||
| } | ||
|
|
||
| private static class OtelHttpTextFormat implements HttpTextFormat { | ||
| private static final OtelHttpTextFormat INSTANCE = new OtelHttpTextFormat(); | ||
|
|
||
| private final AgentTracer.TracerAPI tracer = AgentTracer.get(); | ||
| private final TypeConverter converter = new TypeConverter(); | ||
|
|
||
| @Override | ||
| public List<String> fields() { | ||
| return null; | ||
| } | ||
|
|
||
| @Override | ||
| public <C> void inject(final Context context, final C carrier, final Setter<C> setter) { | ||
| final Span span = TracingContextUtils.getSpanWithoutDefault(context); | ||
| if (span == null || !span.getContext().isValid()) { | ||
| return; | ||
| } | ||
| tracer.inject(converter.toAgentSpan(span), carrier, new OtelSetter<>(setter)); | ||
| } | ||
|
|
||
| @Override | ||
| public <C> Context extract(final Context context, final C carrier, final Getter<C> getter) { | ||
| final AgentSpan.Context agentContext = tracer.extract(carrier, new OtelGetter<>(getter)); | ||
| return TracingContextUtils.withSpan( | ||
| DefaultSpan.create(converter.toSpanContext(agentContext)), context); | ||
| } | ||
| } | ||
|
|
||
| private static class OtelSetter<C> implements AgentPropagation.Setter<C> { | ||
| private final HttpTextFormat.Setter<C> setter; | ||
|
|
||
| private OtelSetter(final HttpTextFormat.Setter<C> setter) { | ||
| this.setter = setter; | ||
| } | ||
|
|
||
| @Override | ||
| public void set(final C carrier, final String key, final String value) { | ||
| setter.set(carrier, key, value); | ||
| } | ||
| } | ||
|
|
||
| private static class OtelGetter<C> implements AgentPropagation.Getter<C> { | ||
| private static final String DD_TRACE_ID_KEY = "x-datadog-trace-id"; | ||
| private static final String DD_SPAN_ID_KEY = "x-datadog-parent-id"; | ||
| private static final String DD_SAMPLING_PRIORITY_KEY = "x-datadog-sampling-priority"; | ||
| private static final String DD_ORIGIN_KEY = "x-datadog-origin"; | ||
|
|
||
| private static final String B3_TRACE_ID_KEY = "X-B3-TraceId"; | ||
| private static final String B3_SPAN_ID_KEY = "X-B3-SpanId"; | ||
| private static final String B3_SAMPLING_PRIORITY_KEY = "X-B3-Sampled"; | ||
|
|
||
| private static final String HAYSTACK_TRACE_ID_KEY = "Trace-ID"; | ||
| private static final String HAYSTACK_SPAN_ID_KEY = "Span-ID"; | ||
| private static final String HAYSTACK_PARENT_ID_KEY = "Parent_ID"; | ||
|
|
||
| private static final List<String> KEYS = | ||
| Arrays.asList( | ||
| DD_TRACE_ID_KEY, | ||
| DD_SPAN_ID_KEY, | ||
| DD_SAMPLING_PRIORITY_KEY, | ||
| DD_ORIGIN_KEY, | ||
| B3_TRACE_ID_KEY, | ||
| B3_SPAN_ID_KEY, | ||
| B3_SAMPLING_PRIORITY_KEY, | ||
| HAYSTACK_TRACE_ID_KEY, | ||
| HAYSTACK_SPAN_ID_KEY, | ||
| HAYSTACK_PARENT_ID_KEY); | ||
|
|
||
| private final HttpTextFormat.Getter<C> getter; | ||
|
|
||
| private OtelGetter(final HttpTextFormat.Getter<C> getter) { | ||
| this.getter = getter; | ||
| } | ||
|
|
||
| @Override | ||
| public Iterable<String> keys(final C carrier) { | ||
| // TODO: Otel doesn't expose the keys, so we have to rely on hard coded keys. | ||
richardstartin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // https://github.com/open-telemetry/opentelemetry-specification/issues/433 | ||
| return KEYS; | ||
| } | ||
|
|
||
| @Override | ||
| public String get(final C carrier, final String key) { | ||
| return getter.get(carrier, key); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps we should be aiming to use this more since every HTTP API can do this quickly. |
||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package datadog.trace.instrumentation.opentelemetry; | ||
|
|
||
| import datadog.trace.bootstrap.instrumentation.api.AgentScope; | ||
| import datadog.trace.context.TraceScope; | ||
| import io.opentelemetry.context.Scope; | ||
|
|
||
| public class OtelScope implements Scope, TraceScope { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't expect our Otel objects to implement our objects -- just the Otel objects. |
||
| private final AgentScope delegate; | ||
richardstartin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| OtelScope(final AgentScope delegate) { | ||
| this.delegate = delegate; | ||
| } | ||
|
|
||
| @Override | ||
| public Continuation capture() { | ||
| return delegate.capture(); | ||
| } | ||
|
|
||
| @Override | ||
| public void close() { | ||
| delegate.close(); | ||
| } | ||
|
|
||
| @Override | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm surprised to see the async propagation methods on this class. I realize it follows from implementing datadog's TraceScope, but wonder why we need to do that. |
||
| public boolean isAsyncPropagating() { | ||
| return delegate.isAsyncPropagating(); | ||
| } | ||
|
|
||
| @Override | ||
| public void setAsyncPropagation(final boolean value) { | ||
| delegate.setAsyncPropagation(value); | ||
| } | ||
|
|
||
| public AgentScope getDelegate() { | ||
| return delegate; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.