diff --git a/agent-testweb/vertx-4-plugin-testweb/pom.xml b/agent-testweb/vertx-4-plugin-testweb/pom.xml index a2ccca0d2b00..baf0e86b22cf 100644 --- a/agent-testweb/vertx-4-plugin-testweb/pom.xml +++ b/agent-testweb/vertx-4-plugin-testweb/pom.xml @@ -32,7 +32,7 @@ 1.8 ${env.JAVA_8_HOME} - 4.2.2 + 4.5.0 true UTF-8 diff --git a/agent-testweb/vertx-4-plugin-testweb/src/main/java/com/pinpoint/test/plugin/Vertx4PluginTestStarter.java b/agent-testweb/vertx-4-plugin-testweb/src/main/java/com/pinpoint/test/plugin/Vertx4PluginTestStarter.java index aaec9af58abe..9a92f1755130 100644 --- a/agent-testweb/vertx-4-plugin-testweb/src/main/java/com/pinpoint/test/plugin/Vertx4PluginTestStarter.java +++ b/agent-testweb/vertx-4-plugin-testweb/src/main/java/com/pinpoint/test/plugin/Vertx4PluginTestStarter.java @@ -57,7 +57,6 @@ public void start(Promise startPromise) throws Exception { routingContext.response().end(buildMain("Welcome pinpoint vert.x HTTP server test", routes)); }); - router.get("/request").handler(routingContext -> { request(80, "naver.com", "/"); routingContext.response().end("Request http://naver.com:80/"); @@ -159,7 +158,7 @@ private void runOnContext(HttpServerRequest request, final int waitSeconds) { private void runOnContextRequest(HttpServerRequest request) { vertx.runOnContext(aVoid -> { - request(80, "naver.com", "/"); + request(80, "httpbin.org", "/"); request.response().end("Run on context request."); }); } diff --git a/plugins/vertx/README.md b/plugins/vertx/README.md index 8536731e414f..eb49cdb5c48b 100644 --- a/plugins/vertx/README.md +++ b/plugins/vertx/README.md @@ -7,7 +7,7 @@ pinpoint.config #### Compatibility setting -Vert.x 3.6 ~ 3.7 +Vert.x 3.6 ~ 4.5 ~~~ profiler.vertx.http.server.request-handler.method.name=io.vertx.ext.web.impl.RouterImpl.handle ~~~ diff --git a/plugins/vertx/src/main/java/com/navercorp/pinpoint/plugin/vertx/VertxPlugin.java b/plugins/vertx/src/main/java/com/navercorp/pinpoint/plugin/vertx/VertxPlugin.java index 3954bb1c210a..a1797c69b170 100644 --- a/plugins/vertx/src/main/java/com/navercorp/pinpoint/plugin/vertx/VertxPlugin.java +++ b/plugins/vertx/src/main/java/com/navercorp/pinpoint/plugin/vertx/VertxPlugin.java @@ -39,6 +39,7 @@ import com.navercorp.pinpoint.plugin.vertx.interceptor.HandlerInterceptor; import com.navercorp.pinpoint.plugin.vertx.interceptor.Http1xClientConnectionCreateRequest38Interceptor; import com.navercorp.pinpoint.plugin.vertx.interceptor.Http1xClientConnectionCreateRequest4xInterceptor; +import com.navercorp.pinpoint.plugin.vertx.interceptor.HttpClientImplCreateRequestInterceptor; import com.navercorp.pinpoint.plugin.vertx.interceptor.HttpClientImplDoRequestInterceptor; import com.navercorp.pinpoint.plugin.vertx.interceptor.HttpClientImplDoRequestInterceptorV4; import com.navercorp.pinpoint.plugin.vertx.interceptor.HttpClientImplInterceptor; @@ -77,7 +78,7 @@ public void setup(ProfilerPluginSetupContext context) { } // 3.3 <= x <= 3.5 - logger.info("Enable VertxPlugin. version range=[3.3, 4.2.2]"); + logger.info("Enable VertxPlugin. version range=[3.3, 4.5.0]"); logger.info("{} config:{}", this.getClass().getSimpleName(), config); // for vertx.io 3.3.x, 3.4.x @@ -106,6 +107,8 @@ public void setup(ProfilerPluginSetupContext context) { addContextImpl("io.vertx.core.impl.WorkerContext"); // 4.x addContextImpl("io.vertx.core.impl.DuplicatedContext"); + addContextImpl("io.vertx.core.impl.ContextBase"); + transformTemplate.transform("io.vertx.core.impl.ContextInternal", ContextInternalTransform.class); } if (config.isEnableHttpServer()) { @@ -226,12 +229,31 @@ public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, if (executeBlockingMethod4 != null) { executeBlockingMethod4.addInterceptor(ContextImplExecuteBlockingInterceptor.class); } + final InstrumentMethod executeBlockingMethod5 = target.getDeclaredMethod("executeBlocking", "io.vertx.core.impl.ContextInternal", "java.util.concurrent.Callable", "io.vertx.core.impl.WorkerPool", "io.vertx.core.impl.TaskQueue"); + if (executeBlockingMethod5 != null) { + executeBlockingMethod5.addInterceptor(ContextImplExecuteBlockingInterceptor.class); + } // skip executeFromIO() return target.toBytecode(); } } + public static class ContextInternalTransform implements TransformCallback { + @Override + public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { + final InstrumentClass target = instrumentor.getInstrumentClass(classLoader, className, classfileBuffer); + + final InstrumentMethod runOnContextMethod = target.getDeclaredMethod("runOnContext", "io.vertx.core.Handler"); + if (runOnContextMethod != null) { + runOnContextMethod.addInterceptor(ContextImplRunOnContextInterceptor.class); + } + + return target.toBytecode(); + } + } + + private void addRequestHandlerMethod(final String className, final String methodName) { transformTemplate.transform(className, RequestHandlerMethodTransform.class, new Object[]{methodName}, new Class[]{String.class}); } @@ -239,8 +261,7 @@ private void addRequestHandlerMethod(final String className, final String method public static class RequestHandlerMethodTransform implements TransformCallback { private final String methodName; - public RequestHandlerMethodTransform(String methodName) - { + public RequestHandlerMethodTransform(String methodName) { this.methodName = methodName; } @@ -381,6 +402,26 @@ public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, if (doRequestMethod3 != null) { doRequestMethod3.addScopedInterceptor(HttpClientImplDoRequestInterceptorV4.class, VertxConstants.HTTP_CLIENT_CREATE_REQUEST_SCOPE); } + // 4.4.0 + // void doRequest(HttpMethod method, SocketAddress peerAddress, SocketAddress server, String host, int port, Boolean useSSL, String requestURI, MultiMap headers, String traceOperation, long timeout, Boolean followRedirects, ProxyOptions proxyOptions, EndpointKey key, PromiseInternal requestPromise) + final InstrumentMethod doRequestMethod4 = target.getDeclaredMethod("doRequest", "io.vertx.core.http.HttpMethod", "io.vertx.core.net.SocketAddress", "io.vertx.core.net.SocketAddress", "java.lang.String", "int", "java.lang.Boolean", "java.lang.String", "io.vertx.core.MultiMap", "java.lang.String", "long", "java.lang.Boolean", "io.vertx.core.net.ProxyOptions", "io.vertx.core.http.impl.EndpointKey", "io.vertx.core.impl.future.PromiseInternal"); + if (doRequestMethod4 != null) { + doRequestMethod4.addScopedInterceptor(HttpClientImplDoRequestInterceptorV4.class, VertxConstants.HTTP_CLIENT_CREATE_REQUEST_SCOPE); + } + // 4.5.0 + // void doRequest(HttpMethod method, SocketAddress server, String host, int port, Boolean useSSL, String requestURI, MultiMap headers, String traceOperation, long connectTimeout, long idleTimeout, Boolean followRedirects, ProxyOptions proxyOptions, EndpointKey key, PromiseInternal requestPromise) + final InstrumentMethod doRequestMethod5 = target.getDeclaredMethod("doRequest", "io.vertx.core.http.HttpMethod", "io.vertx.core.net.SocketAddress", "java.lang.String", "int", "java.lang.Boolean", "java.lang.String", "io.vertx.core.MultiMap", "java.lang.String", "long", "long", "java.lang.Boolean", "io.vertx.core.net.ProxyOptions", "io.vertx.core.http.impl.EndpointKey", "io.vertx.core.impl.future.PromiseInternal"); + if (doRequestMethod5 != null) { + doRequestMethod5.addScopedInterceptor(HttpClientImplDoRequestInterceptorV4.class, VertxConstants.HTTP_CLIENT_CREATE_REQUEST_SCOPE); + } + + // 4.5.0 + // Forward the asyncContext from HttpClientImpl to HttpClientRequestImpl. + final InstrumentMethod createRequestMethod = target.getDeclaredMethod("createRequest", "io.vertx.core.http.impl.HttpClientStream"); + if (createRequestMethod != null) { + createRequestMethod.addInterceptor(HttpClientImplCreateRequestInterceptor.class); + } + return target.toBytecode(); } } @@ -405,6 +446,11 @@ public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, if (constructor2 != null) { constructor2.addInterceptor(HttpClientRequestImplConstructorInterceptor.class); } + // 4.5.0 + InstrumentMethod constructor3 = target.getConstructor("io.vertx.core.http.impl.HttpClientStream", "io.vertx.core.impl.future.PromiseInternal", "io.vertx.core.http.HttpMethod", "io.vertx.core.net.SocketAddress", "java.lang.String", "int", "java.lang.String", "java.lang.String"); + if (constructor3 != null) { + constructor3.addInterceptor(HttpClientRequestImplConstructorInterceptor.class); + } // for HttpClientResponseImpl. for (InstrumentMethod method : target.getDeclaredMethods(MethodFilters.name("doHandleResponse"))) { @@ -470,6 +516,7 @@ public static class ClientConnectionTransform implements TransformCallback { @Override public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { final InstrumentClass target = instrumentor.getInstrumentClass(classLoader, className, classfileBuffer); + target.addField(AsyncContextAccessor.class); target.addField(SamplingRateFlagAccessor.class); // add pinpoint headers. diff --git a/plugins/vertx/src/main/java/com/navercorp/pinpoint/plugin/vertx/interceptor/HttpClientImplCreateRequestInterceptor.java b/plugins/vertx/src/main/java/com/navercorp/pinpoint/plugin/vertx/interceptor/HttpClientImplCreateRequestInterceptor.java new file mode 100644 index 000000000000..513b3e8624c6 --- /dev/null +++ b/plugins/vertx/src/main/java/com/navercorp/pinpoint/plugin/vertx/interceptor/HttpClientImplCreateRequestInterceptor.java @@ -0,0 +1,50 @@ +/* + * Copyright 2023 NAVER Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.navercorp.pinpoint.plugin.vertx.interceptor; + +import com.navercorp.pinpoint.bootstrap.async.AsyncContextAccessorUtils; +import com.navercorp.pinpoint.bootstrap.context.AsyncContext; +import com.navercorp.pinpoint.bootstrap.interceptor.AroundInterceptor; +import com.navercorp.pinpoint.bootstrap.logging.PLogger; +import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory; + +public class HttpClientImplCreateRequestInterceptor implements AroundInterceptor { + private final PLogger logger = PLoggerFactory.getLogger(this.getClass()); + private final boolean isDebug = logger.isDebugEnabled(); + + public HttpClientImplCreateRequestInterceptor() { + } + + @Override + public void before(Object target, Object[] args) { + if (isDebug) { + logger.beforeInterceptor(target, args); + } + + final AsyncContext asyncContext = AsyncContextAccessorUtils.getAsyncContext(target); + if (asyncContext != null) { + AsyncContextAccessorUtils.setAsyncContext(asyncContext, args, 0); + if (isDebug) { + logger.debug("Set asyncContext to args[0]. asyncContext={}", asyncContext); + } + } + } + + @Override + public void after(Object target, Object[] args, Object result, Throwable throwable) { + } +} diff --git a/plugins/vertx/src/main/java/com/navercorp/pinpoint/plugin/vertx/interceptor/HttpClientImplDoRequestInterceptorV4.java b/plugins/vertx/src/main/java/com/navercorp/pinpoint/plugin/vertx/interceptor/HttpClientImplDoRequestInterceptorV4.java index 14dd99e1f2bb..0132e85069ed 100644 --- a/plugins/vertx/src/main/java/com/navercorp/pinpoint/plugin/vertx/interceptor/HttpClientImplDoRequestInterceptorV4.java +++ b/plugins/vertx/src/main/java/com/navercorp/pinpoint/plugin/vertx/interceptor/HttpClientImplDoRequestInterceptorV4.java @@ -107,6 +107,12 @@ private String toHostAndPort(final Object[] args) { final int port = (Integer) args[4]; return HostAndPort.toHostAndPortString(host, port); } + } else if (length == 14) { + if (args[2] instanceof String && args[3] instanceof Integer) { + final String host = (String) args[2]; + final int port = (Integer) args[3]; + return HostAndPort.toHostAndPortString(host, port); + } } return null;