From 9101f691fba659a2616918588cae65da25e04690 Mon Sep 17 00:00:00 2001 From: xu1009 <2933250475@qq.com> Date: Mon, 25 Oct 2021 22:40:37 +0800 Subject: [PATCH] Add okhttp2.x plugin (#49) Co-authored-by: litexu --- .github/workflows/plugins-test.2.yaml | 1 + CHANGES.md | 2 + .../apm-sdk-plugin/okhttp-2.x-plugin/pom.xml | 53 ++++++ .../okhttp/v2/AsyncCallInterceptor.java | 70 +++++++ .../apm/plugin/okhttp/v2/CallInterceptor.java | 97 ++++++++++ .../plugin/okhttp/v2/EnqueueInterceptor.java | 73 ++++++++ .../okhttp/v2/OnResponseInterceptor.java | 51 +++++ .../plugin/okhttp/v2/RealCallInterceptor.java | 128 +++++++++++++ .../v2/define/AsyncCallInstrumentation.java | 83 +++++++++ .../v2/define/CallbackInstrumentation.java | 80 ++++++++ .../v2/define/RealCallInstrumentation.java | 120 ++++++++++++ .../src/main/resources/skywalking-plugin.def | 19 ++ .../okhttp/v2/RealCallInterceptorTest.java | 175 ++++++++++++++++++ .../v3/define/RealCallInstrumentation.java | 100 +++++----- .../v4/define/RealCallInstrumentation.java | 101 +++++----- .../okhttp/common/AsyncCallInterceptor.java | 53 +----- .../plugin/okhttp/common/CallInterceptor.java | 97 ++++++++++ .../okhttp/common/OnFailureInterceptor.java | 3 +- .../okhttp/common/OnResponseInterceptor.java | 8 +- .../okhttp/common/RealCallInterceptor.java | 18 +- apm-sniffer/apm-sdk-plugin/pom.xml | 1 + .../service-agent/java-agent/Plugin-list.md | 1 + .../java-agent/Supported-list.md | 2 +- pom.xml | 1 + .../okhttp-scenario/config/expectedData.yaml | 49 ++++- .../scenarios/okhttp2-scenario/bin/startup.sh | 21 +++ .../okhttp2-scenario/config/expectedData.yaml | 130 +++++++++++++ .../okhttp2-scenario/configuration.yml | 20 ++ .../plugin/scenarios/okhttp2-scenario/pom.xml | 124 +++++++++++++ .../src/main/assembly/assembly.xml | 41 ++++ .../apm/testcase/okhttp2/Application.java | 34 ++++ .../okhttp2/controller/CaseController.java | 83 +++++++++ .../src/main/resources/application.yaml | 23 +++ .../src/main/resources/log4j2.xml | 30 +++ .../okhttp2-scenario/support-version.list | 22 +++ 35 files changed, 1754 insertions(+), 160 deletions(-) create mode 100644 apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/pom.xml create mode 100644 apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/AsyncCallInterceptor.java create mode 100644 apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/CallInterceptor.java create mode 100644 apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/EnqueueInterceptor.java create mode 100644 apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/OnResponseInterceptor.java create mode 100644 apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/RealCallInterceptor.java create mode 100644 apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/define/AsyncCallInstrumentation.java create mode 100644 apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/define/CallbackInstrumentation.java create mode 100644 apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/define/RealCallInstrumentation.java create mode 100644 apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/resources/skywalking-plugin.def create mode 100644 apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/okhttp/v2/RealCallInterceptorTest.java create mode 100644 apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/CallInterceptor.java create mode 100644 test/plugin/scenarios/okhttp2-scenario/bin/startup.sh create mode 100644 test/plugin/scenarios/okhttp2-scenario/config/expectedData.yaml create mode 100644 test/plugin/scenarios/okhttp2-scenario/configuration.yml create mode 100644 test/plugin/scenarios/okhttp2-scenario/pom.xml create mode 100644 test/plugin/scenarios/okhttp2-scenario/src/main/assembly/assembly.xml create mode 100644 test/plugin/scenarios/okhttp2-scenario/src/main/java/test/apache/skywalking/apm/testcase/okhttp2/Application.java create mode 100644 test/plugin/scenarios/okhttp2-scenario/src/main/java/test/apache/skywalking/apm/testcase/okhttp2/controller/CaseController.java create mode 100644 test/plugin/scenarios/okhttp2-scenario/src/main/resources/application.yaml create mode 100644 test/plugin/scenarios/okhttp2-scenario/src/main/resources/log4j2.xml create mode 100644 test/plugin/scenarios/okhttp2-scenario/support-version.list diff --git a/.github/workflows/plugins-test.2.yaml b/.github/workflows/plugins-test.2.yaml index 2ded7f693..44489ee91 100644 --- a/.github/workflows/plugins-test.2.yaml +++ b/.github/workflows/plugins-test.2.yaml @@ -78,6 +78,7 @@ jobs: - struts2.3-scenario - struts2.5-scenario - cxf-scenario + - okhttp2-scenario steps: - uses: actions/checkout@v2 with: diff --git a/CHANGES.md b/CHANGES.md index 6218db077..46455b027 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -37,6 +37,8 @@ Release Notes. * Fix version compatibility for JsonRPC4J plugin. * Add plugin to support Apache Kylin-jdbc 2.6.x 3.x 4.x * Fix instrumentation v2 API doesn't work for constructor instrumentation. +* Add plugin to support okhttp 2.x +* Optimize okhttp 3.x 4.x plugin to get span time cost precisely #### Documentation diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/pom.xml new file mode 100644 index 000000000..be93a8ada --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/pom.xml @@ -0,0 +1,53 @@ + + + + + + apm-sdk-plugin + org.apache.skywalking + 8.8.0-SNAPSHOT + + 4.0.0 + + okhttp-2.x-plugin + okhttp-2.x-plugin + jar + + + 2.7.5 + + + + + com.squareup.okhttp + okhttp + ${okhttp.version} + provided + + + org.apache.skywalking + apm-okhttp-common + ${project.version} + provided + + + + \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/AsyncCallInterceptor.java b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/AsyncCallInterceptor.java new file mode 100644 index 000000000..2163ec1e9 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/AsyncCallInterceptor.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.okhttp.v2; + +import java.lang.reflect.Method; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.plugin.okhttp.common.EnhanceRequiredInfo; + +/** + * {@link AsyncCallInterceptor} get the `EnhanceRequiredInfo` instance from `SkyWalkingDynamicField` and then put it + * into `AsyncCall` instance when the `AsyncCall` constructor called. + *

+ * {@link AsyncCallInterceptor} also create an exit span by using the `EnhanceRequiredInfo` when the `execute` method + * called. + */ +public class AsyncCallInterceptor implements InstanceConstructorInterceptor, InstanceMethodsAroundInterceptor { + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) { + /** + * The first argument of constructor is not the `real` parameter when the enhance class is an inner class. This + * is the JDK compiler mechanism. + */ + EnhancedInstance realCallInstance = (EnhancedInstance) allArguments[1]; + Object enhanceRequireInfo = realCallInstance.getSkyWalkingDynamicField(); + + objInst.setSkyWalkingDynamicField(enhanceRequireInfo); + } + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + EnhanceRequiredInfo enhanceRequiredInfo = (EnhanceRequiredInfo) objInst.getSkyWalkingDynamicField(); + ContextManager.createLocalSpan("Async/execute"); + ContextManager.continued(enhanceRequiredInfo.getContextSnapshot()); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/CallInterceptor.java b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/CallInterceptor.java new file mode 100644 index 000000000..adf5965ee --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/CallInterceptor.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.okhttp.v2; + +import com.squareup.okhttp.Headers; +import com.squareup.okhttp.HttpUrl; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.Response; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +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; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; + +public class CallInterceptor implements InstanceMethodsAroundInterceptor { + + private static Field FIELD_HEADERS_OF_REQUEST; + + static { + try { + final Field field = Request.class.getDeclaredField("headers"); + field.setAccessible(true); + FIELD_HEADERS_OF_REQUEST = field; + } catch (Exception ignore) { + FIELD_HEADERS_OF_REQUEST = null; + } + } + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + Request request = (Request) objInst.getSkyWalkingDynamicField(); + HttpUrl requestUrl = request.httpUrl(); + AbstractSpan span = ContextManager.createExitSpan(requestUrl.uri() + .getPath(), requestUrl.host() + ":" + requestUrl.port()); + ContextCarrier contextCarrier = new ContextCarrier(); + ContextManager.inject(contextCarrier); + span.setComponent(ComponentsDefine.OKHTTP); + Tags.HTTP.METHOD.set(span, request.method()); + Tags.URL.set(span, requestUrl.uri().toString()); + SpanLayer.asHttp(span); + if (FIELD_HEADERS_OF_REQUEST != null) { + Headers.Builder headerBuilder = request.headers().newBuilder(); + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + headerBuilder.set(next.getHeadKey(), next.getHeadValue()); + } + FIELD_HEADERS_OF_REQUEST.set(request, headerBuilder.build()); + } + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + Response response = (Response) ret; + if (response != null) { + int statusCode = response.code(); + AbstractSpan span = ContextManager.activeSpan(); + if (statusCode >= 400) { + span.errorOccurred(); + Tags.HTTP_RESPONSE_STATUS_CODE.set(span, statusCode); + } + } + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/EnqueueInterceptor.java b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/EnqueueInterceptor.java new file mode 100644 index 000000000..a487a3d25 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/EnqueueInterceptor.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.okhttp.v2; + +import com.squareup.okhttp.Request; +import java.lang.reflect.Method; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.plugin.okhttp.common.EnhanceRequiredInfo; + +/** + * {@link EnqueueInterceptor} create a local span and the prefix of the span operation name is start with `Async` when + * the `enqueue` method called and also put the `ContextSnapshot` and `RealCall` instance into the + * `SkyWalkingDynamicField`. + */ +public class EnqueueInterceptor implements InstanceMethodsAroundInterceptor, InstanceConstructorInterceptor { + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + EnhancedInstance callbackInstance = (EnhancedInstance) allArguments[0]; + Request request = (Request) objInst.getSkyWalkingDynamicField(); + ContextManager.createLocalSpan("Async" + request.httpUrl().uri().getPath()); + + /** + * Here is the process about how to trace the async function. + * + * 1. Storage `Request` object into `RealCall` instance when the constructor of `RealCall` called. + * 2. Put the `RealCall` instance to `CallBack` instance + * 3. Get the `RealCall` instance from `CallBack` and then Put the `RealCall` into `AsyncCall` instance + * since the constructor of `RealCall` called. + * 5. Create the exit span by using the `RealCall` instance when `AsyncCall` method called. + */ + + callbackInstance.setSkyWalkingDynamicField(new EnhanceRequiredInfo(objInst, ContextManager.capture())); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().log(t); + } + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) { + objInst.setSkyWalkingDynamicField(allArguments[1]); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/OnResponseInterceptor.java b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/OnResponseInterceptor.java new file mode 100644 index 000000000..b4b540cb9 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/OnResponseInterceptor.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.okhttp.v2; + +import java.lang.reflect.Method; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +/** + * {@link OnResponseInterceptor} validate the response code if it is great equal than 400. if so. the transaction status + * chang to `error`, or do nothing. + */ +public class OnResponseInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + ContextManager.createLocalSpan("Callback/onResponse"); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/RealCallInterceptor.java b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/RealCallInterceptor.java new file mode 100644 index 000000000..2b9387e57 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/RealCallInterceptor.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.okhttp.v2; + +import com.squareup.okhttp.Headers; +import com.squareup.okhttp.HttpUrl; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.Response; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +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; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.logging.api.ILog; +import org.apache.skywalking.apm.agent.core.logging.api.LogManager; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; + +/** + * {@link RealCallInterceptor} intercept the synchronous http calls by the discovery of okhttp. + */ +public class RealCallInterceptor implements InstanceMethodsAroundInterceptor, InstanceConstructorInterceptor { + + private static Field FIELD_HEADERS_OF_REQUEST; + + private static final ILog LOGGER = LogManager.getLogger(RealCallInterceptor.class); + + static { + try { + final Field field = Request.class.getDeclaredField("headers"); + field.setAccessible(true); + FIELD_HEADERS_OF_REQUEST = field; + } catch (Exception ignore) { + FIELD_HEADERS_OF_REQUEST = null; + } + } + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) { + objInst.setSkyWalkingDynamicField(allArguments[1]); + } + + /** + * Get the {@link Request} from {@link EnhancedInstance}, then create {@link AbstractSpan} and set host, + * port, kind, component, url from {@link Request}. Through the reflection of the way, set the http header + * of context data into {@link Request#headers()}. + * + * @param result change this result, if you want to truncate the method. + */ + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + Request request = (Request) objInst.getSkyWalkingDynamicField(); + + ContextCarrier contextCarrier = new ContextCarrier(); + HttpUrl requestUrl = request.httpUrl(); + AbstractSpan span = ContextManager.createExitSpan(requestUrl.uri().getPath(), contextCarrier, + requestUrl.host() + ":" + requestUrl.port()); + ContextManager.inject(contextCarrier); + span.setComponent(ComponentsDefine.OKHTTP); + Tags.HTTP.METHOD.set(span, request.method()); + Tags.URL.set(span, requestUrl.uri().toString()); + SpanLayer.asHttp(span); + + if (FIELD_HEADERS_OF_REQUEST != null) { + Headers.Builder headerBuilder = request.headers().newBuilder(); + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + headerBuilder.set(next.getHeadKey(), next.getHeadValue()); + } + FIELD_HEADERS_OF_REQUEST.set(request, headerBuilder.build()); + } + } + + /** + * Get the status code from {@link Response}, when status code greater than 400, it means there was some errors in + * the server. Finish the {@link AbstractSpan}. + * + * @param ret the method's original return value. + */ + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + Response response = (Response) ret; + if (response != null) { + int statusCode = response.code(); + AbstractSpan span = ContextManager.activeSpan(); + if (statusCode >= 400) { + span.errorOccurred(); + Tags.HTTP_RESPONSE_STATUS_CODE.set(span, statusCode); + } + } + + ContextManager.stopSpan(); + + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + AbstractSpan abstractSpan = ContextManager.activeSpan(); + abstractSpan.log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/define/AsyncCallInstrumentation.java b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/define/AsyncCallInstrumentation.java new file mode 100644 index 000000000..3daa03d3d --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/define/AsyncCallInstrumentation.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.okhttp.v2.define; + +import static net.bytebuddy.matcher.ElementMatchers.any; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +public class AsyncCallInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + + /** + * Intercept class. + */ + private static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.okhttp.v2.AsyncCallInterceptor"; + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPT_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("execute"); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return byName("com.squareup.okhttp.Call$AsyncCall"); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/define/CallbackInstrumentation.java b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/define/CallbackInstrumentation.java new file mode 100644 index 000000000..dd65eb037 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/define/CallbackInstrumentation.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.okhttp.v2.define; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch.byHierarchyMatch; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +public class CallbackInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("onFailure"); + } + + @Override + public String getMethodsInterceptor() { + return "org.apache.skywalking.apm.plugin.okhttp.common.OnFailureInterceptor"; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("onResponse"); + } + + @Override + public String getMethodsInterceptor() { + return "org.apache.skywalking.apm.plugin.okhttp.v2.OnResponseInterceptor"; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return byHierarchyMatch(new String[]{"com.squareup.okhttp.Callback"}); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/define/RealCallInstrumentation.java b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/define/RealCallInstrumentation.java new file mode 100644 index 000000000..aee7fcf90 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v2/define/RealCallInstrumentation.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.okhttp.v2.define; + +import static net.bytebuddy.matcher.ElementMatchers.any; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; + +public class RealCallInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + /** + * Enhance class. + */ + private static final String ENHANCE_CLASS = "com.squareup.okhttp.Call"; + + /** + * Intercept class. + */ + private static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.okhttp.v2.RealCallInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPT_CLASS; + } + } + }; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("execute"); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("getResponseWithInterceptorChain"); + } + + @Override + public String getMethodsInterceptor() { + return "org.apache.skywalking.apm.plugin.okhttp.v2.CallInterceptor"; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("enqueue").and(takesArguments(1)); + } + + @Override + public String getMethodsInterceptor() { + return "org.apache.skywalking.apm.plugin.okhttp.v2.EnqueueInterceptor"; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 000000000..d3772e81e --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +okhttp-2.x=org.apache.skywalking.apm.plugin.okhttp.v2.define.RealCallInstrumentation +okhttp-2.x=org.apache.skywalking.apm.plugin.okhttp.v2.define.CallbackInstrumentation +okhttp-2.x=org.apache.skywalking.apm.plugin.okhttp.v2.define.AsyncCallInstrumentation diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/okhttp/v2/RealCallInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/okhttp/v2/RealCallInterceptorTest.java new file mode 100644 index 000000000..692753f54 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/okhttp-2.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/okhttp/v2/RealCallInterceptorTest.java @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.okhttp.v2; + +import static org.apache.skywalking.apm.agent.test.tools.SpanAssert.assertComponent; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.Response; +import java.util.List; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.test.helper.SegmentHelper; +import org.apache.skywalking.apm.agent.test.helper.SpanHelper; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.SpanAssert; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.modules.junit4.PowerMockRunnerDelegate; + +@RunWith(PowerMockRunner.class) +@PowerMockRunnerDelegate(TracingSegmentRunner.class) +@PrepareForTest({Response.class}) +public class RealCallInterceptorTest { + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + + private RealCallInterceptor realCallInterceptor; + + @Mock + private OkHttpClient client; + + private Request request; + + private Object[] allArguments; + private Class[] argumentTypes; + + private EnhancedInstance enhancedInstance = new EnhancedInstance() { + + private Object object; + + @Override + public Object getSkyWalkingDynamicField() { + return object; + } + + @Override + public void setSkyWalkingDynamicField(Object value) { + this.object = value; + } + }; + + @Before + public void setUp() throws Exception { + request = new Request.Builder().url("http://skywalking.org").build(); + allArguments = new Object[]{ + client, + request, + false + }; + argumentTypes = new Class[]{ + client.getClass(), + request.getClass(), + Boolean.class + }; + realCallInterceptor = new RealCallInterceptor(); + } + + @Test + public void testOnConstruct() { + realCallInterceptor.onConstruct(enhancedInstance, allArguments); + assertThat(enhancedInstance.getSkyWalkingDynamicField(), is(allArguments[1])); + } + + @Test + public void testMethodsAround() throws Throwable { + realCallInterceptor.onConstruct(enhancedInstance, allArguments); + realCallInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentTypes, null); + + Response response = mock(Response.class); + when(response.code()).thenReturn(200); + realCallInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentTypes, response); + + assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + + assertSpan(spans.get(0)); + SpanAssert.assertOccurException(spans.get(0), false); + } + + @Test + public void testMethodsAroundError() throws Throwable { + realCallInterceptor.onConstruct(enhancedInstance, allArguments); + realCallInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentTypes, null); + + Response response = mock(Response.class); + when(response.code()).thenReturn(404); + realCallInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentTypes, response); + + assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + + assertSpan(spans.get(0)); + SpanAssert.assertOccurException(spans.get(0), true); + } + + private void assertSpan(AbstractTracingSpan span) { + assertComponent(span, ComponentsDefine.OKHTTP); + SpanAssert.assertLayer(span, SpanLayer.HTTP); + SpanAssert.assertTag(span, 0, "GET"); + SpanAssert.assertTag(span, 1, "http://skywalking.org/"); + assertThat(span.isExit(), is(true)); + assertThat(span.getOperationName(), is("/")); + } + + @Test + public void testException() throws Throwable { + realCallInterceptor.onConstruct(enhancedInstance, allArguments); + realCallInterceptor.beforeMethod(enhancedInstance, null, allArguments, argumentTypes, null); + + realCallInterceptor.handleMethodException(enhancedInstance, null, allArguments, argumentTypes, + new NullPointerException("testException")); + + Response response = mock(Response.class); + when(response.code()).thenReturn(200); + realCallInterceptor.afterMethod(enhancedInstance, null, allArguments, argumentTypes, response); + + assertThat(segmentStorage.getTraceSegments().size(), is(1)); + TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0); + List spans = SegmentHelper.getSpans(traceSegment); + + assertSpan(spans.get(0)); + SpanAssert.assertOccurException(spans.get(0), true); + SpanAssert.assertLogSize(spans.get(0), 1); + SpanAssert.assertException(SpanHelper.getLogs(spans.get(0)) + .get(0), NullPointerException.class, "testException"); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v3/define/RealCallInstrumentation.java b/apm-sniffer/apm-sdk-plugin/okhttp-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v3/define/RealCallInstrumentation.java index 0658359b3..97465453f 100644 --- a/apm-sniffer/apm-sdk-plugin/okhttp-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v3/define/RealCallInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/okhttp-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v3/define/RealCallInstrumentation.java @@ -18,6 +18,10 @@ package org.apache.skywalking.apm.plugin.okhttp.v3.define; +import static net.bytebuddy.matcher.ElementMatchers.any; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.matcher.ElementMatcher; import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; @@ -25,10 +29,6 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsIn import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; -import static net.bytebuddy.matcher.ElementMatchers.any; -import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.takesArguments; - public class RealCallInstrumentation extends AbstractOkhttpInstrumentation { /** @@ -48,56 +48,72 @@ public class RealCallInstrumentation extends AbstractOkhttpInstrumentation { @Override public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { - return new ConstructorInterceptPoint[] { - new ConstructorInterceptPoint() { - @Override - public ElementMatcher getConstructorMatcher() { - return any(); - } + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } - @Override - public String getConstructorInterceptor() { - return INTERCEPT_CLASS; + @Override + public String getConstructorInterceptor() { + return INTERCEPT_CLASS; + } } - } }; } @Override public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { - return new InstanceMethodsInterceptPoint[] { - new InstanceMethodsInterceptPoint() { - @Override - public ElementMatcher getMethodsMatcher() { - return named("execute"); - } + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("execute"); + } - @Override - public String getMethodsInterceptor() { - return INTERCEPT_CLASS; - } + @Override + public String getMethodsInterceptor() { + return INTERCEPT_CLASS; + } - @Override - public boolean isOverrideArgs() { - return false; - } - }, - new InstanceMethodsInterceptPoint() { - @Override - public ElementMatcher getMethodsMatcher() { - return named("enqueue").and(takesArguments(1)); - } + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("getResponseWithInterceptorChain"); + } - @Override - public String getMethodsInterceptor() { - return "org.apache.skywalking.apm.plugin.okhttp.common.EnqueueInterceptor"; - } + @Override + public String getMethodsInterceptor() { + return "org.apache.skywalking.apm.plugin.okhttp.common.CallInterceptor"; + } - @Override - public boolean isOverrideArgs() { - return false; + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("enqueue").and(takesArguments(1)); + } + + @Override + public String getMethodsInterceptor() { + return "org.apache.skywalking.apm.plugin.okhttp.common.EnqueueInterceptor"; + } + + @Override + public boolean isOverrideArgs() { + return false; + } } - } }; } } diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v4/define/RealCallInstrumentation.java b/apm-sniffer/apm-sdk-plugin/okhttp-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v4/define/RealCallInstrumentation.java index ea97358b1..1f7edc8b7 100644 --- a/apm-sniffer/apm-sdk-plugin/okhttp-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v4/define/RealCallInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/okhttp-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/okhttp/v4/define/RealCallInstrumentation.java @@ -18,6 +18,11 @@ package org.apache.skywalking.apm.plugin.okhttp.v4.define; +import static net.bytebuddy.matcher.ElementMatchers.any; +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.matcher.ElementMatcher; import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; @@ -25,10 +30,6 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsIn import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; import org.apache.skywalking.apm.agent.core.plugin.match.NameMatch; -import static net.bytebuddy.matcher.ElementMatchers.any; -import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.takesArguments; - public class RealCallInstrumentation extends AbstractOkhttpInstrumentation { /** @@ -48,56 +49,72 @@ public class RealCallInstrumentation extends AbstractOkhttpInstrumentation { @Override public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { - return new ConstructorInterceptPoint[] { - new ConstructorInterceptPoint() { - @Override - public ElementMatcher getConstructorMatcher() { - return any(); - } + return new ConstructorInterceptPoint[]{ + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } - @Override - public String getConstructorInterceptor() { - return INTERCEPT_CLASS; + @Override + public String getConstructorInterceptor() { + return INTERCEPT_CLASS; + } } - } }; } @Override public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { - return new InstanceMethodsInterceptPoint[] { - new InstanceMethodsInterceptPoint() { - @Override - public ElementMatcher getMethodsMatcher() { - return named("execute"); - } + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("execute"); + } - @Override - public String getMethodsInterceptor() { - return INTERCEPT_CLASS; - } + @Override + public String getMethodsInterceptor() { + return INTERCEPT_CLASS; + } - @Override - public boolean isOverrideArgs() { - return false; - } - }, - new InstanceMethodsInterceptPoint() { - @Override - public ElementMatcher getMethodsMatcher() { - return named("enqueue").and(takesArguments(1)); - } + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return nameStartsWith("getResponseWithInterceptorChain"); + } - @Override - public String getMethodsInterceptor() { - return "org.apache.skywalking.apm.plugin.okhttp.common.EnqueueInterceptor"; - } + @Override + public String getMethodsInterceptor() { + return "org.apache.skywalking.apm.plugin.okhttp.common.CallInterceptor"; + } - @Override - public boolean isOverrideArgs() { - return false; + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("enqueue").and(takesArguments(1)); + } + + @Override + public String getMethodsInterceptor() { + return "org.apache.skywalking.apm.plugin.okhttp.common.EnqueueInterceptor"; + } + + @Override + public boolean isOverrideArgs() { + return false; + } } - } }; } } diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/AsyncCallInterceptor.java b/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/AsyncCallInterceptor.java index 54364f469..20e966e81 100644 --- a/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/AsyncCallInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/AsyncCallInterceptor.java @@ -18,23 +18,12 @@ package org.apache.skywalking.apm.plugin.okhttp.common; -import okhttp3.Headers; -import okhttp3.HttpUrl; -import okhttp3.Request; -import org.apache.skywalking.apm.agent.core.context.CarrierItem; -import org.apache.skywalking.apm.agent.core.context.ContextCarrier; +import java.lang.reflect.Method; import org.apache.skywalking.apm.agent.core.context.ContextManager; -import org.apache.skywalking.apm.agent.core.context.tag.Tags; -import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; -import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; -import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; /** * {@link AsyncCallInterceptor} get the `EnhanceRequiredInfo` instance from `SkyWalkingDynamicField` and then put it @@ -45,18 +34,6 @@ import java.lang.reflect.Method; */ public class AsyncCallInterceptor implements InstanceConstructorInterceptor, InstanceMethodsAroundInterceptor { - private static Field FIELD_HEADERS_OF_REQUEST; - - static { - try { - final Field field = Request.class.getDeclaredField("headers"); - field.setAccessible(true); - FIELD_HEADERS_OF_REQUEST = field; - } catch (Exception ignore) { - FIELD_HEADERS_OF_REQUEST = null; - } - } - @Override public void onConstruct(EnhancedInstance objInst, Object[] allArguments) { /** @@ -71,43 +48,23 @@ public class AsyncCallInterceptor implements InstanceConstructorInterceptor, Ins @Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, - MethodInterceptResult result) throws Throwable { + MethodInterceptResult result) throws Throwable { EnhanceRequiredInfo enhanceRequiredInfo = (EnhanceRequiredInfo) objInst.getSkyWalkingDynamicField(); - Request request = (Request) enhanceRequiredInfo.getRealCallEnhance().getSkyWalkingDynamicField(); - - HttpUrl requestUrl = request.url(); - AbstractSpan span = ContextManager.createExitSpan(requestUrl.uri() - .getPath(), requestUrl.host() + ":" + requestUrl.port()); + ContextManager.createLocalSpan("Async/execute"); ContextManager.continued(enhanceRequiredInfo.getContextSnapshot()); - ContextCarrier contextCarrier = new ContextCarrier(); - ContextManager.inject(contextCarrier); - span.setComponent(ComponentsDefine.OKHTTP); - Tags.HTTP.METHOD.set(span, request.method()); - Tags.URL.set(span, requestUrl.uri().toString()); - SpanLayer.asHttp(span); - - if (FIELD_HEADERS_OF_REQUEST != null) { - Headers.Builder headerBuilder = request.headers().newBuilder(); - CarrierItem next = contextCarrier.items(); - while (next.hasNext()) { - next = next.next(); - headerBuilder.set(next.getHeadKey(), next.getHeadValue()); - } - FIELD_HEADERS_OF_REQUEST.set(request, headerBuilder.build()); - } } @Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, - Object ret) throws Throwable { + Object ret) throws Throwable { ContextManager.stopSpan(); return ret; } @Override public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, - Class[] argumentsTypes, Throwable t) { + Class[] argumentsTypes, Throwable t) { ContextManager.activeSpan().log(t); } } diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/CallInterceptor.java b/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/CallInterceptor.java new file mode 100644 index 000000000..90b15a405 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/CallInterceptor.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.skywalking.apm.plugin.okhttp.common; + +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.Request; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import okhttp3.Response; +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; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; + +public class CallInterceptor implements InstanceMethodsAroundInterceptor { + + private static Field FIELD_HEADERS_OF_REQUEST; + + static { + try { + final Field field = Request.class.getDeclaredField("headers"); + field.setAccessible(true); + FIELD_HEADERS_OF_REQUEST = field; + } catch (Exception ignore) { + FIELD_HEADERS_OF_REQUEST = null; + } + } + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + Request request = (Request) objInst.getSkyWalkingDynamicField(); + HttpUrl requestUrl = request.url(); + AbstractSpan span = ContextManager.createExitSpan(requestUrl.uri() + .getPath(), requestUrl.host() + ":" + requestUrl.port()); + ContextCarrier contextCarrier = new ContextCarrier(); + ContextManager.inject(contextCarrier); + span.setComponent(ComponentsDefine.OKHTTP); + Tags.HTTP.METHOD.set(span, request.method()); + Tags.URL.set(span, requestUrl.uri().toString()); + SpanLayer.asHttp(span); + if (FIELD_HEADERS_OF_REQUEST != null) { + Headers.Builder headerBuilder = request.headers().newBuilder(); + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + headerBuilder.set(next.getHeadKey(), next.getHeadValue()); + } + FIELD_HEADERS_OF_REQUEST.set(request, headerBuilder.build()); + } + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + Response response = (Response) ret; + if (response != null) { + int statusCode = response.code(); + AbstractSpan span = ContextManager.activeSpan(); + if (statusCode >= 400) { + span.errorOccurred(); + Tags.HTTP_RESPONSE_STATUS_CODE.set(span, statusCode); + } + } + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/OnFailureInterceptor.java b/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/OnFailureInterceptor.java index 8b38b4c8d..7a75f9d29 100644 --- a/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/OnFailureInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/OnFailureInterceptor.java @@ -29,12 +29,13 @@ public class OnFailureInterceptor implements InstanceMethodsAroundInterceptor { @Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { - ContextManager.activeSpan().log((Throwable) allArguments[1]); + ContextManager.createLocalSpan("Callback/onFailure"); } @Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + ContextManager.stopSpan(); return ret; } diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/OnResponseInterceptor.java b/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/OnResponseInterceptor.java index 62728b364..b1bd3c1fe 100644 --- a/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/OnResponseInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/OnResponseInterceptor.java @@ -18,7 +18,6 @@ package org.apache.skywalking.apm.plugin.okhttp.common; -import okhttp3.Response; import org.apache.skywalking.apm.agent.core.context.ContextManager; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; @@ -34,16 +33,13 @@ public class OnResponseInterceptor implements InstanceMethodsAroundInterceptor { @Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { - Response response = (Response) allArguments[1]; - - if (response.code() >= 400) { - ContextManager.activeSpan().errorOccurred(); - } + ContextManager.createLocalSpan("Callback/onResponse"); } @Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + ContextManager.stopSpan(); return ret; } diff --git a/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/RealCallInterceptor.java b/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/RealCallInterceptor.java index c94464953..c305c556b 100644 --- a/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/RealCallInterceptor.java +++ b/apm-sniffer/apm-sdk-plugin/okhttp-common/src/main/java/org/apache/skywalking/apm/plugin/okhttp/common/RealCallInterceptor.java @@ -18,6 +18,8 @@ package org.apache.skywalking.apm.plugin.okhttp.common; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import okhttp3.Headers; import okhttp3.HttpUrl; import okhttp3.Request; @@ -34,9 +36,6 @@ import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceM import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; -import java.lang.reflect.Field; -import java.lang.reflect.Method; - /** * {@link RealCallInterceptor} intercept the synchronous http calls by the discovery of okhttp. */ @@ -62,20 +61,19 @@ public class RealCallInterceptor implements InstanceMethodsAroundInterceptor, In /** * Get the {@link Request} from {@link EnhancedInstance}, then create {@link AbstractSpan} and set host, * port, kind, component, url from {@link Request}. Through the reflection of the way, set the http header - * of context data into {@link Request#headers}. + * of context data into {@link Request#headers()}. * * @param result change this result, if you want to truncate the method. */ @Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, - MethodInterceptResult result) throws Throwable { + MethodInterceptResult result) throws Throwable { Request request = (Request) objInst.getSkyWalkingDynamicField(); ContextCarrier contextCarrier = new ContextCarrier(); HttpUrl requestUrl = request.url(); - AbstractSpan span = ContextManager.createExitSpan(requestUrl.uri() - .getPath(), contextCarrier, requestUrl.host() + ":" + requestUrl - .port()); + AbstractSpan span = ContextManager.createExitSpan(requestUrl.uri().getPath(), contextCarrier, + requestUrl.host() + ":" + requestUrl.port()); span.setComponent(ComponentsDefine.OKHTTP); Tags.HTTP.METHOD.set(span, request.method()); Tags.URL.set(span, requestUrl.uri().toString()); @@ -100,7 +98,7 @@ public class RealCallInterceptor implements InstanceMethodsAroundInterceptor, In */ @Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, - Object ret) throws Throwable { + Object ret) throws Throwable { Response response = (Response) ret; if (response != null) { int statusCode = response.code(); @@ -118,7 +116,7 @@ public class RealCallInterceptor implements InstanceMethodsAroundInterceptor, In @Override public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, - Class[] argumentsTypes, Throwable t) { + Class[] argumentsTypes, Throwable t) { AbstractSpan abstractSpan = ContextManager.activeSpan(); abstractSpan.log(t); } diff --git a/apm-sniffer/apm-sdk-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/pom.xml index dfafd196f..b33edaca1 100644 --- a/apm-sniffer/apm-sdk-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/pom.xml @@ -112,6 +112,7 @@ httpclient-5.x-plugin clickhouse-0.3.x-plugin kylin-jdbc-2.6.x-3.x-4.x-plugin + okhttp-2.x-plugin pom diff --git a/docs/en/setup/service-agent/java-agent/Plugin-list.md b/docs/en/setup/service-agent/java-agent/Plugin-list.md index e3aa29bd8..968b96869 100644 --- a/docs/en/setup/service-agent/java-agent/Plugin-list.md +++ b/docs/en/setup/service-agent/java-agent/Plugin-list.md @@ -126,3 +126,4 @@ - neo4j-4.x - clickhouse-0.3.x - kylin-jdbc-2.6.x-3.x-4.x +- okhttp-2.x diff --git a/docs/en/setup/service-agent/java-agent/Supported-list.md b/docs/en/setup/service-agent/java-agent/Supported-list.md index 98d49a9d9..9f5c98052 100644 --- a/docs/en/setup/service-agent/java-agent/Supported-list.md +++ b/docs/en/setup/service-agent/java-agent/Supported-list.md @@ -22,7 +22,7 @@ metrics based on the tracing data. * HTTP Client * [Feign](https://github.com/OpenFeign/feign) 9.x * [Netflix Spring Cloud Feign](https://github.com/spring-cloud/spring-cloud-openfeign) 1.1.x -> 2.x - * [Okhttp](https://github.com/square/okhttp) 3.x -> 4.x + * [Okhttp](https://github.com/square/okhttp) 2.x -> 3.x -> 4.x * [Apache httpcomponent HttpClient](http://hc.apache.org/) 2.0 -> 3.1, 4.2, 4.3, 5.0, 5.1 * [Spring RestTemplete](https://github.com/spring-projects/spring-framework) 4.x * [Jetty Client](http://www.eclipse.org/jetty/) 9 diff --git a/pom.xml b/pom.xml index f90d4afd6..e7249bdbf 100755 --- a/pom.xml +++ b/pom.xml @@ -391,6 +391,7 @@ ${project.build.sourceDirectory} ${project.build.testSourceDirectory} +scenarios/okhttp-scenario **/*.properties, diff --git a/test/plugin/scenarios/okhttp-scenario/config/expectedData.yaml b/test/plugin/scenarios/okhttp-scenario/config/expectedData.yaml index fb12e4b3d..640777184 100644 --- a/test/plugin/scenarios/okhttp-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/okhttp-scenario/config/expectedData.yaml @@ -56,8 +56,8 @@ segmentItems: - {key: url, value: 'http://127.0.0.1:8080/okhttp-case/case/receiveContext-0'} - {key: http.method, value: GET} refs: - - {parentEndpoint: /okhttp-case/case/receiveContext-0, networkAddress: '127.0.0.1:8080', - refType: CrossProcess, parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + - {parentEndpoint: Async/execute, networkAddress: '127.0.0.1:8080', + refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: okhttp-scenario, traceId: not null} skipAnalysis: 'false' - segmentId: not null @@ -75,15 +75,15 @@ segmentItems: - {key: url, value: 'http://127.0.0.1:8080/okhttp-case/case/receiveContext-1'} - {key: http.method, value: GET} refs: - - {parentEndpoint: /okhttp-case/case/receiveContext-0, networkAddress: '127.0.0.1:8080', - refType: CrossProcess, parentSpanId: 0, parentTraceSegmentId: not null, parentServiceInstance: not + - {parentEndpoint: Async/execute, networkAddress: '127.0.0.1:8080', + refType: CrossProcess, parentSpanId: 3, parentTraceSegmentId: not null, parentServiceInstance: not null, parentService: okhttp-scenario, traceId: not null} skipAnalysis: 'false' - segmentId: not null spans: - operationName: /okhttp-case/case/receiveContext-0 - parentSpanId: -1 - spanId: 0 + parentSpanId: 0 + spanId: 1 spanLayer: Http startTime: nq 0 endTime: nq 0 @@ -94,8 +94,37 @@ segmentItems: tags: - {key: http.method, value: GET} - {key: url, value: 'http://127.0.0.1:8080/okhttp-case/case/receiveContext-0'} - refs: - - {parentEndpoint: GET:/case/okhttp-case, networkAddress: '', refType: CrossThread, - parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not - null, parentService: okhttp-scenario, traceId: not null} skipAnalysis: 'false' + - operationName: /okhttp-case/case/receiveContext-1 + parentSpanId: 2 + spanId: 3 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 12 + isError: false + spanType: Exit + peer: 127.0.0.1:8080 + skipAnalysis: false + tags: + - {key: http.method, value: GET} + - {key: url, value: 'http://127.0.0.1:8080/okhttp-case/case/receiveContext-1'} + - {operationName: Callback/onResponse, parentSpanId: 0, spanId: 2, + spanLayer: Unknown, startTime: nq 0, endTime: nq 0, componentId: 0, + isError: false, spanType: Local, peer: '', skipAnalysis: false} + - operationName: Async/execute + parentSpanId: -1 + spanId: 0 + spanLayer: Unknown + startTime: nq 0 + endTime: nq 0 + componentId: 0 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + refs: + - {parentEndpoint: 'GET:/case/okhttp-case', networkAddress: '', refType: CrossThread, + parentSpanId: 1, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: okhttp-scenario, + traceId: not null} diff --git a/test/plugin/scenarios/okhttp2-scenario/bin/startup.sh b/test/plugin/scenarios/okhttp2-scenario/bin/startup.sh new file mode 100644 index 000000000..6a7d3b60f --- /dev/null +++ b/test/plugin/scenarios/okhttp2-scenario/bin/startup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +home="$(cd "$(dirname $0)"; pwd)" + +java -jar ${agent_opts} ${home}/../libs/okhttp2-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/okhttp2-scenario/config/expectedData.yaml b/test/plugin/scenarios/okhttp2-scenario/config/expectedData.yaml new file mode 100644 index 000000000..4ed87e874 --- /dev/null +++ b/test/plugin/scenarios/okhttp2-scenario/config/expectedData.yaml @@ -0,0 +1,130 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +segmentItems: +- serviceName: okhttp2-scenario + segmentSize: ge 5 + segments: + - segmentId: not null + spans: + - operationName: Async/okhttp2-scenario/case/receiveContext-0 + parentSpanId: 0 + spanId: 1 + startTime: nq 0 + endTime: nq 0 + componentId: 0 + isError: false + spanType: Local + skipAnalysis: 'false' + - operationName: GET:/case/okhttp2-scenario + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 14 + isError: false + spanType: Entry + tags: + - {key: url, value: 'http://localhost:8080/okhttp2-scenario/case/okhttp2-scenario'} + - {key: http.method, value: GET} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: GET:/case/receiveContext-0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 14 + isError: false + spanType: Entry + tags: + - {key: url, value: 'http://127.0.0.1:8080/okhttp2-scenario/case/receiveContext-0'} + - {key: http.method, value: GET} + refs: + - {parentEndpoint: Async/execute, networkAddress: '127.0.0.1:8080', + refType: CrossProcess, parentSpanId: 1, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: okhttp2-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: GET:/case/receiveContext-1 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 14 + isError: false + spanType: Entry + tags: + - {key: url, value: 'http://127.0.0.1:8080/okhttp2-scenario/case/receiveContext-1'} + - {key: http.method, value: GET} + refs: + - {parentEndpoint: Async/execute, networkAddress: '127.0.0.1:8080', + refType: CrossProcess, parentSpanId: 3, parentTraceSegmentId: not null, parentServiceInstance: not + null, parentService: okhttp2-scenario, traceId: not null} + skipAnalysis: 'false' + - segmentId: not null + spans: + - operationName: /okhttp2-scenario/case/receiveContext-0 + parentSpanId: 0 + spanId: 1 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 12 + isError: false + spanType: Exit + peer: 127.0.0.1:8080 + tags: + - {key: http.method, value: GET} + - {key: url, value: 'http://127.0.0.1:8080/okhttp2-scenario/case/receiveContext-0'} + - operationName: /okhttp2-scenario/case/receiveContext-1 + parentSpanId: 2 + spanId: 3 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 12 + isError: false + spanType: Exit + peer: 127.0.0.1:8080 + skipAnalysis: false + tags: + - {key: http.method, value: GET} + - {key: url, value: 'http://127.0.0.1:8080/okhttp2-scenario/case/receiveContext-1'} + - {operationName: Callback/onResponse, parentSpanId: 0, spanId: 2, + spanLayer: Unknown, startTime: nq 0, endTime: nq 0, componentId: 0, + isError: false, spanType: Local, peer: '', skipAnalysis: false} + - operationName: Async/execute + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Unknown + startTime: nq 0 + endTime: nq 0 + componentId: 0 + isError: false + spanType: Local + peer: '' + skipAnalysis: false + refs: + - {parentEndpoint: 'GET:/case/okhttp2-scenario', networkAddress: '', refType: CrossThread, + parentSpanId: 1, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: okhttp2-scenario, + traceId: not null} diff --git a/test/plugin/scenarios/okhttp2-scenario/configuration.yml b/test/plugin/scenarios/okhttp2-scenario/configuration.yml new file mode 100644 index 000000000..b350fc776 --- /dev/null +++ b/test/plugin/scenarios/okhttp2-scenario/configuration.yml @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +type: jvm +entryService: http://localhost:8080/okhttp2-scenario/case/okhttp2-scenario +healthCheck: http://localhost:8080/okhttp2-scenario/case/healthCheck +startScript: ./bin/startup.sh diff --git a/test/plugin/scenarios/okhttp2-scenario/pom.xml b/test/plugin/scenarios/okhttp2-scenario/pom.xml new file mode 100644 index 000000000..f57242753 --- /dev/null +++ b/test/plugin/scenarios/okhttp2-scenario/pom.xml @@ -0,0 +1,124 @@ + + + + + org.apache.skywalking.apm.testcase + okhttp2-scenario + 1.0.0 + jar + + 4.0.0 + + + UTF-8 + 1.8 + 2.7.5 + 2.1.6.RELEASE + 1.18.20 + + + skywalking-okhttp2-scenario + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot-version} + pom + import + + + + + + + com.squareup.okhttp + okhttp + ${test.framework.version} + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + org.projectlombok + lombok + ${lombok.version} + provided + + + + + okhttp2-scenario + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + maven-compiler-plugin + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + + diff --git a/test/plugin/scenarios/okhttp2-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/okhttp2-scenario/src/main/assembly/assembly.xml new file mode 100644 index 000000000..e1f876557 --- /dev/null +++ b/test/plugin/scenarios/okhttp2-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ${project.build.directory}/okhttp2-scenario.jar + ./libs + 0775 + + + diff --git a/test/plugin/scenarios/okhttp2-scenario/src/main/java/test/apache/skywalking/apm/testcase/okhttp2/Application.java b/test/plugin/scenarios/okhttp2-scenario/src/main/java/test/apache/skywalking/apm/testcase/okhttp2/Application.java new file mode 100644 index 000000000..0b4d8b6be --- /dev/null +++ b/test/plugin/scenarios/okhttp2-scenario/src/main/java/test/apache/skywalking/apm/testcase/okhttp2/Application.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 test.apache.skywalking.apm.testcase.okhttp2; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + try { + SpringApplication.run(Application.class, args); + } catch (Exception e) { + // Never do this + } + } +} diff --git a/test/plugin/scenarios/okhttp2-scenario/src/main/java/test/apache/skywalking/apm/testcase/okhttp2/controller/CaseController.java b/test/plugin/scenarios/okhttp2-scenario/src/main/java/test/apache/skywalking/apm/testcase/okhttp2/controller/CaseController.java new file mode 100644 index 000000000..60aa663de --- /dev/null +++ b/test/plugin/scenarios/okhttp2-scenario/src/main/java/test/apache/skywalking/apm/testcase/okhttp2/controller/CaseController.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 test.apache.skywalking.apm.testcase.okhttp2.controller; + +import com.squareup.okhttp.Callback; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; +import com.squareup.okhttp.Response; +import java.io.IOException; +import lombok.extern.log4j.Log4j2; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/case") +@Log4j2 +public class CaseController { + + private static final String SUCCESS = "Success"; + + @RequestMapping("/receiveContext-1") + @ResponseBody + public String receiveContextService1() throws InterruptedException { + return "receiveContext-1"; + } + + @RequestMapping("/receiveContext-0") + @ResponseBody + public String receiveContextService0() throws InterruptedException { + return "receiveContext-0"; + } + + @RequestMapping("/okhttp2-scenario") + @ResponseBody + public String okHttpScenario() { + // Like gateway forward trace header. + Request request = new Request.Builder().url("http://127.0.0.1:8080/okhttp2-scenario/case/receiveContext-0") + .build(); + + new OkHttpClient().newCall(request).enqueue(new Callback() { + @Override + public void onFailure(Request request, IOException e) { + + } + + @Override + public void onResponse(Response response) throws IOException { + Request request = new Request.Builder().url( + "http://127.0.0.1:8080/okhttp2-scenario/case/receiveContext-1") + .build(); + new OkHttpClient().newCall(request).execute(); + } + + }); + + return "Success"; + } + + @RequestMapping("/healthCheck") + @ResponseBody + public String healthCheck() { + // your codes + return SUCCESS; + } + +} diff --git a/test/plugin/scenarios/okhttp2-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/okhttp2-scenario/src/main/resources/application.yaml new file mode 100644 index 000000000..aeff4b1af --- /dev/null +++ b/test/plugin/scenarios/okhttp2-scenario/src/main/resources/application.yaml @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# +# +server: + port: 8080 + servlet: + context-path: /okhttp2-scenario +logging: + config: classpath:log4j2.xml \ No newline at end of file diff --git a/test/plugin/scenarios/okhttp2-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/okhttp2-scenario/src/main/resources/log4j2.xml new file mode 100644 index 000000000..9849ed5a8 --- /dev/null +++ b/test/plugin/scenarios/okhttp2-scenario/src/main/resources/log4j2.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/plugin/scenarios/okhttp2-scenario/support-version.list b/test/plugin/scenarios/okhttp2-scenario/support-version.list new file mode 100644 index 000000000..f758f52ff --- /dev/null +++ b/test/plugin/scenarios/okhttp2-scenario/support-version.list @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. + +# lists your version here (Contains only the last version number of each minor version.) + +2.4.0 +2.5.0 +2.6.0 +2.7.5 \ No newline at end of file