Made couchbase agent compliant to semantic conventions (#189)

* Made couchbase agent compliant to semantic conventions

* Made tests resilient to variations in internally generated queries

* Fixed test function signatures
This commit is contained in:
Pontus Rydin 2020-02-27 15:28:18 -05:00 committed by GitHub
parent 3cb25f0b52
commit 147b536891
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 91 additions and 42 deletions

View File

@ -1,6 +1,5 @@
package io.opentelemetry.auto.instrumentation.couchbase.client;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
@ -13,6 +12,7 @@ import com.google.auto.service.AutoService;
import io.opentelemetry.auto.bootstrap.CallDepthThreadLocalMap;
import io.opentelemetry.auto.tooling.Instrumenter;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
@ -52,9 +52,14 @@ public class CouchbaseBucketInstrumentation extends Instrumenter.Default {
@Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
return singletonMap(
isMethod().and(isPublic()).and(returns(named("rx.Observable"))),
final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
transformers.put(
isMethod().and(isPublic()).and(returns(named("rx.Observable"))).and(not(named("query"))),
CouchbaseBucketInstrumentation.class.getName() + "$CouchbaseClientAdvice");
transformers.put(
isMethod().and(isPublic()).and(returns(named("rx.Observable"))).and(named("query")),
CouchbaseBucketInstrumentation.class.getName() + "$CouchbaseClientQueryAdvice");
return transformers;
}
public static class CouchbaseClientAdvice {
@ -74,8 +79,35 @@ public class CouchbaseBucketInstrumentation extends Instrumenter.Default {
return;
}
CallDepthThreadLocalMap.reset(CouchbaseCluster.class);
result = Observable.create(new CouchbaseOnSubscribe(result, method, bucket, null));
}
}
result = Observable.create(new CouchbaseOnSubscribe(result, method, bucket));
public static class CouchbaseClientQueryAdvice {
@Advice.OnMethodEnter
public static int trackCallDepth() {
return CallDepthThreadLocalMap.incrementCallDepth(CouchbaseCluster.class);
}
@Advice.OnMethodExit(onThrowable = Throwable.class)
public static void subscribeResult(
@Advice.Enter final int callDepth,
@Advice.Origin final Method method,
@Advice.FieldValue("bucket") final String bucket,
@Advice.Argument(value = 0, optional = true) final Object query,
@Advice.Return(readOnly = false) Observable result) {
if (callDepth > 0) {
return;
}
CallDepthThreadLocalMap.reset(CouchbaseCluster.class);
// A query can be of many different types. We could track the creation of them and try to
// rewind back to when they were created from a string, but for now we rely on toString()
// returning something useful. That seems to be the case. If we're starting to see strange
// query texts, this is the place to look!
final String queryText = query != null ? query.toString() : null;
result = Observable.create(new CouchbaseOnSubscribe(result, method, bucket, queryText));
}
}
}

View File

@ -74,7 +74,7 @@ public class CouchbaseClusterInstrumentation extends Instrumenter.Default {
}
CallDepthThreadLocalMap.reset(CouchbaseCluster.class);
result = Observable.create(new CouchbaseOnSubscribe(result, method, null));
result = Observable.create(new CouchbaseOnSubscribe(result, method, null, null));
}
}
}

View File

@ -4,6 +4,7 @@ import static io.opentelemetry.auto.instrumentation.couchbase.client.CouchbaseCl
import static io.opentelemetry.trace.Span.Kind.CLIENT;
import io.opentelemetry.auto.instrumentation.api.MoreTags;
import io.opentelemetry.auto.instrumentation.api.Tags;
import io.opentelemetry.auto.instrumentation.rxjava.TracedOnSubscribe;
import io.opentelemetry.trace.Span;
import java.lang.reflect.Method;
@ -12,9 +13,13 @@ import rx.Observable;
public class CouchbaseOnSubscribe extends TracedOnSubscribe {
private final String resourceName;
private final String bucket;
private final String query;
public CouchbaseOnSubscribe(
final Observable originalObservable, final Method method, final String bucket) {
final Observable originalObservable,
final Method method,
final String bucket,
final String query) {
super(originalObservable, "couchbase.call", DECORATE, CLIENT);
final Class<?> declaringClass = method.getDeclaringClass();
@ -22,6 +27,7 @@ public class CouchbaseOnSubscribe extends TracedOnSubscribe {
declaringClass.getSimpleName().replace("CouchbaseAsync", "").replace("DefaultAsync", "");
resourceName = className + "." + method.getName();
this.bucket = bucket;
this.query = query;
}
@Override
@ -31,7 +37,10 @@ public class CouchbaseOnSubscribe extends TracedOnSubscribe {
span.setAttribute(MoreTags.RESOURCE_NAME, resourceName);
if (bucket != null) {
span.setAttribute("bucket", bucket);
span.setAttribute(Tags.DB_INSTANCE, bucket);
}
if (query != null) {
span.setAttribute(Tags.DB_STATEMENT, query);
}
}
}

View File

@ -31,7 +31,7 @@ class CouchbaseAsyncClientTest extends AbstractCouchbaseTest {
assertTraces(1) {
trace(0, 2) {
assertCouchbaseCall(it, 0, "Cluster.openBucket", null)
assertCouchbaseCall(it, 1, "ClusterManager.hasBucket", null, span(0))
assertCouchbaseCall(it, 1, "ClusterManager.hasBucket", null, null, span(0))
}
}
@ -68,8 +68,8 @@ class CouchbaseAsyncClientTest extends AbstractCouchbaseTest {
trace(0, 3) {
basicSpan(it, 0, "someTrace")
assertCouchbaseCall(it, 1, "Cluster.openBucket", null, span(0))
assertCouchbaseCall(it, 2, "Bucket.upsert", bucketSettings.name(), span(1))
assertCouchbaseCall(it, 1, "Cluster.openBucket", null, null, span(0))
assertCouchbaseCall(it, 2, "Bucket.upsert", bucketSettings.name(), null, span(1))
}
}
@ -113,9 +113,9 @@ class CouchbaseAsyncClientTest extends AbstractCouchbaseTest {
trace(0, 4) {
basicSpan(it, 0, "someTrace")
assertCouchbaseCall(it, 1, "Cluster.openBucket", null, span(0))
assertCouchbaseCall(it, 2, "Bucket.upsert", bucketSettings.name(), span(1))
assertCouchbaseCall(it, 3, "Bucket.get", bucketSettings.name(), span(2))
assertCouchbaseCall(it, 1, "Cluster.openBucket", null, null, span(0))
assertCouchbaseCall(it, 2, "Bucket.upsert", bucketSettings.name(), null, span(1))
assertCouchbaseCall(it, 3, "Bucket.get", bucketSettings.name(), null, span(2))
}
}
@ -158,8 +158,8 @@ class CouchbaseAsyncClientTest extends AbstractCouchbaseTest {
trace(0, 3) {
basicSpan(it, 0, "someTrace")
assertCouchbaseCall(it, 1, "Cluster.openBucket", null, span(0))
assertCouchbaseCall(it, 2, "Bucket.query", bucketCouchbase.name(), span(1))
assertCouchbaseCall(it, 1, "Cluster.openBucket", null, null, span(0))
assertCouchbaseCall(it, 2, "Bucket.query", bucketCouchbase.name(), 'SimpleN1qlQuery{statement=SELECT mockrow}', span(1))
}
}

View File

@ -64,8 +64,8 @@ class CouchbaseClientTest extends AbstractCouchbaseTest {
}
trace(1, 3) {
basicSpan(it, 0, "someTrace")
assertCouchbaseCall(it, 1, "Bucket.upsert", bucketSettings.name(), span(0))
assertCouchbaseCall(it, 2, "Bucket.get", bucketSettings.name(), span(0))
assertCouchbaseCall(it, 1, "Bucket.upsert", bucketSettings.name(), null, span(0))
assertCouchbaseCall(it, 2, "Bucket.get", bucketSettings.name(), null, span(0))
}
}
@ -104,7 +104,7 @@ class CouchbaseClientTest extends AbstractCouchbaseTest {
assertCouchbaseCall(it, 0, "Cluster.openBucket")
}
trace(1, 1) {
assertCouchbaseCall(it, 0, "Bucket.query", bucketCouchbase.name())
assertCouchbaseCall(it, 0, "Bucket.query", bucketCouchbase.name(), 'SimpleN1qlQuery{statement=SELECT mockrow}')
}
}

View File

@ -77,7 +77,7 @@ class CouchbaseSpringRepositoryTest extends AbstractCouchbaseTest {
and:
assertTraces(1) {
trace(0, 1) {
assertCouchbaseCall(it, 0, "Bucket.query", bucketCouchbase.name())
assertCouchbaseCall(it, 0, "Bucket.query", bucketCouchbase.name(), ~/^ViewQuery\(doc\/all\).*/)
}
}
}
@ -119,8 +119,8 @@ class CouchbaseSpringRepositoryTest extends AbstractCouchbaseTest {
assertTraces(1) {
trace(0, 3) {
basicSpan(it, 0, "someTrace")
assertCouchbaseCall(it, 1, "Bucket.upsert", bucketCouchbase.name(), span(0))
assertCouchbaseCall(it, 2, "Bucket.get", bucketCouchbase.name(), span(0))
assertCouchbaseCall(it, 1, "Bucket.upsert", bucketCouchbase.name(), null, span(0))
assertCouchbaseCall(it, 2, "Bucket.get", bucketCouchbase.name(), null, span(0))
}
}
@ -146,8 +146,8 @@ class CouchbaseSpringRepositoryTest extends AbstractCouchbaseTest {
assertTraces(1) {
trace(0, 3) {
basicSpan(it, 0, "someTrace")
assertCouchbaseCall(it, 1, "Bucket.upsert", bucketCouchbase.name(), span(0))
assertCouchbaseCall(it, 2, "Bucket.upsert", bucketCouchbase.name(), span(0))
assertCouchbaseCall(it, 1, "Bucket.upsert", bucketCouchbase.name(), null, span(0))
assertCouchbaseCall(it, 2, "Bucket.upsert", bucketCouchbase.name(), null, span(0))
}
}
@ -174,9 +174,9 @@ class CouchbaseSpringRepositoryTest extends AbstractCouchbaseTest {
assertTraces(1) {
trace(0, 4) {
basicSpan(it, 0, "someTrace")
assertCouchbaseCall(it, 1, "Bucket.upsert", bucketCouchbase.name(), span(0))
assertCouchbaseCall(it, 2, "Bucket.remove", bucketCouchbase.name(), span(0))
assertCouchbaseCall(it, 3, "Bucket.query", bucketCouchbase.name(), span(0))
assertCouchbaseCall(it, 1, "Bucket.upsert", bucketCouchbase.name(), null, span(0))
assertCouchbaseCall(it, 2, "Bucket.remove", bucketCouchbase.name(), null, span(0))
assertCouchbaseCall(it, 3, "Bucket.query", bucketCouchbase.name(), ~/^ViewQuery\(doc\/all\).*/, span(0))
}
}
}

View File

@ -73,8 +73,8 @@ class CouchbaseSpringTemplateTest extends AbstractCouchbaseTest {
assertTraces(1) {
trace(0, 3) {
basicSpan(it, 0, "someTrace")
assertCouchbaseCall(it, 1, "Bucket.upsert", name, span(0))
assertCouchbaseCall(it, 2, "Bucket.get", name, span(0))
assertCouchbaseCall(it, 1, "Bucket.upsert", name, null, span(0))
assertCouchbaseCall(it, 2, "Bucket.get", name, null, span(0))
}
}
@ -98,8 +98,8 @@ class CouchbaseSpringTemplateTest extends AbstractCouchbaseTest {
assertTraces(1) {
trace(0, 3) {
basicSpan(it, 0, "someTrace")
assertCouchbaseCall(it, 1, "Bucket.upsert", name, span(0))
assertCouchbaseCall(it, 2, "Bucket.remove", name, span(0))
assertCouchbaseCall(it, 1, "Bucket.upsert", name, null, span(0))
assertCouchbaseCall(it, 2, "Bucket.remove", name, null, span(0))
}
}

View File

@ -107,7 +107,7 @@ abstract class AbstractCouchbaseTest extends AgentTestRunner {
.socketConnectTimeout(timeout.intValue())
}
void assertCouchbaseCall(TraceAssert trace, int index, String name, String bucketName = null, Object parentSpan = null) {
void assertCouchbaseCall(TraceAssert trace, int index, String name, String bucketName = null, Object dbStatement = null, Object parentSpan = null) {
trace.span(index) {
operationName "couchbase.call"
spanKind CLIENT
@ -124,7 +124,10 @@ abstract class AbstractCouchbaseTest extends AgentTestRunner {
"$Tags.COMPONENT" "couchbase-client"
"$Tags.DB_TYPE" "couchbase"
if (bucketName != null) {
"bucket" bucketName
"$Tags.DB_INSTANCE" bucketName
}
if (dbStatement != null) {
"$Tags.DB_STATEMENT" dbStatement
}
}
}

View File

@ -3,7 +3,7 @@ import io.opentelemetry.auto.test.asserts.TraceAssert
class CouchbaseAsyncClient26Test extends CouchbaseAsyncClientTest {
@Override
void assertCouchbaseCall(TraceAssert trace, int index, String name, String bucketName = null, Object parentSpan = null) {
CouchbaseSpanUtil.assertCouchbaseCall(trace, index, name, bucketName, parentSpan)
void assertCouchbaseCall(TraceAssert trace, int index, String name, String bucketName = null, Object dbStatement = null, Object parentSpan = null) {
CouchbaseSpanUtil.assertCouchbaseCall(trace, index, name, bucketName, dbStatement, parentSpan)
}
}

View File

@ -2,7 +2,7 @@ import io.opentelemetry.auto.test.asserts.TraceAssert
class CouchbaseClient26Test extends CouchbaseClientTest {
@Override
void assertCouchbaseCall(TraceAssert trace, int index, String name, String bucketName = null, Object parentSpan = null) {
CouchbaseSpanUtil.assertCouchbaseCall(trace, index, name, bucketName, parentSpan)
void assertCouchbaseCall(TraceAssert trace, int index, String name, String bucketName = null, Object dbStatement = null, Object parentSpan = null) {
CouchbaseSpanUtil.assertCouchbaseCall(trace, index, name, bucketName, dbStatement, parentSpan)
}
}

View File

@ -9,7 +9,7 @@ import static io.opentelemetry.trace.Span.Kind.CLIENT
class CouchbaseSpanUtil {
// Reusable span assertion method. Cannot directly override AbstractCouchbaseTest.assertCouchbaseSpan because
// Of the class hierarchy of these tests
static void assertCouchbaseCall(TraceAssert trace, int index, String name, String bucketName = null, Object parentSpan = null) {
static void assertCouchbaseCall(TraceAssert trace, int index, String name, String bucketName = null, Object dbStatement = null, Object parentSpan = null) {
trace.span(index) {
operationName "couchbase.call"
spanKind CLIENT
@ -31,7 +31,7 @@ class CouchbaseSpanUtil {
"$Tags.DB_TYPE" "couchbase"
if (bucketName != null) {
"bucket" bucketName
"$Tags.DB_INSTANCE" bucketName
}
// Because of caching, not all requests hit the server so this tag may be absent
@ -41,6 +41,10 @@ class CouchbaseSpanUtil {
// We assign a resourceName of 'Bucket.query' and this is shared with n1ql queries
// that do have operation ids
"couchbase.operation_id" { it == null || String }
if (dbStatement != null) {
"$Tags.DB_STATEMENT" dbStatement
}
}
}
}

View File

@ -3,8 +3,9 @@ package springdata
import io.opentelemetry.auto.test.asserts.TraceAssert
class CouchbaseSpringRepository26Test extends CouchbaseSpringRepositoryTest {
@Override
void assertCouchbaseCall(TraceAssert trace, int index, String name, String bucketName = null, Object parentSpan = null) {
CouchbaseSpanUtil.assertCouchbaseCall(trace, index, name, bucketName, parentSpan)
void assertCouchbaseCall(TraceAssert trace, int index, String name, String bucketName = null, Object dbStatement = null, Object parentSpan = null) {
CouchbaseSpanUtil.assertCouchbaseCall(trace, index, name, bucketName, dbStatement, parentSpan)
}
}

View File

@ -4,7 +4,7 @@ import io.opentelemetry.auto.test.asserts.TraceAssert
class CouchbaseSpringTemplate26Test extends CouchbaseSpringTemplateTest {
@Override
void assertCouchbaseCall(TraceAssert trace, int index, String name, String bucketName = null, Object parentSpan = null) {
CouchbaseSpanUtil.assertCouchbaseCall(trace, index, name, bucketName, parentSpan)
void assertCouchbaseCall(TraceAssert trace, int index, String name, String bucketName = null, Object dbStatement = null, Object parentSpan = null) {
CouchbaseSpanUtil.assertCouchbaseCall(trace, index, name, bucketName, dbStatement, parentSpan)
}
}