Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Release Notes.
* Support Tomcat thread pool metric collect.
* Remove plugin for ServiceComb Java Chassis 0.x
* Add Guava EventBus plugin.
* Fix Dubbo 3.x plugin's tracing problem.
* Support Druid Connection pool metrics collecting.
* Support HikariCP Connection pool metrics collecting.
* Support Dbcp2 Connection pool metrics collecting.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,9 @@
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;

public class DubboInstrumentationBase extends ClassInstanceMethodsEnhancePluginDefine {
public class DubboInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {

public static final String PROVIDER_ENHANCE_CLASS = "org.apache.dubbo.monitor.support.MonitorFilter";

public static final String CONSUMER_ENHANCE_CLASS = "org.apache.dubbo.monitor.support.MonitorClusterFilter";
public static final String ENHANCE_CLASS = "org.apache.dubbo.monitor.support.MonitorFilter";

public static final String INTERCEPT_POINT_METHOD = "invoke";

Expand All @@ -49,15 +47,9 @@ public class DubboInstrumentationBase extends ClassInstanceMethodsEnhancePluginD

public static final String CONTEXT_ATTACHMENT_TYPE_NAME = "org.apache.dubbo.rpc.RpcContextAttachment";

private final String enhanceClassName;

public DubboInstrumentationBase(String enhanceClassName) {
this.enhanceClassName = enhanceClassName;
}

@Override
protected ClassMatch enhanceClass() {
return NameMatch.byName(enhanceClassName);
return NameMatch.byName(ENHANCE_CLASS);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@

package org.apache.skywalking.apm.plugin.asf.dubbo3;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcContextAttachment;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcServiceContext;
import org.apache.skywalking.apm.agent.core.context.CarrierItem;
import org.apache.skywalking.apm.agent.core.context.ContextCarrier;
import org.apache.skywalking.apm.agent.core.context.ContextManager;
Expand Down Expand Up @@ -65,15 +63,7 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr
Invoker invoker = (Invoker) allArguments[0];
Invocation invocation = (Invocation) allArguments[1];

RpcServiceContext serviceContext = RpcContext.getServiceContext();
boolean isConsumer;
URL url = serviceContext.getUrl();
if (url == null) {
url = serviceContext.getConsumerUrl();
isConsumer = url != null;
} else {
isConsumer = serviceContext.isConsumerSide();
}
boolean isConsumer = isConsumer(invocation);
Copy link
Copy Markdown
Member

@AlbumenJ AlbumenJ Mar 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wu-sheng I'm a little confused about this. Why not judge whether it is the consumer side directly from EnhancedInstance objInst. If objInst is a MonitorFilter object, it should be on the provider side. If objInst is a MonitorClusterFilter object, it should be on the provider side.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We just enhanced MonitorFilter class finally, as MonitorClusterFilter extends MonitorFilter and enhance both of them will cause problem. We have talked above.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AlbumenJ Agree with @honganan , as a byte code manipulator, once we change the parent class, and the sub classes don't override the changed method, this enhancement actually changes the behavior of all classes on this hierarchy tree.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wu-sheng got it!


RpcContextAttachment attachment = isConsumer ? RpcContext.getClientAttachment() : RpcContext.getServerAttachment();
URL requestURL = invoker.getUrl();
Expand Down Expand Up @@ -125,12 +115,8 @@ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allAr
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
Object ret) throws Throwable {
Result result = (Result) ret;
try {
if (result != null && result.getException() != null) {
dealException(result.getException());
}
} catch (RpcException e) {
dealException(e);
if (result != null && result.getException() != null) {
dealException(result.getException());
}

ContextManager.stopSpan();
Expand All @@ -151,6 +137,19 @@ private void dealException(Throwable throwable) {
span.log(throwable);
}

/**
* To judge if current is in provider side.
*/
private static boolean isConsumer(Invocation invocation) {
Invoker<?> invoker = invocation.getInvoker();
// As RpcServiceContext may not been reset when it's role switched from provider
// to consumer in the same thread, but RpcInvocation is always correctly bounded
// to the current request or serve request, https://github.com/apache/skywalking-java/pull/110
return invoker.getUrl()
.getParameter("side", "provider")
.equals("consumer");
}

/**
* Format operation name. e.g. org.apache.skywalking.apm.plugin.test.Test.test(String)
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.

dubbo-3.x=org.apache.skywalking.apm.plugin.asf.dubbo3.DubboProviderInstrumentation
dubbo-3.x=org.apache.skywalking.apm.plugin.asf.dubbo3.DubboConsumerInstrumentation
dubbo-3.x=org.apache.skywalking.apm.plugin.asf.dubbo3.DubboInstrumentation
Original file line number Diff line number Diff line change
Expand Up @@ -87,39 +87,52 @@ public class DubboInterceptorTest {
@Mock
private RpcContextAttachment contextAttachment;
@Mock
private Invoker invoker;
private Invoker consumerInvoker;
@Mock
private Invocation invocation;
private Invoker providerInvoker;
@Mock
private Invocation consumerInvocation;
@Mock
private Invocation providerInvocation;
@Mock
private MethodInterceptResult methodInterceptResult;
@Mock
private Result result;

private Object[] allArguments;
private Object[] consumerArguments;
private Object[] providerArguments;
private Class[] argumentTypes;
private static final URL CONSUMER_URL = URL.valueOf("dubbo://127.0.0.1:20880/org.apache.skywalking.apm.test.TestDubboService?side=consumer");
private static final URL PROVIDER_URL = URL.valueOf("dubbo://127.0.0.1:20880/org.apache.skywalking.apm.test.TestDubboService?side=provider");

@Before
public void setUp() throws Exception {
dubboInterceptor = new DubboInterceptor();

PowerMockito.mockStatic(RpcContext.class);
URL url = URL.valueOf("dubbo://127.0.0.1:20880/org.apache.skywalking.apm.test.TestDubboService");
when(invoker.getUrl()).thenReturn(url);
when(invocation.getMethodName()).thenReturn("test");
when(invocation.getParameterTypes()).thenReturn(new Class[] {String.class});
when(invocation.getArguments()).thenReturn(new Object[] {"abc"});
when(RpcContext.getServiceContext()).thenReturn(serviceContext);
when(serviceContext.getUrl()).thenReturn(null);
when(serviceContext.getConsumerUrl()).thenReturn(url);
when(consumerInvoker.getUrl()).thenReturn(CONSUMER_URL);
when(consumerInvocation.getMethodName()).thenReturn("test");
when(consumerInvocation.getParameterTypes()).thenReturn(new Class[] {String.class});
when(consumerInvocation.getArguments()).thenReturn(new Object[] {"abc"});
when(consumerInvocation.getInvoker()).thenReturn(consumerInvoker);
when(RpcContext.getClientAttachment()).thenReturn(contextAttachment);
consumerArguments = new Object[] {
consumerInvoker,
consumerInvocation
};

allArguments = new Object[] {
invoker,
invocation
when(providerInvoker.getUrl()).thenReturn(PROVIDER_URL);
when(providerInvocation.getMethodName()).thenReturn("test");
when(providerInvocation.getParameterTypes()).thenReturn(new Class[] {String.class});
when(providerInvocation.getArguments()).thenReturn(new Object[] {"abc"});
when(providerInvocation.getInvoker()).thenReturn(providerInvoker);
providerArguments = new Object[] {
providerInvoker,
providerInvocation
};
argumentTypes = new Class[] {
invoker.getClass(),
invocation.getClass()
consumerInvoker.getClass(),
consumerInvocation.getClass()
};
Config.Agent.SERVICE_NAME = "DubboTestCases-APP";
}
Expand All @@ -140,8 +153,8 @@ public void testServiceOverrideFromPlugin() {

@Test
public void testConsumerWithAttachment() throws Throwable {
dubboInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentTypes, methodInterceptResult);
dubboInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentTypes, result);
dubboInterceptor.beforeMethod(enhancedInstance, null, consumerArguments, argumentTypes, methodInterceptResult);
dubboInterceptor.afterMethod(enhancedInstance, null, consumerArguments, argumentTypes, result);

assertThat(segmentStorage.getTraceSegments().size(), is(1));
TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0);
Expand All @@ -152,10 +165,10 @@ public void testConsumerWithAttachment() throws Throwable {

@Test
public void testConsumerWithException() throws Throwable {
dubboInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentTypes, methodInterceptResult);
dubboInterceptor.beforeMethod(enhancedInstance, null, consumerArguments, argumentTypes, methodInterceptResult);
dubboInterceptor.handleMethodException(
enhancedInstance, null, allArguments, argumentTypes, new RuntimeException());
dubboInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentTypes, result);
enhancedInstance, null, consumerArguments, argumentTypes, new RuntimeException());
dubboInterceptor.afterMethod(enhancedInstance, null, consumerArguments, argumentTypes, result);
assertThat(segmentStorage.getTraceSegments().size(), is(1));
TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0);
assertConsumerTraceSegmentInErrorCase(traceSegment);
Expand All @@ -165,8 +178,8 @@ public void testConsumerWithException() throws Throwable {
public void testConsumerWithResultHasException() throws Throwable {
when(result.getException()).thenReturn(new RuntimeException());

dubboInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentTypes, methodInterceptResult);
dubboInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentTypes, result);
dubboInterceptor.beforeMethod(enhancedInstance, null, consumerArguments, argumentTypes, methodInterceptResult);
dubboInterceptor.afterMethod(enhancedInstance, null, consumerArguments, argumentTypes, result);

assertThat(segmentStorage.getTraceSegments().size(), is(1));
TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0);
Expand All @@ -175,16 +188,14 @@ public void testConsumerWithResultHasException() throws Throwable {

@Test
public void testProviderWithAttachment() throws Throwable {
when(serviceContext.getUrl()).thenReturn(
URL.valueOf("dubbo://127.0.0.1:20880/org.apache.skywalking.apm.test.TestDubboService"));
when(serviceContext.getConsumerUrl()).thenReturn(null);
when(providerInvoker.getUrl()).thenReturn(PROVIDER_URL);
when(RpcContext.getServerAttachment()).thenReturn(contextAttachment);
when(contextAttachment.getAttachment(
SW8CarrierItem.HEADER_NAME)).thenReturn(
"1-My40LjU=-MS4yLjM=-3-c2VydmljZQ==-aW5zdGFuY2U=-L2FwcA==-MTI3LjAuMC4xOjgwODA=");

dubboInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentTypes, methodInterceptResult);
dubboInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentTypes, result);
dubboInterceptor.beforeMethod(enhancedInstance, null, providerArguments, argumentTypes, methodInterceptResult);
dubboInterceptor.afterMethod(enhancedInstance, null, providerArguments, argumentTypes, result);
assertProvider();
}

Expand Down
144 changes: 94 additions & 50 deletions test/plugin/scenarios/dubbo-3.x-scenario/config/expectedData.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,53 +18,97 @@ segmentItems:
- serviceName: dubbo-3.x-scenario
segmentSize: ge 3
segments:
- segmentId: not null
spans:
- operationName: org.apache.skywalking.apm.testcase.dubbo3.services.GreetService.doBusiness(String)
parentSpanId: -1
spanId: 0
spanLayer: RPCFramework
startTime: nq 0
endTime: nq 0
componentId: 3
isError: false
spanType: Entry
peer: not null
tags:
- {key: url, value: not null}
- {key: arguments, value: helloWorld}
refs:
- {parentEndpoint: GET:/dubbo-3.x-scenario/case/dubbo, networkAddress: not null,
refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null,
parentServiceInstance: not null, parentService: dubbo-3.x-scenario, traceId: not null}
skipAnalysis: 'false'
- segmentId: not null
spans:
- operationName: org.apache.skywalking.apm.testcase.dubbo3.services.GreetService.doBusiness(String)
parentSpanId: 0
spanId: 1
spanLayer: RPCFramework
startTime: nq 0
endTime: nq 0
componentId: 3
isError: false
spanType: Exit
peer: not null
tags:
- {key: url, value: not null}
- {key: arguments, value: helloWorld}
skipAnalysis: 'false'
- operationName: GET:/dubbo-3.x-scenario/case/dubbo
parentSpanId: -1
spanId: 0
spanLayer: Http
startTime: nq 0
endTime: nq 0
componentId: 1
isError: false
spanType: Entry
peer: ''
tags:
- {key: url, value: 'http://localhost:8080/dubbo-3.x-scenario/case/dubbo'}
- {key: http.method, value: GET}
skipAnalysis: 'false'
- segmentId: not null
spans:
- operationName: org.apache.skywalking.apm.testcase.dubbo3.services.ExceptionService.exceptionCall()
parentSpanId: -1
spanId: 0
spanLayer: RPCFramework
startTime: nq 0
endTime: nq 0
componentId: 3
isError: true
spanType: Entry
peer: not null
tags:
- { key: url, value: not null }
logs:
- logEvent:
- { key: event, value: error }
- { key: error.kind, value: java.lang.RuntimeException }
- { key: message, value: test exception! }
- { key: "stack", value: "not null" }
refs:
- { parentEndpoint: org.apache.skywalking.apm.testcase.dubbo3.services.GreetService.doBusiness(String), networkAddress: not null,
refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not null,
parentService: dubbo-3.x-scenario, traceId: not null }
skipAnalysis: 'false'
- segmentId: not null
spans:
- operationName: org.apache.skywalking.apm.testcase.dubbo3.services.ExceptionService.exceptionCall()
parentSpanId: 0
spanId: 1
spanLayer: RPCFramework
startTime: nq 0
endTime: nq 0
componentId: 3
isError: true
spanType: Exit
peer: not null
tags:
- { key: url, value: not null }
logs:
- logEvent:
- { key: event, value: error }
- { key: error.kind, value: java.lang.RuntimeException }
- { key: message, value: test exception! }
- { key: "stack", value: "not null" }
skipAnalysis: 'false'
- operationName: org.apache.skywalking.apm.testcase.dubbo3.services.GreetService.doBusiness(String)
parentSpanId: -1
spanId: 0
spanLayer: RPCFramework
startTime: nq 0
endTime: nq 0
componentId: 3
isError: false
spanType: Entry
peer: not null
tags:
- {key: url, value: not null}
- {key: arguments, value: helloWorld}
refs:
- {parentEndpoint: GET:/dubbo-3.x-scenario/case/dubbo, networkAddress: not null,
refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null,
parentServiceInstance: not null, parentService: dubbo-3.x-scenario, traceId: not null}
skipAnalysis: 'false'
- segmentId: not null
spans:
- operationName: org.apache.skywalking.apm.testcase.dubbo3.services.GreetService.doBusiness(String)
parentSpanId: 0
spanId: 1
spanLayer: RPCFramework
startTime: nq 0
endTime: nq 0
componentId: 3
isError: false
spanType: Exit
peer: not null
tags:
- {key: url, value: not null}
- {key: arguments, value: helloWorld}
skipAnalysis: 'false'
- operationName: GET:/dubbo-3.x-scenario/case/dubbo
parentSpanId: -1
spanId: 0
spanLayer: Http
startTime: nq 0
endTime: nq 0
componentId: 1
isError: false
spanType: Entry
peer: ''
tags:
- {key: url, value: 'http://localhost:8080/dubbo-3.x-scenario/case/dubbo'}
- {key: http.method, value: GET}
skipAnalysis: 'false'
Loading