Skip to content

Commit 8289a28

Browse files
Add SOFA RPC instrumentation (Bolt, H2C, Triple protocols)
1 parent 95de525 commit 8289a28

11 files changed

Lines changed: 475 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
apply from: "$rootDir/gradle/java.gradle"
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
muzzle {
2+
pass {
3+
group = "com.alipay.sofa"
4+
module = "sofa-rpc-all"
5+
versions = "[5.0.0,)"
6+
assertInverse = true
7+
}
8+
}
9+
10+
apply from: "$rootDir/gradle/java.gradle"
11+
12+
addTestSuiteForDir('latestDepTest', 'test')
13+
14+
dependencies {
15+
compileOnly group: "com.alipay.sofa", name: "sofa-rpc-all", version: "5.6.0"
16+
17+
testImplementation group: "com.alipay.sofa", name: "sofa-rpc-all", version: "5.6.0"
18+
19+
latestDepTestImplementation group: "com.alipay.sofa", name: "sofa-rpc-all", version: "+"
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package datadog.trace.instrumentation.sofarpc;
2+
3+
import static datadog.context.propagation.Propagators.defaultPropagator;
4+
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
5+
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
6+
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
7+
import static datadog.trace.instrumentation.sofarpc.SofaRpcClientDecorator.DECORATE;
8+
import static datadog.trace.instrumentation.sofarpc.SofaRpcClientDecorator.SOFA_RPC_CLIENT;
9+
import static datadog.trace.instrumentation.sofarpc.SofaRpcInjectAdapter.SETTER;
10+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
11+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
12+
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
13+
14+
import com.alipay.sofa.rpc.core.request.SofaRequest;
15+
import com.alipay.sofa.rpc.core.response.SofaResponse;
16+
import datadog.trace.agent.tooling.Instrumenter;
17+
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
18+
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
19+
import net.bytebuddy.asm.Advice;
20+
21+
public class AbstractClusterInstrumentation
22+
implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice {
23+
24+
@Override
25+
public String instrumentedType() {
26+
return "com.alipay.sofa.rpc.client.AbstractCluster";
27+
}
28+
29+
@Override
30+
public void methodAdvice(MethodTransformer transformer) {
31+
transformer.applyAdvice(
32+
isMethod()
33+
.and(named("invoke"))
34+
.and(takesArguments(1))
35+
.and(takesArgument(0, named("com.alipay.sofa.rpc.core.request.SofaRequest"))),
36+
getClass().getName() + "$InvokeAdvice");
37+
}
38+
39+
public static class InvokeAdvice {
40+
@Advice.OnMethodEnter(suppress = Throwable.class)
41+
public static AgentScope enter(@Advice.Argument(0) SofaRequest request) {
42+
AgentSpan span = startSpan(SOFA_RPC_CLIENT);
43+
DECORATE.afterStart(span);
44+
DECORATE.onRequest(span, request);
45+
AgentScope scope = activateSpan(span);
46+
defaultPropagator().inject(span, request, SETTER);
47+
return scope;
48+
}
49+
50+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
51+
public static void exit(
52+
@Advice.Enter AgentScope scope,
53+
@Advice.Return SofaResponse response,
54+
@Advice.Thrown Throwable throwable) {
55+
if (scope == null) {
56+
return;
57+
}
58+
AgentSpan span = scope.span();
59+
DECORATE.onResponse(span, response);
60+
DECORATE.onError(span, throwable);
61+
DECORATE.beforeFinish(span);
62+
span.finish();
63+
scope.close();
64+
}
65+
}
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package datadog.trace.instrumentation.sofarpc;
2+
3+
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
4+
import static datadog.trace.bootstrap.instrumentation.api.AgentPropagation.extractContextAndGetSpanContext;
5+
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
6+
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan;
7+
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
8+
import static datadog.trace.instrumentation.sofarpc.SofaRpcExtractAdapter.GETTER;
9+
import static datadog.trace.instrumentation.sofarpc.SofaRpcServerDecorator.DECORATE;
10+
import static datadog.trace.instrumentation.sofarpc.SofaRpcServerDecorator.SOFA_RPC_SERVER;
11+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
12+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
13+
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
14+
15+
import com.alipay.sofa.rpc.core.request.SofaRequest;
16+
import com.alipay.sofa.rpc.core.response.SofaResponse;
17+
import datadog.trace.agent.tooling.Instrumenter;
18+
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
19+
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
20+
import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext;
21+
import net.bytebuddy.asm.Advice;
22+
23+
public class ProviderProxyInvokerInstrumentation
24+
implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice {
25+
26+
@Override
27+
public String instrumentedType() {
28+
return "com.alipay.sofa.rpc.server.ProviderProxyInvoker";
29+
}
30+
31+
@Override
32+
public void methodAdvice(MethodTransformer transformer) {
33+
transformer.applyAdvice(
34+
isMethod()
35+
.and(named("invoke"))
36+
.and(takesArguments(1))
37+
.and(takesArgument(0, named("com.alipay.sofa.rpc.core.request.SofaRequest"))),
38+
getClass().getName() + "$InvokeAdvice");
39+
}
40+
41+
public static class InvokeAdvice {
42+
@Advice.OnMethodEnter(suppress = Throwable.class)
43+
public static AgentScope enter(@Advice.Argument(0) SofaRequest request) {
44+
// Triple protocol uses gRPC transport: the gRPC instrumentation already handles
45+
// context propagation and creates a grpc.server span. Detect this by checking
46+
// whether an active span is already present on the current thread — if so, skip
47+
// creating a duplicate sofarpc.request span that would start an orphan trace.
48+
if (activeSpan() != null) {
49+
return null;
50+
}
51+
AgentSpanContext parentContext = extractContextAndGetSpanContext(request, GETTER);
52+
AgentSpan span = startSpan(SOFA_RPC_SERVER, parentContext);
53+
DECORATE.afterStart(span);
54+
DECORATE.onRequest(span, request);
55+
return activateSpan(span);
56+
}
57+
58+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
59+
public static void exit(
60+
@Advice.Enter AgentScope scope,
61+
@Advice.Return SofaResponse response,
62+
@Advice.Thrown Throwable throwable) {
63+
if (scope == null) {
64+
return;
65+
}
66+
AgentSpan span = scope.span();
67+
DECORATE.onResponse(span, response);
68+
DECORATE.onError(span, throwable);
69+
DECORATE.beforeFinish(span);
70+
span.finish();
71+
scope.close();
72+
}
73+
}
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package datadog.trace.instrumentation.sofarpc;
2+
3+
import com.alipay.sofa.rpc.core.request.SofaRequest;
4+
import com.alipay.sofa.rpc.core.response.SofaResponse;
5+
import datadog.trace.api.naming.SpanNaming;
6+
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
7+
import datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes;
8+
import datadog.trace.bootstrap.instrumentation.api.Tags;
9+
import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
10+
import datadog.trace.bootstrap.instrumentation.decorator.ClientDecorator;
11+
12+
public class SofaRpcClientDecorator extends ClientDecorator {
13+
14+
public static final CharSequence SOFA_RPC_CLIENT =
15+
UTF8BytesString.create(
16+
SpanNaming.instance().namingSchema().client().operationForProtocol("sofarpc"));
17+
18+
private static final CharSequence COMPONENT_NAME = UTF8BytesString.create("sofarpc-client");
19+
20+
public static final SofaRpcClientDecorator DECORATE = new SofaRpcClientDecorator();
21+
22+
@Override
23+
protected String[] instrumentationNames() {
24+
return new String[] {"sofarpc"};
25+
}
26+
27+
@Override
28+
protected CharSequence component() {
29+
return COMPONENT_NAME;
30+
}
31+
32+
@Override
33+
protected CharSequence spanType() {
34+
return InternalSpanTypes.RPC;
35+
}
36+
37+
@Override
38+
protected String service() {
39+
return null;
40+
}
41+
42+
public AgentSpan onRequest(AgentSpan span, SofaRequest request) {
43+
if (request == null) {
44+
return span;
45+
}
46+
String serviceName = request.getTargetServiceUniqueName();
47+
String methodName = request.getMethodName();
48+
span.setTag(Tags.RPC_SERVICE, serviceName);
49+
if (serviceName != null && methodName != null) {
50+
span.setResourceName(serviceName + "/" + methodName);
51+
} else if (methodName != null) {
52+
span.setResourceName(methodName);
53+
}
54+
return span;
55+
}
56+
57+
public AgentSpan onResponse(AgentSpan span, SofaResponse response) {
58+
if (response != null && response.isError()) {
59+
span.setError(true);
60+
span.setTag("error.message", response.getErrorMsg());
61+
}
62+
return span;
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package datadog.trace.instrumentation.sofarpc;
2+
3+
import com.alipay.sofa.rpc.core.request.SofaRequest;
4+
import datadog.trace.bootstrap.instrumentation.api.AgentPropagation;
5+
import java.util.Map;
6+
7+
public final class SofaRpcExtractAdapter implements AgentPropagation.ContextVisitor<SofaRequest> {
8+
9+
public static final SofaRpcExtractAdapter GETTER = new SofaRpcExtractAdapter();
10+
11+
@Override
12+
public void forEachKey(SofaRequest carrier, AgentPropagation.KeyClassifier classifier) {
13+
Map<String, Object> props = carrier.getRequestProps();
14+
if (props == null || props.isEmpty()) {
15+
return;
16+
}
17+
for (Map.Entry<String, Object> entry : props.entrySet()) {
18+
if (entry.getValue() instanceof String) {
19+
if (!classifier.accept(entry.getKey(), (String) entry.getValue())) {
20+
return;
21+
}
22+
}
23+
}
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package datadog.trace.instrumentation.sofarpc;
2+
3+
import com.alipay.sofa.rpc.core.request.SofaRequest;
4+
import datadog.context.propagation.CarrierSetter;
5+
6+
public final class SofaRpcInjectAdapter implements CarrierSetter<SofaRequest> {
7+
8+
public static final SofaRpcInjectAdapter SETTER = new SofaRpcInjectAdapter();
9+
10+
@Override
11+
public void set(SofaRequest carrier, String key, String value) {
12+
carrier.addRequestProp(key, value);
13+
}
14+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package datadog.trace.instrumentation.sofarpc;
2+
3+
import static java.util.Arrays.asList;
4+
5+
import com.google.auto.service.AutoService;
6+
import datadog.trace.agent.tooling.Instrumenter;
7+
import datadog.trace.agent.tooling.InstrumenterModule;
8+
import java.util.List;
9+
10+
@AutoService(InstrumenterModule.class)
11+
public class SofaRpcModule extends InstrumenterModule.Tracing {
12+
13+
public SofaRpcModule() {
14+
super("sofarpc");
15+
}
16+
17+
@Override
18+
public String[] helperClassNames() {
19+
return new String[] {
20+
packageName + ".SofaRpcClientDecorator",
21+
packageName + ".SofaRpcServerDecorator",
22+
packageName + ".SofaRpcInjectAdapter",
23+
packageName + ".SofaRpcExtractAdapter",
24+
};
25+
}
26+
27+
@Override
28+
public List<Instrumenter> typeInstrumentations() {
29+
return asList(new AbstractClusterInstrumentation(), new ProviderProxyInvokerInstrumentation());
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package datadog.trace.instrumentation.sofarpc;
2+
3+
import com.alipay.sofa.rpc.core.request.SofaRequest;
4+
import com.alipay.sofa.rpc.core.response.SofaResponse;
5+
import datadog.trace.api.naming.SpanNaming;
6+
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
7+
import datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes;
8+
import datadog.trace.bootstrap.instrumentation.api.Tags;
9+
import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
10+
import datadog.trace.bootstrap.instrumentation.decorator.ServerDecorator;
11+
12+
public class SofaRpcServerDecorator extends ServerDecorator {
13+
14+
public static final CharSequence SOFA_RPC_SERVER =
15+
UTF8BytesString.create(
16+
SpanNaming.instance().namingSchema().server().operationForProtocol("sofarpc"));
17+
18+
private static final CharSequence COMPONENT_NAME = UTF8BytesString.create("sofarpc-server");
19+
20+
public static final SofaRpcServerDecorator DECORATE = new SofaRpcServerDecorator();
21+
22+
@Override
23+
protected String[] instrumentationNames() {
24+
return new String[] {"sofarpc"};
25+
}
26+
27+
@Override
28+
protected CharSequence component() {
29+
return COMPONENT_NAME;
30+
}
31+
32+
@Override
33+
protected CharSequence spanType() {
34+
return InternalSpanTypes.RPC;
35+
}
36+
37+
@Override
38+
public AgentSpan afterStart(AgentSpan span) {
39+
span.setMeasured(true);
40+
return super.afterStart(span);
41+
}
42+
43+
public AgentSpan onRequest(AgentSpan span, SofaRequest request) {
44+
if (request == null) {
45+
return span;
46+
}
47+
String serviceName = request.getTargetServiceUniqueName();
48+
String methodName = request.getMethodName();
49+
span.setTag(Tags.RPC_SERVICE, serviceName);
50+
if (serviceName != null && methodName != null) {
51+
span.setResourceName(serviceName + "/" + methodName);
52+
} else if (methodName != null) {
53+
span.setResourceName(methodName);
54+
}
55+
return span;
56+
}
57+
58+
public AgentSpan onResponse(AgentSpan span, SofaResponse response) {
59+
if (response != null && response.isError()) {
60+
span.setError(true);
61+
span.setTag("error.message", response.getErrorMsg());
62+
}
63+
return span;
64+
}
65+
}

0 commit comments

Comments
 (0)