Skip to content

Commit

Permalink
Fix WithSpan annotation instrumentation (open-telemetry#929)
Browse files Browse the repository at this point in the history
  • Loading branch information
trask authored and mabdinur committed Aug 17, 2020
1 parent 475b277 commit 5cc4309
Show file tree
Hide file tree
Showing 28 changed files with 231 additions and 93 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ OpenTelemetry `getTracer` and API directly, configure a `@WithSpan`
annotation. Add the trace annotation to your application's code:

```java
import io.opentelemetry.contrib.auto.annotations.WithSpan;
import io.opentelemetry.extensions.auto.annotations.WithSpan;

public class MyClass {
@WithSpan
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,11 @@
* limitations under the License.
*/

package io.opentelemetry.auto.instrumentation.traceannotation;
package io.opentelemetry.auto.config;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import io.opentelemetry.auto.config.Config;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
Expand Down Expand Up @@ -59,7 +58,7 @@ public static Map<String, Set<String>> parse(String configString) {
configString);
return Collections.emptyMap();
} else {
Map<String, Set<String>> toTrace = Maps.newHashMap();
Map<String, Set<String>> toTrace = new HashMap<>();
String[] classMethods = configString.split(";", -1);
for (String classMethod : classMethods) {
if (classMethod.trim().isEmpty()) {
Expand All @@ -70,7 +69,7 @@ public static Map<String, Set<String>> parse(String configString) {
String method = splitClassMethod[1].trim();
String methodNames = method.substring(0, method.length() - 1);
String[] splitMethodNames = methodNames.split(",", -1);
Set<String> trimmedMethodNames = Sets.newHashSetWithExpectedSize(splitMethodNames.length);
Set<String> trimmedMethodNames = new HashSet<>(splitMethodNames.length);
for (String methodName : splitMethodNames) {
String trimmedMethodName = methodName.trim();
if (!trimmedMethodName.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

package io.opentelemetry.auto.instrumentation.traceannotation
package io.opentelemetry.auto.config


import spock.lang.Specification
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ public class AutoInstrumentationPlugin implements Plugin<Project> {
"io.opentelemetry.internal", // OpenTelemetry API
"io.opentelemetry.metrics", // OpenTelemetry API
"io.opentelemetry.trace", // OpenTelemetry API
"io.opentelemetry.contrib.auto.annotations", // OpenTelemetry API Contrib
"io.grpc.Context", // OpenTelemetry API dependency
"io.grpc.Deadline", // OpenTelemetry API dependency
"io.grpc.PersistentHashArrayMappedTrie", // OpenTelemetry API dependency
Expand Down
42 changes: 21 additions & 21 deletions gradle/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,22 @@ ext {

deps = [
// OpenTelemetry
opentelemetryApi : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-api', version: versions.opentelemetry),
opentelemetryApiAutoAnnotations: dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-extension-auto-annotations', version: versions.opentelemetryOther),
opentelemetryTraceProps : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-extension-trace-propagators', version: versions.opentelemetryOther),
opentelemetrySdk : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-sdk', version: versions.opentelemetry),
opentelemetrySdkAutoConfig : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-sdk-extension-auto-config', version: versions.opentelemetryOther),
opentelemetryJaeger : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-exporters-jaeger', version: versions.opentelemetry),
opentelemetryOtlp : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-exporters-otlp', version: versions.opentelemetryOther),
opentelemetryZipkin : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-exporters-zipkin', version: versions.opentelemetryOther),
opentelemetryLogging : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-exporters-logging', version: versions.opentelemetryOther),
opentelemetryApi : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-api', version: versions.opentelemetry),
opentelemetryAutoAnnotations: dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-extension-auto-annotations', version: versions.opentelemetryOther),
opentelemetryTraceProps : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-extension-trace-propagators', version: versions.opentelemetryOther),
opentelemetrySdk : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-sdk', version: versions.opentelemetry),
opentelemetrySdkAutoConfig : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-sdk-extension-auto-config', version: versions.opentelemetryOther),
opentelemetryJaeger : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-exporters-jaeger', version: versions.opentelemetry),
opentelemetryOtlp : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-exporters-otlp', version: versions.opentelemetryOther),
opentelemetryZipkin : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-exporters-zipkin', version: versions.opentelemetryOther),
opentelemetryLogging : dependencies.create(group: 'io.opentelemetry', name: 'opentelemetry-exporters-logging', version: versions.opentelemetryOther),

// General
slf4j : "org.slf4j:slf4j-api:${versions.slf4j}",
guava : "com.google.guava:guava:$versions.guava",
bytebuddy : dependencies.create(group: 'net.bytebuddy', name: 'byte-buddy', version: "${versions.bytebuddy}"),
bytebuddyagent : dependencies.create(group: 'net.bytebuddy', name: 'byte-buddy-agent', version: "${versions.bytebuddy}"),
autoservice : [
slf4j : "org.slf4j:slf4j-api:${versions.slf4j}",
guava : "com.google.guava:guava:$versions.guava",
bytebuddy : dependencies.create(group: 'net.bytebuddy', name: 'byte-buddy', version: "${versions.bytebuddy}"),
bytebuddyagent : dependencies.create(group: 'net.bytebuddy', name: 'byte-buddy-agent', version: "${versions.bytebuddy}"),
autoservice : [
dependencies.create(group: 'com.google.auto.service', name: 'auto-service', version: '1.0-rc3'),
dependencies.create(group: 'com.google.auto', name: 'auto-common', version: '0.8'),
// These are the last versions that support guava 20.0. Upgrading has odd interactions with shadow.
Expand All @@ -52,23 +52,23 @@ ext {

// Testing

spock : [
spock : [
dependencies.create("org.spockframework:spock-core:${versions.spock}", {
exclude group: 'org.codehaus.groovy', module: 'groovy-all'
}),
// Used by Spock for mocking:
dependencies.create(group: 'org.objenesis', name: 'objenesis', version: '2.6') // Last version to support Java7
],
groovy : "org.codehaus.groovy:groovy-all:${versions.groovy}",
testcontainers : "org.testcontainers:testcontainers:1.12.2",
testLogging : [
groovy : "org.codehaus.groovy:groovy-all:${versions.groovy}",
testcontainers : "org.testcontainers:testcontainers:1.12.2",
testLogging : [
dependencies.create(group: 'ch.qos.logback', name: 'logback-classic', version: versions.logback),
dependencies.create(group: 'org.slf4j', name: 'log4j-over-slf4j', version: versions.slf4j),
dependencies.create(group: 'org.slf4j', name: 'jcl-over-slf4j', version: versions.slf4j),
dependencies.create(group: 'org.slf4j', name: 'jul-to-slf4j', version: versions.slf4j),
],
scala : dependencies.create(group: 'org.scala-lang', name: 'scala-library', version: "${versions.scala}"),
kotlin : dependencies.create(group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: "${versions.kotlin}"),
coroutines : dependencies.create(group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: "${versions.coroutines}"),
scala : dependencies.create(group: 'org.scala-lang', name: 'scala-library', version: "${versions.scala}"),
kotlin : dependencies.create(group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: "${versions.kotlin}"),
coroutines : dependencies.create(group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-core', version: "${versions.coroutines}"),
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ dependencies {
implementation group: 'org.springframework.boot', name: 'spring-boot-autoconfigure', version: versions.springboot
annotationProcessor group: 'org.springframework.boot', name: 'spring-boot-autoconfigure-processor', version: versions.springboot
implementation group: 'javax.validation', name: 'validation-api', version: '2.0.1.Final'

compileOnly group: 'org.springframework.boot', name: 'spring-boot-starter-aop', version: versions.springboot
compileOnly group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: versions.springboot
compileOnly group: 'org.springframework.boot', name: 'spring-boot-starter-webflux', version: versions.springboot
implementation project(':instrumentation-core:spring:spring-webmvc-3.1')
implementation project(':instrumentation-core:spring:spring-webmvc-3.1')
implementation project(':instrumentation-core:spring:spring-web-3.1')
implementation project(':instrumentation-core:spring:spring-webflux-5.0')
compileOnly deps.opentelemetryApiAutoAnnotations

compileOnly deps.opentelemetryAutoAnnotations
compileOnly group: 'io.grpc', name: 'grpc-api', version: '1.30.2'
compileOnly deps.opentelemetryLogging
compileOnly deps.opentelemetryJaeger
Expand All @@ -34,9 +34,9 @@ dependencies {
testImplementation(group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: versions.springboot) {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}

testImplementation deps.opentelemetrySdk
testImplementation deps.opentelemetryApiAutoAnnotations
testImplementation deps.opentelemetryAutoAnnotations
testImplementation group: 'io.grpc', name: 'grpc-api', version: '1.30.2'
testImplementation group: 'io.grpc', name: 'grpc-netty-shaded', version: '1.30.2'
testImplementation deps.opentelemetryLogging
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ apply from: "$rootDir/gradle/publish.gradle"

sourceCompatibility = '8'

dependencies {
dependencies {
api group: "org.springframework.boot", name: "spring-boot-starter", version: versions.springboot
api group: 'org.springframework.boot', name: 'spring-boot-starter-aop', version: versions.springboot
api project(':instrumentation-core:spring:spring-boot-autoconfigure')
api deps.opentelemetryApiAutoAnnotations
api deps.opentelemetryAutoAnnotations
api deps.opentelemetryApi
api deps.opentelemetryLogging
api deps.opentelemetrySdk
Expand Down
2 changes: 0 additions & 2 deletions instrumentation/annotations/annotations.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ muzzle {
}

dependencies {
implementation deps.opentelemetryApiAutoAnnotations

testImplementation group: 'com.newrelic.agent.java', name: 'newrelic-api', version: '+'
testImplementation(group: 'io.opentracing.contrib.dropwizard', name: 'dropwizard-opentracing', version: '0.2.2') {
transitive = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static net.bytebuddy.matcher.ElementMatchers.none;

import io.opentelemetry.auto.config.Config;
import io.opentelemetry.auto.config.MethodsConfigurationParser;
import io.opentelemetry.auto.tooling.Instrumenter;
import java.util.Map;
import java.util.Set;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import com.google.auto.service.AutoService;
import io.opentelemetry.auto.config.Config;
import io.opentelemetry.auto.config.MethodsConfigurationParser;
import io.opentelemetry.auto.tooling.Instrumenter;
import java.util.Collections;
import java.util.Map;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,11 @@

import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.auto.bootstrap.instrumentation.decorator.BaseDecorator;
import io.opentelemetry.extensions.auto.annotations.WithSpan;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Span.Kind;
import io.opentelemetry.trace.Tracer;
import java.lang.reflect.Method;

public class TraceDecorator extends BaseDecorator {
public static final TraceDecorator DECORATE = new TraceDecorator();

public static final Tracer TRACER =
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.trace-annotation");

/**
* This method is used to generate an acceptable span (operation) name based on a given method
* reference. It first checks for existence of {@link WithSpan} annotation. If it is present, then
* tries to derive name from its {@code value} attribute. Otherwise delegates to {@link
* #spanNameForMethod(Method)}.
*/
public String spanNameForMethodWithAnnotation(final Method method) {
WithSpan annotation = method.getAnnotation(WithSpan.class);
if (annotation != null && !annotation.value().isEmpty()) {
return annotation.value();
}

return spanNameForMethod(method);
}

public Span.Kind extractSpanKind(final Method method) {
WithSpan annotation = method.getAnnotation(WithSpan.class);
return annotation != null ? annotation.kind() : Kind.INTERNAL;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ dependencies {
// 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-beta-shaded-for-instrumenting', configuration: 'shadow')
compileOnly project(path: ':opentelemetry-auto-annotations-beta-shaded-for-instrumenting', configuration: 'shadow')

// using OpenTelemetry SDK to make sure that instrumentation doesn't cause
// OpenTelemetrySdk.getTracerProvider() to throw ClassCastException
testImplementation project(path: ':opentelemetry-sdk-beta-shaded-for-instrumenting', configuration: 'shadow')
testImplementation project(path: ':opentelemetry-auto-annotations-beta-shaded-for-instrumenting', configuration: 'shadow')
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.opentelemetry.auto.instrumentation.opentelemetryapi.anotations;

import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
import static net.bytebuddy.matcher.ElementMatchers.none;

import io.opentelemetry.auto.config.Config;
import io.opentelemetry.auto.config.MethodsConfigurationParser;
import io.opentelemetry.auto.tooling.Instrumenter;
import java.util.Map;
import java.util.Set;
import net.bytebuddy.description.ByteCodeElement;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;

public abstract class AbstractTraceAnnotationInstrumentation extends Instrumenter.Default {
public AbstractTraceAnnotationInstrumentation(
String instrumentationName, String... additionalNames) {
super(instrumentationName, additionalNames);
}

/*
Returns a matcher for all methods that should be excluded from auto-instrumentation by
annotation-based advices.
*/
ElementMatcher.Junction<MethodDescription> configureExcludedMethods() {
ElementMatcher.Junction<MethodDescription> result = none();

Map<String, Set<String>> excludedMethods =
MethodsConfigurationParser.parse(Config.get().getTraceAnnotatedMethodsExclude());
for (Map.Entry<String, Set<String>> entry : excludedMethods.entrySet()) {
String className = entry.getKey();
ElementMatcher.Junction<ByteCodeElement> classMather =
isDeclaredBy(ElementMatchers.<TypeDescription>named(className));

ElementMatcher.Junction<MethodDescription> excludedMethodsMatcher = none();
for (String methodName : entry.getValue()) {
excludedMethodsMatcher = excludedMethodsMatcher.or(ElementMatchers.named(methodName));
}

result = result.or(classMather.and(excludedMethodsMatcher));
}

return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.opentelemetry.auto.instrumentation.opentelemetryapi.anotations;

import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.auto.bootstrap.instrumentation.decorator.BaseDecorator;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Tracer;
import java.lang.reflect.Method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import unshaded.io.opentelemetry.extensions.auto.annotations.WithSpan;
import unshaded.io.opentelemetry.trace.Span.Kind;

public class TraceDecorator extends BaseDecorator {
public static final TraceDecorator DECORATE = new TraceDecorator();

public static final Tracer TRACER =
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.trace-annotation");

private static final Logger log = LoggerFactory.getLogger(TraceDecorator.class);

/**
* This method is used to generate an acceptable span (operation) name based on a given method
* reference. It first checks for existence of {@link WithSpan} annotation. If it is present, then
* tries to derive name from its {@code value} attribute. Otherwise delegates to {@link
* #spanNameForMethod(Method)}.
*/
public String spanNameForMethodWithAnnotation(WithSpan annotation, Method method) {
if (annotation != null && !annotation.value().isEmpty()) {
return annotation.value();
}
return spanNameForMethod(method);
}

public Span.Kind extractSpanKind(WithSpan annotation) {
Kind unshadedKind = annotation != null ? annotation.kind() : Kind.INTERNAL;
return toShadedOrNull(unshadedKind);
}

public static io.opentelemetry.trace.Span.Kind toShadedOrNull(
final unshaded.io.opentelemetry.trace.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 io.opentelemetry.trace.Span.Kind.INTERNAL;
}
}
}
Loading

0 comments on commit 5cc4309

Please sign in to comment.