From 4dadb4c9af63a500d2f9c2db7c4e4420b53214e3 Mon Sep 17 00:00:00 2001 From: jaehong-kim Date: Tue, 20 Oct 2020 18:09:36 +0900 Subject: [PATCH] [#7463] Add Reactor-Netty HTTP client plugin --- agent-testweb/pom.xml | 1 + .../reactor-netty-plugin-testweb/README.md | 15 ++ .../reactor-netty-plugin-testweb/pom.xml | 32 ++++ .../ReactorNettyPluginTestController.java | 73 +++++++++ .../plugin/ReactorNettyPluginTestStarter.java | 31 ++++ .../src/main/resources/application.yml | 12 ++ .../resources/profiles/local/pinpoint.config | 11 ++ .../profiles/release/pinpoint.config | 8 + ...ntextSpanEventSimpleAroundInterceptor.java | 8 +- .../pinpoint/common/trace/ServiceType.java | 2 + ...ncContextSpanEventEndPointInterceptor.java | 2 +- .../server/ServerListenerInterceptor.java | 14 +- ...ervableTimeoutListenerTickInterceptor.java | 21 ++- plugins/reactor-netty/README.md | 11 +- .../reactor/netty/ReactorNettyConstants.java | 2 + .../reactor/netty/ReactorNettyPlugin.java | 72 ++++++++- .../netty/ReactorNettyPluginConfig.java | 41 ++++-- ...tpClientHandlerConstructorInterceptor.java | 64 ++++++++ ...ientHandlerRequestWithBodyInterceptor.java | 138 ++++++++++++++++++ ...entOperationsOnInboundNextInterceptor.java | 98 +++++++++++++ ...erationsOnOutboundCompleteInterceptor.java | 45 ++++++ ...tOperationsOnOutboundErrorInterceptor.java | 50 +++++++ .../HttpClientRequestCookieExtractor.java | 58 ++++++++ .../HttpClientRequestHeaderAdaptor.java | 40 +++++ .../interceptor/HttpClientRequestWrapper.java | 81 ++++++++++ .../HttpTcpClientConnectInterceptor.java | 52 +++++++ .../META-INF/pinpoint/type-provider.yml | 14 ++ .../spring/webflux/SpringWebFluxPlugin.java | 11 +- .../webflux/SpringWebFluxPluginConfig.java | 7 + ...HandlerInvokeHandlerMethodInterceptor.java | 34 ++++- .../HttpServerExchangeAdaptor.java | 1 + 31 files changed, 1009 insertions(+), 40 deletions(-) create mode 100644 agent-testweb/reactor-netty-plugin-testweb/README.md create mode 100644 agent-testweb/reactor-netty-plugin-testweb/pom.xml create mode 100644 agent-testweb/reactor-netty-plugin-testweb/src/main/java/com/pinpoint/test/plugin/ReactorNettyPluginTestController.java create mode 100644 agent-testweb/reactor-netty-plugin-testweb/src/main/java/com/pinpoint/test/plugin/ReactorNettyPluginTestStarter.java create mode 100644 agent-testweb/reactor-netty-plugin-testweb/src/main/resources/application.yml create mode 100644 plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientHandlerConstructorInterceptor.java create mode 100644 plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientHandlerRequestWithBodyInterceptor.java create mode 100644 plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientOperationsOnInboundNextInterceptor.java create mode 100644 plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientOperationsOnOutboundCompleteInterceptor.java create mode 100644 plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientOperationsOnOutboundErrorInterceptor.java create mode 100644 plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientRequestCookieExtractor.java create mode 100644 plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientRequestHeaderAdaptor.java create mode 100644 plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientRequestWrapper.java create mode 100644 plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpTcpClientConnectInterceptor.java diff --git a/agent-testweb/pom.xml b/agent-testweb/pom.xml index 04e3137f5fbb..72945af21653 100644 --- a/agent-testweb/pom.xml +++ b/agent-testweb/pom.xml @@ -53,6 +53,7 @@ thread-plugin-testweb paho-mqtt-plugin-testweb + reactor-netty-plugin-testweb diff --git a/agent-testweb/reactor-netty-plugin-testweb/README.md b/agent-testweb/reactor-netty-plugin-testweb/README.md new file mode 100644 index 000000000000..ce71af2c8587 --- /dev/null +++ b/agent-testweb/reactor-netty-plugin-testweb/README.md @@ -0,0 +1,15 @@ +## Install +``` +$ mvnw -P pinpoint-reactor-netty-plugin-testweb install -Dmaven.test.skip=true +``` + +## Run +``` +$ mvnw -P pinpoint-reactor-netty-plugin-testweb spring-boot:start +``` +You can then access here: http://localhost:18080/client/get + +## Stop +``` +$ mvnw -P pinpoint-reactor-netty-plugin-testweb spring-boot:stop +``` diff --git a/agent-testweb/reactor-netty-plugin-testweb/pom.xml b/agent-testweb/reactor-netty-plugin-testweb/pom.xml new file mode 100644 index 000000000000..662ac09cad4b --- /dev/null +++ b/agent-testweb/reactor-netty-plugin-testweb/pom.xml @@ -0,0 +1,32 @@ + + + 4.0.0 + + com.navercorp.pinpoint + pinpoint-agent-testweb + 2.2.1-SNAPSHOT + + + pinpoint-reactor-netty-plugin-testweb + + jar + + + + ${pinpoint.agent.default.jvmargument} + -Dprofiler.reactor-netty.client.enable=true + -Dprofiler.reactor-netty.client.param=true + + + + + + io.projectreactor.netty + reactor-netty + 0.9.12.RELEASE + + + + \ No newline at end of file diff --git a/agent-testweb/reactor-netty-plugin-testweb/src/main/java/com/pinpoint/test/plugin/ReactorNettyPluginTestController.java b/agent-testweb/reactor-netty-plugin-testweb/src/main/java/com/pinpoint/test/plugin/ReactorNettyPluginTestController.java new file mode 100644 index 000000000000..b472cf742bd3 --- /dev/null +++ b/agent-testweb/reactor-netty-plugin-testweb/src/main/java/com/pinpoint/test/plugin/ReactorNettyPluginTestController.java @@ -0,0 +1,73 @@ +/* + * Copyright 2020 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.pinpoint.test.plugin; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import reactor.core.publisher.Mono; +import reactor.netty.ByteBufFlux; +import reactor.netty.http.client.HttpClient; +import reactor.netty.http.client.HttpClientResponse; + +import javax.servlet.http.HttpServletRequest; + +/** + * @author jaehong.kim + */ +@RestController +public class ReactorNettyPluginTestController { + + @RequestMapping(value = "/client/echo", method = RequestMethod.GET) + @ResponseBody + public String clientEcho() { + return "Welcome"; + } + + @RequestMapping(value = "/client/get", method = RequestMethod.GET) + @ResponseBody + public String clientGet() { + HttpClient client = HttpClient.create().port(80); + String response = client.get().uri("https://www.google.com?foo=bar").responseContent().aggregate().asString().block(); + return response; + } + + @RequestMapping(value = "/client/get_local", method = RequestMethod.GET) + @ResponseBody + public String clientError(HttpServletRequest request) { + HttpClient client = HttpClient.create().port(request.getLocalPort()); + String response = client.get().uri("/client/echo").responseContent().aggregate().asString().block(); + return response; + } + + @RequestMapping(value = "/client/post", method = RequestMethod.GET) + @ResponseBody + public String clientPost() { + HttpClient client = HttpClient.create().port(80); + HttpClientResponse response = client.post().uri("https://www.google.com/").send(ByteBufFlux.fromString(Mono.just("hello"))).response().block(); + return response.toString(); + } + + @RequestMapping(value = "/client/unknown_host", method = RequestMethod.GET) + @ResponseBody + public String clientError() { + HttpClient client = HttpClient.create().port(80); + String response = client.get().uri("http://fjalkjdlfaj.com").responseContent().aggregate().asString().block(); + return response; + } +} diff --git a/agent-testweb/reactor-netty-plugin-testweb/src/main/java/com/pinpoint/test/plugin/ReactorNettyPluginTestStarter.java b/agent-testweb/reactor-netty-plugin-testweb/src/main/java/com/pinpoint/test/plugin/ReactorNettyPluginTestStarter.java new file mode 100644 index 000000000000..0248792d1657 --- /dev/null +++ b/agent-testweb/reactor-netty-plugin-testweb/src/main/java/com/pinpoint/test/plugin/ReactorNettyPluginTestStarter.java @@ -0,0 +1,31 @@ +/* + * Copyright 2020 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.pinpoint.test.plugin; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author jaehong.kim + */ +@SpringBootApplication +public class ReactorNettyPluginTestStarter { + + public static void main(String[] args) { + SpringApplication.run(ReactorNettyPluginTestStarter.class, args); + } +} \ No newline at end of file diff --git a/agent-testweb/reactor-netty-plugin-testweb/src/main/resources/application.yml b/agent-testweb/reactor-netty-plugin-testweb/src/main/resources/application.yml new file mode 100644 index 000000000000..932e51e19650 --- /dev/null +++ b/agent-testweb/reactor-netty-plugin-testweb/src/main/resources/application.yml @@ -0,0 +1,12 @@ +# Defined in commandlineArgument of agent-test pom.xml + +server: + port: 18080 + +#logging: +# level: +# root: info + +#springdoc: +# swagger-ui: +# path: / \ No newline at end of file diff --git a/agent/src/main/resources/profiles/local/pinpoint.config b/agent/src/main/resources/profiles/local/pinpoint.config index 44668eb0e34b..f7f88bc5051d 100644 --- a/agent/src/main/resources/profiles/local/pinpoint.config +++ b/agent/src/main/resources/profiles/local/pinpoint.config @@ -500,6 +500,7 @@ profiler.springboot.bootstrap.main=org.springframework.boot.loader.JarLauncher, ########################################################### profiler.reactor-netty.enable=true +# Server # Classes for detecting application server type. Comma separated list of fully qualified class names. Wildcard not supported. profiler.reactor-netty.server.bootstrap.main= # use an asynchronous endpoint. @@ -520,11 +521,19 @@ profiler.reactor-netty.server.excludeurl= # optional parameter, If the header value is ${profiler.reactor-netty.realipemptyvalue}, Ignore header value. #profiler.reactor-netty.server.realipemptyvalue=unknown +# Client +profiler.reactor-netty.client.enable=true +# Record Parameter. +profiler.reactor-netty.client.param=true +# Unsupported cookie and entity information + # Set whether to trace the Subscriber.onError(Throwable t) method profiler.reactor-netty.trace.subscribe.error=true # Set messages to be excluded from errors. Messages are separated by ',' characters. profiler.reactor-netty.trace.subscribe.error.exclude.message= + + ########################################################### # JSP # ########################################################### @@ -917,6 +926,8 @@ profiler.spring.async.executor.class.names= profiler.spring.webflux.enable=true # Client +# If you are not using Reactor-Netty, set it to true. +profiler.spring.webflux.client.enable=false # Record Parameter. profiler.spring.webflux.client.param=true # Record cookies. diff --git a/agent/src/main/resources/profiles/release/pinpoint.config b/agent/src/main/resources/profiles/release/pinpoint.config index 3ddf66cc5a18..2862777f8aa4 100644 --- a/agent/src/main/resources/profiles/release/pinpoint.config +++ b/agent/src/main/resources/profiles/release/pinpoint.config @@ -519,6 +519,12 @@ profiler.reactor-netty.server.excludeurl= # optional parameter, If the header value is ${profiler.reactor-netty.realipemptyvalue}, Ignore header value. #profiler.reactor-netty.server.realipemptyvalue=unknown +# Client +profiler.reactor-netty.client.enable=true +# Record Parameter. +profiler.reactor-netty.client.param=true +# Unsupported cookie and entity information + # Set whether to trace the Subscriber.onError(Throwable t) method profiler.reactor-netty.trace.subscribe.error=true # Set messages to be excluded from errors. Messages are separated by ',' characters. @@ -916,6 +922,8 @@ profiler.spring.async.executor.class.names= profiler.spring.webflux.enable=true # Client +# If you are not using Reactor-Netty, set it to true. +profiler.spring.webflux.client.enable=false # Record Parameter. profiler.spring.webflux.client.param=true # Record cookies. diff --git a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/AsyncContextSpanEventSimpleAroundInterceptor.java b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/AsyncContextSpanEventSimpleAroundInterceptor.java index 2512c43666fb..90b242f7294e 100644 --- a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/AsyncContextSpanEventSimpleAroundInterceptor.java +++ b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/interceptor/AsyncContextSpanEventSimpleAroundInterceptor.java @@ -82,7 +82,7 @@ public void after(Object target, Object[] args, Object result, Throwable throwab logger.afterInterceptor(target, args, result, throwable); } - final AsyncContext asyncContext = getAsyncContext(target, args); + final AsyncContext asyncContext = getAsyncContext(target, args, result, throwable); if (asyncContext == null) { logger.debug("AsyncContext not found"); return; @@ -120,11 +120,11 @@ public void after(Object target, Object[] args, Object result, Throwable throwab protected abstract void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable); - protected AsyncContext getAsyncContext(Object target) { - return getAsyncContext(target, null); + protected AsyncContext getAsyncContext(Object target, Object[] args) { + return AsyncContextAccessorUtils.getAsyncContext(target); } - protected AsyncContext getAsyncContext(Object target, Object[] args) { + protected AsyncContext getAsyncContext(Object target, Object[] args, Object result, Throwable throwable) { return AsyncContextAccessorUtils.getAsyncContext(target); } diff --git a/commons/src/main/java/com/navercorp/pinpoint/common/trace/ServiceType.java b/commons/src/main/java/com/navercorp/pinpoint/common/trace/ServiceType.java index 14ca59b6ca9d..87d0df680a4a 100644 --- a/commons/src/main/java/com/navercorp/pinpoint/common/trace/ServiceType.java +++ b/commons/src/main/java/com/navercorp/pinpoint/common/trace/ServiceType.java @@ -214,6 +214,8 @@ * 9151NETTY_INTERNAL * 9152NETTY_HTTP * 9153SPRING_WEBFLUX_CLIENT + * 9154REACTOR_NETTY_CLIENT + * 9155REACTOR_NETTY_CLIENT_INTERNAL * 9160GRPC * 9161GRPC_INTERNAL * 9162GRPC_SERVER_INTERNAL diff --git a/plugins/grpc/src/main/java/com/navercorp/pinpoint/plugin/grpc/interceptor/server/GrpcAsyncContextSpanEventEndPointInterceptor.java b/plugins/grpc/src/main/java/com/navercorp/pinpoint/plugin/grpc/interceptor/server/GrpcAsyncContextSpanEventEndPointInterceptor.java index f776b317fd1a..cc70fbc9916e 100644 --- a/plugins/grpc/src/main/java/com/navercorp/pinpoint/plugin/grpc/interceptor/server/GrpcAsyncContextSpanEventEndPointInterceptor.java +++ b/plugins/grpc/src/main/java/com/navercorp/pinpoint/plugin/grpc/interceptor/server/GrpcAsyncContextSpanEventEndPointInterceptor.java @@ -43,7 +43,7 @@ public void after(Object target, Object[] args, Object result, Throwable throwab logger.afterInterceptor(target, args, result, throwable); } - final AsyncContext asyncContext = getAsyncContext(target); + final AsyncContext asyncContext = getAsyncContext(target, args, result, throwable); if (asyncContext == null) { logger.debug("Not found asynchronous invocation metadata"); return; diff --git a/plugins/grpc/src/main/java/com/navercorp/pinpoint/plugin/grpc/interceptor/server/ServerListenerInterceptor.java b/plugins/grpc/src/main/java/com/navercorp/pinpoint/plugin/grpc/interceptor/server/ServerListenerInterceptor.java index b7753cb244fc..beb851c80c04 100644 --- a/plugins/grpc/src/main/java/com/navercorp/pinpoint/plugin/grpc/interceptor/server/ServerListenerInterceptor.java +++ b/plugins/grpc/src/main/java/com/navercorp/pinpoint/plugin/grpc/interceptor/server/ServerListenerInterceptor.java @@ -32,8 +32,9 @@ public ServerListenerInterceptor(TraceContext traceContext, MethodDescriptor met super(traceContext, methodDescriptor); } + // BEFORE @Override - protected AsyncContext getAsyncContext(Object target) { + protected AsyncContext getAsyncContext(Object target, Object[] args) { if (target instanceof AsyncContextAccessor) { return ((AsyncContextAccessor) target)._$PINPOINT$_getAsyncContext(); } @@ -44,7 +45,17 @@ protected AsyncContext getAsyncContext(Object target) { @Override protected void doInBeforeTrace(SpanEventRecorder recorder, AsyncContext asyncContext, Object target, Object[] args) { + } + // AFTER + @Override + protected AsyncContext getAsyncContext(Object target, Object[] args, Object result, Throwable throwable) { + if (target instanceof AsyncContextAccessor) { + return ((AsyncContextAccessor) target)._$PINPOINT$_getAsyncContext(); + } + + logger.info("failed to get AsyncContext"); + return null; } @Override @@ -52,5 +63,4 @@ protected void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[ recorder.recordApi(methodDescriptor); recorder.recordServiceType(GrpcConstants.SERVER_SERVICE_TYPE_INTERNAL); } - } diff --git a/plugins/hystrix/src/main/java/com/navercorp/pinpoint/plugin/hystrix/interceptor/HystrixObservableTimeoutListenerTickInterceptor.java b/plugins/hystrix/src/main/java/com/navercorp/pinpoint/plugin/hystrix/interceptor/HystrixObservableTimeoutListenerTickInterceptor.java index 22f0571d0d9e..d4eaf09d1ccc 100644 --- a/plugins/hystrix/src/main/java/com/navercorp/pinpoint/plugin/hystrix/interceptor/HystrixObservableTimeoutListenerTickInterceptor.java +++ b/plugins/hystrix/src/main/java/com/navercorp/pinpoint/plugin/hystrix/interceptor/HystrixObservableTimeoutListenerTickInterceptor.java @@ -38,13 +38,9 @@ public HystrixObservableTimeoutListenerTickInterceptor(TraceContext traceContext traceContext.cacheApi(HYSTRIX_COMMAND_TIMEOUT_TIMER_METHOD_DESCRIPTOR); } + // BEFORE @Override - protected AsyncContext getAsyncContext(Object target) { - return getAsyncContext(target, null); - } - - @Override - protected AsyncContext getAsyncContext(Object target, Object[] args) { + public AsyncContext getAsyncContext(Object target, Object[] args) { if (target instanceof EnclosingInstanceAccessor) { return AsyncContextAccessorUtils.getAsyncContext(((EnclosingInstanceAccessor) target)._$PINPOINT$_getEnclosingInstance()); } @@ -52,11 +48,20 @@ protected AsyncContext getAsyncContext(Object target, Object[] args) { } @Override - protected void doInBeforeTrace(SpanEventRecorder recorder, AsyncContext asyncContext, Object target, Object[] args) { + public void doInBeforeTrace(SpanEventRecorder recorder, AsyncContext asyncContext, Object target, Object[] args) { + } + + // AFTER + @Override + public AsyncContext getAsyncContext(Object target, Object[] args, Object result, Throwable throwable) { + if (target instanceof EnclosingInstanceAccessor) { + return AsyncContextAccessorUtils.getAsyncContext(((EnclosingInstanceAccessor) target)._$PINPOINT$_getEnclosingInstance()); + } + return null; } @Override - protected void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) { + public void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) { recorder.recordServiceType(HystrixPluginConstants.HYSTRIX_INTERNAL_SERVICE_TYPE); recorder.recordApi(HYSTRIX_COMMAND_TIMEOUT_TIMER_METHOD_DESCRIPTOR); recorder.recordException(throwable); diff --git a/plugins/reactor-netty/README.md b/plugins/reactor-netty/README.md index d93c985ecb31..12e6d2cbdfe0 100644 --- a/plugins/reactor-netty/README.md +++ b/plugins/reactor-netty/README.md @@ -1,5 +1,5 @@ ## Reactor Netty -* Version: 1.0 +* Version: 1.1 * Since: Pinpoint 2.0.0 * See: https://github.com/reactor/reactor-netty * See: [Project Reactor](https://projectreactor.io/) @@ -34,6 +34,12 @@ profiler.reactor-netty.server.excludeurl= #profiler.reactor-netty.server.realipheader=X-Real-IP # optional parameter, If the header value is ${profiler.reactor-netty.realipemptyvalue}, Ignore header value. #profiler.reactor-netty.server.realipemptyvalue=unknown + +# Client +profiler.reactor-netty.client.enable=true +# Record Parameter. +profiler.reactor-netty.client.param=true +# Unsupported cookie and entity information ~~~ If you use Spring boot starter, if you set main class as profiler.spring boot.bootstrap.main setting value. @@ -44,3 +50,6 @@ profiler.springboot.bootstrap.main=foo.bar.SampleApplication ### Web Server * Netty(Reactor Netty) HTTP Server + +### HTTP Client +* Netty(Reactor Netty) HTTP Client diff --git a/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/ReactorNettyConstants.java b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/ReactorNettyConstants.java index 38a07d5a30a2..f2221f2ef7a3 100644 --- a/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/ReactorNettyConstants.java +++ b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/ReactorNettyConstants.java @@ -25,4 +25,6 @@ public class ReactorNettyConstants { public static final ServiceType REACTOR_NETTY = ServiceTypeProvider.getByName("REACTOR_NETTY"); public static final ServiceType REACTOR_NETTY_INTERNAL = ServiceTypeProvider.getByName("REACTOR_NETTY_INTERNAL"); + public static final ServiceType REACTOR_NETTY_CLIENT = ServiceTypeProvider.getByName("REACTOR_NETTY_CLIENT"); + public static final ServiceType REACTOR_NETTY_CLIENT_INTERNAL = ServiceTypeProvider.getByName("REACTOR_NETTY_CLIENT_INTERNAL"); } diff --git a/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/ReactorNettyPlugin.java b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/ReactorNettyPlugin.java index 091e3c218d9e..bba3189cfcf5 100644 --- a/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/ReactorNettyPlugin.java +++ b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/ReactorNettyPlugin.java @@ -29,8 +29,6 @@ import com.navercorp.pinpoint.bootstrap.instrument.transformer.MatchableTransformTemplate; import com.navercorp.pinpoint.bootstrap.instrument.transformer.MatchableTransformTemplateAware; import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformCallback; -import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplate; -import com.navercorp.pinpoint.bootstrap.instrument.transformer.TransformTemplateAware; import com.navercorp.pinpoint.bootstrap.logging.PLogger; import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory; import com.navercorp.pinpoint.bootstrap.plugin.ProfilerPlugin; @@ -40,8 +38,14 @@ import com.navercorp.pinpoint.plugin.reactor.netty.interceptor.ChannelOperationsInterceptor; import com.navercorp.pinpoint.plugin.reactor.netty.interceptor.CorePublisherInterceptor; import com.navercorp.pinpoint.plugin.reactor.netty.interceptor.CoreSubscriberInterceptor; +import com.navercorp.pinpoint.plugin.reactor.netty.interceptor.HttpClientHandlerRequestWithBodyInterceptor; +import com.navercorp.pinpoint.plugin.reactor.netty.interceptor.HttpClientHandlerConstructorInterceptor; +import com.navercorp.pinpoint.plugin.reactor.netty.interceptor.HttpClientOperationsOnInboundNextInterceptor; +import com.navercorp.pinpoint.plugin.reactor.netty.interceptor.HttpClientOperationsOnOutboundCompleteInterceptor; +import com.navercorp.pinpoint.plugin.reactor.netty.interceptor.HttpClientOperationsOnOutboundErrorInterceptor; import com.navercorp.pinpoint.plugin.reactor.netty.interceptor.HttpServerHandleHttpServerStateInterceptor; import com.navercorp.pinpoint.plugin.reactor.netty.interceptor.HttpServerHandleStateInterceptor; +import com.navercorp.pinpoint.plugin.reactor.netty.interceptor.HttpTcpClientConnectInterceptor; import com.navercorp.pinpoint.plugin.reactor.netty.interceptor.SubscribeOrReturnMethodInterceptor; import java.security.ProtectionDomain; @@ -77,6 +81,14 @@ public void setup(ProfilerPluginSetupContext context) { transformTemplate.transform("reactor.netty.http.server.HttpServerHandle", HttpServerHandleTransform.class); transformTemplate.transform("reactor.netty.channel.ChannelOperations", ChannelOperationsTransform.class); transformTemplate.transform("reactor.netty.http.server.HttpServerOperations", HttpServerOperationsTransform.class); + + // HTTP client + if (Boolean.TRUE == config.isClientEnable()) { + transformTemplate.transform("reactor.netty.http.client.HttpClientConnect$HttpTcpClient", HttpTcpClientTransform.class); + transformTemplate.transform("reactor.netty.http.client.HttpClientConnect$HttpClientHandler", HttpClientHandleTransform.class); + transformTemplate.transform("reactor.netty.http.client.HttpClientOperations", HttpClientOperationsTransform.class); + } + // Reactor final Matcher monoMatcher = Matchers.newPackageBasedMatcher("reactor.netty", new SuperClassInternalNameMatcherOperand("reactor.core.publisher.Mono", true)); transformTemplate.transform(monoMatcher, FluxAndMonoTransform.class); @@ -139,6 +151,62 @@ public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, Strin } } + public static class HttpTcpClientTransform implements TransformCallback { + @Override + public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { + InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); + final InstrumentMethod method = target.getDeclaredMethod("connect", "io.netty.bootstrap.Bootstrap"); + if (method != null) { + method.addInterceptor(HttpTcpClientConnectInterceptor.class); + } + + return target.toBytecode(); + } + } + + public static class HttpClientHandleTransform implements TransformCallback { + @Override + public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { + InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); + target.addField(AsyncContextAccessor.class); + final InstrumentMethod constructor = target.getConstructor("reactor.netty.http.client.HttpClientConfiguration", "java.net.SocketAddress", "reactor.netty.tcp.SslProvider", "reactor.netty.tcp.ProxyProvider"); + if (constructor != null) { + constructor.addInterceptor(HttpClientHandlerConstructorInterceptor.class); + } + + final InstrumentMethod method = target.getDeclaredMethod("requestWithBody", "reactor.netty.http.client.HttpClientOperations"); + if (method != null) { + method.addInterceptor(HttpClientHandlerRequestWithBodyInterceptor.class); + } + + return target.toBytecode(); + } + } + + + public static class HttpClientOperationsTransform implements TransformCallback { + @Override + public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { + InstrumentClass target = instrumentor.getInstrumentClass(loader, className, classfileBuffer); + target.addField(AsyncContextAccessor.class); + final InstrumentMethod onOutboundCompleteMethod = target.getDeclaredMethod("onOutboundComplete"); + if (onOutboundCompleteMethod != null) { + onOutboundCompleteMethod.addInterceptor(HttpClientOperationsOnOutboundCompleteInterceptor.class); + } + final InstrumentMethod onOutboundErrorMethod = target.getDeclaredMethod("onOutboundError", "java.lang.Throwable"); + if (onOutboundErrorMethod != null) { + onOutboundErrorMethod.addInterceptor(HttpClientOperationsOnOutboundErrorInterceptor.class); + } + + final InstrumentMethod onInboundNextMethod = target.getDeclaredMethod("onInboundNext", "io.netty.channel.ChannelHandlerContext", "java.lang.Object"); + if (onInboundNextMethod != null) { + onInboundNextMethod.addInterceptor(HttpClientOperationsOnInboundNextInterceptor.class); + } + + return target.toBytecode(); + } + } + public static class FluxAndMonoTransform implements TransformCallback { @Override public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws InstrumentException { diff --git a/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/ReactorNettyPluginConfig.java b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/ReactorNettyPluginConfig.java index 12be845555a5..e8e230f648fd 100644 --- a/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/ReactorNettyPluginConfig.java +++ b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/ReactorNettyPluginConfig.java @@ -36,6 +36,8 @@ public class ReactorNettyPluginConfig { private final boolean enableAsyncEndPoint; private final boolean traceSubscribeError; private final List traceSubscribeErrorExcludeMessageList; + private final boolean clientEnable; + private boolean param = true; public ReactorNettyPluginConfig(ProfilerConfig config) { if (config == null) { @@ -53,6 +55,10 @@ public ReactorNettyPluginConfig(ProfilerConfig config) { this.realIpHeader = serverConfig.getRealIpHeader("profiler.reactor-netty.server.realipheader"); this.realIpEmptyValue = serverConfig.getRealIpEmptyValue("profiler.reactor-netty.server.realipemptyvalue"); this.excludeProfileMethodFilter = serverConfig.getExcludeMethodFilter("profiler.reactor-netty.server.excludemethod"); + // Client + this.clientEnable = config.readBoolean("profiler.reactor-netty.client.enable", true); + this.param = config.readBoolean("profiler.reactor-netty.client.param", true); + // Reactor this.traceSubscribeError = config.readBoolean("profiler.reactor-netty.trace.subscribe.error", true); this.traceSubscribeErrorExcludeMessageList = config.readList("profiler.reactor-netty.trace.subscribe.error.exclude.message"); @@ -98,20 +104,29 @@ public List getTraceSubscribeErrorExcludeMessageList() { return traceSubscribeErrorExcludeMessageList; } + public boolean isClientEnable() { + return clientEnable; + } + + public boolean isParam() { + return param; + } + @Override public String toString() { - final StringBuilder sb = new StringBuilder("ReactorNettyPluginConfig{"); - sb.append("enable=").append(enable); - sb.append(", bootstrapMains=").append(bootstrapMains); - sb.append(", traceRequestParam=").append(traceRequestParam); - sb.append(", excludeUrlFilter=").append(excludeUrlFilter); - sb.append(", realIpHeader='").append(realIpHeader).append('\''); - sb.append(", realIpEmptyValue='").append(realIpEmptyValue).append('\''); - sb.append(", excludeProfileMethodFilter=").append(excludeProfileMethodFilter); - sb.append(", enableAsyncEndPoint=").append(enableAsyncEndPoint); - sb.append(", traceSubscribeError=").append(traceSubscribeError); - sb.append(", traceSubscribeErrorExcludeMessageList=").append(traceSubscribeErrorExcludeMessageList); - sb.append('}'); - return sb.toString(); + return "ReactorNettyPluginConfig{" + + "enable=" + enable + + ", bootstrapMains=" + bootstrapMains + + ", traceRequestParam=" + traceRequestParam + + ", excludeUrlFilter=" + excludeUrlFilter + + ", realIpHeader='" + realIpHeader + '\'' + + ", realIpEmptyValue='" + realIpEmptyValue + '\'' + + ", excludeProfileMethodFilter=" + excludeProfileMethodFilter + + ", enableAsyncEndPoint=" + enableAsyncEndPoint + + ", traceSubscribeError=" + traceSubscribeError + + ", traceSubscribeErrorExcludeMessageList=" + traceSubscribeErrorExcludeMessageList + + ", clientEnable=" + clientEnable + + ", param=" + param + + '}'; } } diff --git a/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientHandlerConstructorInterceptor.java b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientHandlerConstructorInterceptor.java new file mode 100644 index 000000000000..669002bbdac2 --- /dev/null +++ b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientHandlerConstructorInterceptor.java @@ -0,0 +1,64 @@ +/* + * Copyright 2020 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.reactor.netty.interceptor; + +import com.navercorp.pinpoint.bootstrap.async.AsyncContextAccessor; +import com.navercorp.pinpoint.bootstrap.context.AsyncContext; +import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor; +import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; +import com.navercorp.pinpoint.bootstrap.interceptor.SpanEventSimpleAroundInterceptorForPlugin; +import com.navercorp.pinpoint.bootstrap.plugin.util.SocketAddressUtils; +import com.navercorp.pinpoint.common.plugin.util.HostAndPort; +import com.navercorp.pinpoint.common.trace.AnnotationKey; +import com.navercorp.pinpoint.plugin.reactor.netty.ReactorNettyConstants; + +import java.net.InetSocketAddress; + +/** + * @author jaehong.kim + */ +public class HttpClientHandlerConstructorInterceptor extends SpanEventSimpleAroundInterceptorForPlugin { + + public HttpClientHandlerConstructorInterceptor(TraceContext traceContext, MethodDescriptor methodDescriptor) { + super(traceContext, methodDescriptor); + } + + @Override + public void doInBeforeTrace(SpanEventRecorder recorder, Object target, Object[] args) throws Exception { + if (args != null && args.length >= 2 && args[1] instanceof InetSocketAddress) { + final InetSocketAddress inetSocketAddress = (InetSocketAddress) args[1]; + if (inetSocketAddress != null) { + final String hostName = SocketAddressUtils.getHostNameFirst(inetSocketAddress); + if (hostName != null) { + recorder.recordAttribute(AnnotationKey.HTTP_INTERNAL_DISPLAY, HostAndPort.toHostAndPortString(hostName, inetSocketAddress.getPort())); + } + } + } + } + + @Override + public void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) throws Exception { + recorder.recordApi(methodDescriptor); + recorder.recordException(throwable); + recorder.recordServiceType(ReactorNettyConstants.REACTOR_NETTY_CLIENT_INTERNAL); + if (throwable == null && target instanceof AsyncContextAccessor) { + final AsyncContext asyncContext = recorder.recordNextAsyncContext(); + ((AsyncContextAccessor) target)._$PINPOINT$_setAsyncContext(asyncContext); + } + } +} diff --git a/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientHandlerRequestWithBodyInterceptor.java b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientHandlerRequestWithBodyInterceptor.java new file mode 100644 index 000000000000..550fed039411 --- /dev/null +++ b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientHandlerRequestWithBodyInterceptor.java @@ -0,0 +1,138 @@ +/* + * Copyright 2020 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.reactor.netty.interceptor; + +import com.navercorp.pinpoint.bootstrap.async.AsyncContextAccessor; +import com.navercorp.pinpoint.bootstrap.async.AsyncContextAccessorUtils; +import com.navercorp.pinpoint.bootstrap.context.AsyncContext; +import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor; +import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder; +import com.navercorp.pinpoint.bootstrap.context.Trace; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; +import com.navercorp.pinpoint.bootstrap.context.TraceId; +import com.navercorp.pinpoint.bootstrap.interceptor.AsyncContextSpanEventSimpleAroundInterceptor; +import com.navercorp.pinpoint.bootstrap.logging.PLogger; +import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory; +import com.navercorp.pinpoint.bootstrap.plugin.request.ClientRequestAdaptor; +import com.navercorp.pinpoint.bootstrap.plugin.request.ClientRequestRecorder; +import com.navercorp.pinpoint.bootstrap.plugin.request.ClientRequestWrapper; +import com.navercorp.pinpoint.bootstrap.plugin.request.ClientRequestWrapperAdaptor; +import com.navercorp.pinpoint.bootstrap.plugin.request.DefaultRequestTraceWriter; +import com.navercorp.pinpoint.bootstrap.plugin.request.RequestTraceWriter; +import com.navercorp.pinpoint.plugin.reactor.netty.ReactorNettyConstants; +import com.navercorp.pinpoint.plugin.reactor.netty.ReactorNettyPluginConfig; +import reactor.netty.http.client.HttpClientRequest; + +/** + * @author jaehong.kim + */ +public class HttpClientHandlerRequestWithBodyInterceptor extends AsyncContextSpanEventSimpleAroundInterceptor { + private final PLogger logger = PLoggerFactory.getLogger(this.getClass()); + private final boolean isDebug = logger.isDebugEnabled(); + + private final ClientRequestRecorder clientRequestRecorder; + private final RequestTraceWriter requestTraceWriter; + + public HttpClientHandlerRequestWithBodyInterceptor(TraceContext traceContext, MethodDescriptor methodDescriptor) { + super(traceContext, methodDescriptor); + + final ReactorNettyPluginConfig config = new ReactorNettyPluginConfig(traceContext.getProfilerConfig()); + final boolean param = config.isParam(); + final ClientRequestAdaptor clientRequestAdaptor = ClientRequestWrapperAdaptor.INSTANCE; + this.clientRequestRecorder = new ClientRequestRecorder(param, clientRequestAdaptor); + final HttpClientRequestHeaderAdaptor clientHeaderAdaptor = new HttpClientRequestHeaderAdaptor(); + this.requestTraceWriter = new DefaultRequestTraceWriter(clientHeaderAdaptor, traceContext); + } + + // BEFORE + @Override + public AsyncContext getAsyncContext(Object target, Object[] args) { + if (Boolean.FALSE == validate(args)) { + return null; + } + + final HttpClientRequest request = (HttpClientRequest) args[0]; + final AsyncContext asyncContext = AsyncContextAccessorUtils.getAsyncContext(target); + if (asyncContext == null) { + // Set sampling rate to false + this.requestTraceWriter.write(request); + return null; + } + return asyncContext; + } + + @Override + public void doInBeforeTrace(SpanEventRecorder recorder, AsyncContext asyncContext, Object target, Object[] args) { + final Trace trace = asyncContext.currentAsyncTraceObject(); + if (trace == null) { + if (logger.isWarnEnabled()) { + logger.warn("Unexpected error, Current async trace is null"); + } + return; + } + final TraceId nextId = trace.getTraceId().getNextTraceId(); + recorder.recordNextSpanId(nextId.getSpanId()); + recorder.recordServiceType(ReactorNettyConstants.REACTOR_NETTY_CLIENT); + + final HttpClientRequest request = (HttpClientRequest) args[0]; + final ClientRequestWrapper clientRequestWrapper = new HttpClientRequestWrapper(request); + this.requestTraceWriter.write(request, nextId, clientRequestWrapper.getDestinationId()); + + // Set HttpClientOptions + if (request instanceof AsyncContextAccessor) { + ((AsyncContextAccessor) request)._$PINPOINT$_setAsyncContext(asyncContext); + } + } + + // AFTER + @Override + public AsyncContext getAsyncContext(Object target, Object[] args, Object result, Throwable throwable) { + if (Boolean.FALSE == validate(args)) { + return null; + } + + return AsyncContextAccessorUtils.getAsyncContext(target); + } + + @Override + public void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) { + recorder.recordApi(methodDescriptor); + recorder.recordException(throwable); + + final HttpClientRequest request = (HttpClientRequest) args[0]; + final ClientRequestWrapper clientRequestWrapper = new HttpClientRequestWrapper(request); + this.clientRequestRecorder.record(recorder, clientRequestWrapper, throwable); + } + + private boolean validate(final Object[] args) { + if (args == null || args.length < 1) { + if (isDebug) { + logger.debug("Invalid args object. args={}.", args); + } + return false; + } + + if (!(args[0] instanceof HttpClientRequest)) { + if (isDebug) { + logger.debug("Invalid args[0] object. Need ClientHttpRequest, args[0]={}.", args[0]); + } + return false; + } + + return true; + } +} diff --git a/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientOperationsOnInboundNextInterceptor.java b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientOperationsOnInboundNextInterceptor.java new file mode 100644 index 000000000000..8c836b0903ab --- /dev/null +++ b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientOperationsOnInboundNextInterceptor.java @@ -0,0 +1,98 @@ +/* + * Copyright 2020 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.reactor.netty.interceptor; + +import com.navercorp.pinpoint.bootstrap.async.AsyncContextAccessorUtils; +import com.navercorp.pinpoint.bootstrap.context.AsyncContext; +import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor; +import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; +import com.navercorp.pinpoint.bootstrap.interceptor.AsyncContextSpanEventSimpleAroundInterceptor; +import com.navercorp.pinpoint.common.trace.AnnotationKey; +import com.navercorp.pinpoint.plugin.reactor.netty.ReactorNettyConstants; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; + +/** + * @author jaehong.kim + */ +public class HttpClientOperationsOnInboundNextInterceptor extends AsyncContextSpanEventSimpleAroundInterceptor { + public HttpClientOperationsOnInboundNextInterceptor(TraceContext traceContext, MethodDescriptor methodDescriptor) { + super(traceContext, methodDescriptor); + } + + // BEFORE + @Override + public AsyncContext getAsyncContext(Object target, Object[] args) { + final AsyncContext asyncContext = AsyncContextAccessorUtils.getAsyncContext(target); + if (asyncContext == null) { + return null; + } + if (Boolean.FALSE == validate(args)) { + return null; + } + return asyncContext; + } + + @Override + public void doInBeforeTrace(SpanEventRecorder recorder, AsyncContext asyncContext, Object target, Object[] args) { + final HttpResponse httpResponses = (HttpResponse) args[1]; + try { + final HttpResponseStatus httpResponseStatus = httpResponses.status(); + if (httpResponseStatus != null) { + recorder.recordAttribute(AnnotationKey.HTTP_STATUS_CODE, httpResponseStatus.code()); + } + } catch (Exception ignored) { + } + } + + // AFTER + @Override + public AsyncContext getAsyncContext(Object target, Object[] args, Object result, Throwable throwable) { + final AsyncContext asyncContext = AsyncContextAccessorUtils.getAsyncContext(target); + if (asyncContext == null) { + return null; + } + if (Boolean.FALSE == validate(args)) { + return null; + } + return asyncContext; + } + + @Override + public void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) { + recorder.recordApi(methodDescriptor); + recorder.recordException(throwable); + recorder.recordServiceType(ReactorNettyConstants.REACTOR_NETTY_CLIENT_INTERNAL); + } + + private boolean validate(final Object[] args) { + if (args == null || args.length < 2) { + if (isDebug) { + logger.debug("Invalid args object. args={}.", args); + } + return false; + } + + if (!(args[1] instanceof HttpResponse)) { + // Skip LastHttpContent or else. + return false; + } + + return true; + } +} diff --git a/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientOperationsOnOutboundCompleteInterceptor.java b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientOperationsOnOutboundCompleteInterceptor.java new file mode 100644 index 000000000000..3c3b1807eeb1 --- /dev/null +++ b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientOperationsOnOutboundCompleteInterceptor.java @@ -0,0 +1,45 @@ +/* + * Copyright 2020 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.reactor.netty.interceptor; + +import com.navercorp.pinpoint.bootstrap.context.AsyncContext; +import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor; +import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; +import com.navercorp.pinpoint.bootstrap.interceptor.AsyncContextSpanEventSimpleAroundInterceptor; +import com.navercorp.pinpoint.plugin.reactor.netty.ReactorNettyConstants; + +/** + * @author jaehong.kim + */ +public class HttpClientOperationsOnOutboundCompleteInterceptor extends AsyncContextSpanEventSimpleAroundInterceptor { + + public HttpClientOperationsOnOutboundCompleteInterceptor(TraceContext traceContext, MethodDescriptor methodDescriptor) { + super(traceContext, methodDescriptor); + } + + @Override + public void doInBeforeTrace(SpanEventRecorder recorder, AsyncContext asyncContext, Object target, Object[] args) { + } + + @Override + public void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) { + recorder.recordApi(methodDescriptor); + recorder.recordException(throwable); + recorder.recordServiceType(ReactorNettyConstants.REACTOR_NETTY_CLIENT_INTERNAL); + } +} diff --git a/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientOperationsOnOutboundErrorInterceptor.java b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientOperationsOnOutboundErrorInterceptor.java new file mode 100644 index 000000000000..da2117ab0a0f --- /dev/null +++ b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientOperationsOnOutboundErrorInterceptor.java @@ -0,0 +1,50 @@ +/* + * Copyright 2020 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.reactor.netty.interceptor; + +import com.navercorp.pinpoint.bootstrap.context.AsyncContext; +import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor; +import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; +import com.navercorp.pinpoint.bootstrap.interceptor.AsyncContextSpanEventSimpleAroundInterceptor; +import com.navercorp.pinpoint.plugin.reactor.netty.ReactorNettyConstants; + +/** + * @author jaehong.kim + */ +public class HttpClientOperationsOnOutboundErrorInterceptor extends AsyncContextSpanEventSimpleAroundInterceptor { + + public HttpClientOperationsOnOutboundErrorInterceptor(TraceContext traceContext, MethodDescriptor methodDescriptor) { + super(traceContext, methodDescriptor); + } + + @Override + public void doInBeforeTrace(SpanEventRecorder recorder, AsyncContext asyncContext, Object target, Object[] args) { + } + + @Override + public void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) { + recorder.recordApi(methodDescriptor); + recorder.recordServiceType(ReactorNettyConstants.REACTOR_NETTY_CLIENT_INTERNAL); + if (args != null && args.length >= 1 && args[0] instanceof Throwable) { + final Throwable t = (Throwable) args[0]; + recorder.recordException(t); + } else { + recorder.recordException(throwable); + } + } +} \ No newline at end of file diff --git a/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientRequestCookieExtractor.java b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientRequestCookieExtractor.java new file mode 100644 index 000000000000..033aebb64ab1 --- /dev/null +++ b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientRequestCookieExtractor.java @@ -0,0 +1,58 @@ +/* + * Copyright 2020 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.reactor.netty.interceptor; + +import com.navercorp.pinpoint.bootstrap.logging.PLogger; +import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory; +import com.navercorp.pinpoint.bootstrap.plugin.request.util.CookieExtractor; +import io.netty.handler.codec.http.cookie.Cookie; +import reactor.netty.http.client.HttpClientRequest; + +import java.util.Map; +import java.util.Set; + +/** + * @author jaehong.kim + */ +public class HttpClientRequestCookieExtractor implements CookieExtractor { + private final PLogger logger = PLoggerFactory.getLogger(this.getClass()); + private final boolean isDebug = logger.isDebugEnabled(); + + @Override + public String getCookie(final HttpClientRequest request) { + if (request != null && request.cookies() != null) { + final StringBuilder sb = new StringBuilder(); + for (Map.Entry> entry : request.cookies().entrySet()) { + boolean repeated = false; + for (Cookie httpCookie : entry.getValue()) { + if (repeated) { + sb.append(','); + } + sb.append(httpCookie.name()); + sb.append('='); + sb.append(httpCookie.value()); + repeated = true; + } + } + if (isDebug) { + logger.debug("Cookie={}", sb.toString()); + } + return sb.toString(); + } + return null; + } +} \ No newline at end of file diff --git a/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientRequestHeaderAdaptor.java b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientRequestHeaderAdaptor.java new file mode 100644 index 000000000000..b195e6d6a24e --- /dev/null +++ b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientRequestHeaderAdaptor.java @@ -0,0 +1,40 @@ +/* + * Copyright 2020 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.reactor.netty.interceptor; + +import com.navercorp.pinpoint.bootstrap.logging.PLogger; +import com.navercorp.pinpoint.bootstrap.logging.PLoggerFactory; +import com.navercorp.pinpoint.bootstrap.plugin.request.ClientHeaderAdaptor; +import reactor.netty.http.client.HttpClientRequest; + +/** + * @author jaehong.kim + */ +public class HttpClientRequestHeaderAdaptor implements ClientHeaderAdaptor { + private final PLogger logger = PLoggerFactory.getLogger(this.getClass()); + private final boolean isDebug = logger.isDebugEnabled(); + + @Override + public void setHeader(final HttpClientRequest request, final String name, final String value) { + if (request != null) { + request.addHeader(name, value); + if (isDebug) { + logger.debug("Set header {}={}", name, value); + } + } + } +} \ No newline at end of file diff --git a/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientRequestWrapper.java b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientRequestWrapper.java new file mode 100644 index 000000000000..d149ccfe7463 --- /dev/null +++ b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpClientRequestWrapper.java @@ -0,0 +1,81 @@ +/* + * Copyright 2020 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.reactor.netty.interceptor; + +import com.navercorp.pinpoint.bootstrap.plugin.request.ClientRequestWrapper; +import com.navercorp.pinpoint.bootstrap.plugin.util.SocketAddressUtils; +import com.navercorp.pinpoint.common.plugin.util.HostAndPort; +import reactor.netty.http.HttpOperations; +import reactor.netty.http.client.HttpClientRequest; + +import java.net.InetSocketAddress; + +/** + * @author jaehong.kim + */ +public class HttpClientRequestWrapper implements ClientRequestWrapper { + + private final HttpClientRequest request; + + public HttpClientRequestWrapper(final HttpClientRequest request) { + this.request = request; + } + + @Override + public String getDestinationId() { + if (request instanceof HttpOperations) { + try { + final HttpOperations httpOperations = (HttpOperations) request; + final InetSocketAddress inetSocketAddress = httpOperations.address(); + if (inetSocketAddress != null) { + final String hostName = SocketAddressUtils.getHostNameFirst(inetSocketAddress); + if (hostName != null) { + return HostAndPort.toHostAndPortString(hostName, inetSocketAddress.getPort()); + } + } + } catch (Exception ignored) { + } + } + + return "UNKNOWN"; + } + + @Override + public String getUrl() { + if (request instanceof HttpOperations) { + try { + final StringBuilder sb = new StringBuilder(); + final HttpOperations httpOperations = (HttpOperations) request; + final InetSocketAddress inetSocketAddress = httpOperations.address(); + if (inetSocketAddress != null) { + final String hostName = SocketAddressUtils.getHostNameFirst(inetSocketAddress); + if (hostName != null) { + sb.append(hostName).append(":").append(inetSocketAddress.getPort()); + } + } + final String uri = httpOperations.uri(); + if (uri != null) { + sb.append(uri); + } + return sb.toString(); + } catch (Exception ignored) { + } + } + + return null; + } +} diff --git a/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpTcpClientConnectInterceptor.java b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpTcpClientConnectInterceptor.java new file mode 100644 index 000000000000..a8818c0c47e5 --- /dev/null +++ b/plugins/reactor-netty/src/main/java/com/navercorp/pinpoint/plugin/reactor/netty/interceptor/HttpTcpClientConnectInterceptor.java @@ -0,0 +1,52 @@ +/* + * Copyright 2020 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.reactor.netty.interceptor; + +import com.navercorp.pinpoint.bootstrap.async.AsyncContextAccessor; +import com.navercorp.pinpoint.bootstrap.context.AsyncContext; +import com.navercorp.pinpoint.bootstrap.context.MethodDescriptor; +import com.navercorp.pinpoint.bootstrap.context.SpanEventRecorder; +import com.navercorp.pinpoint.bootstrap.context.TraceContext; +import com.navercorp.pinpoint.bootstrap.interceptor.SpanEventSimpleAroundInterceptorForPlugin; +import com.navercorp.pinpoint.plugin.reactor.netty.ReactorNettyConstants; + +/** + * @author jaehong.kim + */ +public class HttpTcpClientConnectInterceptor extends SpanEventSimpleAroundInterceptorForPlugin { + + public HttpTcpClientConnectInterceptor(TraceContext traceContext, MethodDescriptor descriptor) { + super(traceContext, descriptor); + } + + @Override + public void doInBeforeTrace(SpanEventRecorder recorder, Object target, Object[] args) throws Exception { + } + + @Override + public void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) throws Exception { + recorder.recordApi(methodDescriptor); + recorder.recordException(throwable); + recorder.recordServiceType(ReactorNettyConstants.REACTOR_NETTY_CLIENT_INTERNAL); + + if (result instanceof AsyncContextAccessor) { + // Set MonoHttpConnect + final AsyncContext asyncContext = recorder.recordNextAsyncContext(); + ((AsyncContextAccessor) result)._$PINPOINT$_setAsyncContext(asyncContext); + } + } +} diff --git a/plugins/reactor-netty/src/main/resources/META-INF/pinpoint/type-provider.yml b/plugins/reactor-netty/src/main/resources/META-INF/pinpoint/type-provider.yml index cb8fdbda8f4f..d6811a368868 100644 --- a/plugins/reactor-netty/src/main/resources/META-INF/pinpoint/type-provider.yml +++ b/plugins/reactor-netty/src/main/resources/META-INF/pinpoint/type-provider.yml @@ -6,3 +6,17 @@ serviceTypes: - code: 1141 name: 'REACTOR_NETTY_INTERNAL' desc: 'REACTOR_NETTY' + - code: 9154 + name: 'REACTOR_NETTY_CLIENT' + desc: 'REACTOR_NETTY' + property: + recordStatistics: true + matcher: + type: 'exact' + code: 40 # http.url + - code: 9155 + name: 'REACTOR_NETTY_CLIENT_INTERNAL' + desc: 'REACTOR_NETTY' + matcher: + type: 'exact' + code: 48 # http.internal.display \ No newline at end of file diff --git a/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/SpringWebFluxPlugin.java b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/SpringWebFluxPlugin.java index 69ba6449a9a4..5d82474d19ee 100644 --- a/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/SpringWebFluxPlugin.java +++ b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/SpringWebFluxPlugin.java @@ -48,7 +48,7 @@ public class SpringWebFluxPlugin implements ProfilerPlugin, MatchableTransformTe @Override public void setup(ProfilerPluginSetupContext context) { final SpringWebFluxPluginConfig config = new SpringWebFluxPluginConfig(context.getConfig()); - if (!config.isEnable()) { + if (Boolean.FALSE == config.isEnable()) { logger.info("{} disabled", this.getClass().getSimpleName()); return; } @@ -59,9 +59,12 @@ public void setup(ProfilerPluginSetupContext context) { transformTemplate.transform("org.springframework.web.server.adapter.DefaultServerWebExchange", ServerWebExchangeTransform.class); // Client - transformTemplate.transform("org.springframework.web.reactive.function.client.DefaultWebClient$DefaultRequestBodyUriSpec", DefaultWebClientTransform.class); - transformTemplate.transform("org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction", ExchangeFunctionTransform.class); - transformTemplate.transform("org.springframework.web.reactive.function.client.DefaultClientRequestBuilder$BodyInserterRequest", BodyInserterRequestTransform.class); + if (Boolean.TRUE == config.isClientEnable()) { + // If there is a conflict with Reactor-Netty, set it to false. + transformTemplate.transform("org.springframework.web.reactive.function.client.DefaultWebClient$DefaultRequestBodyUriSpec", DefaultWebClientTransform.class); + transformTemplate.transform("org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction", ExchangeFunctionTransform.class); + transformTemplate.transform("org.springframework.web.reactive.function.client.DefaultClientRequestBuilder$BodyInserterRequest", BodyInserterRequestTransform.class); + } } @Override diff --git a/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/SpringWebFluxPluginConfig.java b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/SpringWebFluxPluginConfig.java index 38e517389aee..df9d8a746280 100644 --- a/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/SpringWebFluxPluginConfig.java +++ b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/SpringWebFluxPluginConfig.java @@ -27,6 +27,7 @@ public class SpringWebFluxPluginConfig { private final boolean enable; private final boolean param; private final HttpDumpConfig httpDumpConfig; + private final boolean clientEnable; public SpringWebFluxPluginConfig(ProfilerConfig config) { if (config == null) { @@ -34,7 +35,9 @@ public SpringWebFluxPluginConfig(ProfilerConfig config) { } this.enable = config.readBoolean("profiler.spring.webflux.enable", true); + // Client + this.clientEnable = config.readBoolean("profiler.spring.webflux.client.enable", false); this.param = config.readBoolean("profiler.spring.webflux.client.param", true); boolean cookie = config.readBoolean("profiler.spring.webflux.client.cookie", false); DumpType cookieDumpType = config.readDumpType("profiler.spring.webflux.client.cookie.dumptype", DumpType.EXCEPTION); @@ -55,6 +58,10 @@ public HttpDumpConfig getHttpDumpConfig() { return httpDumpConfig; } + public boolean isClientEnable() { + return clientEnable; + } + @Override public String toString() { final StringBuilder sb = new StringBuilder("SpringWebFluxPluginConfig{"); diff --git a/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/DispatchHandlerInvokeHandlerMethodInterceptor.java b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/DispatchHandlerInvokeHandlerMethodInterceptor.java index 68cfd460b849..5ad7dd9c2324 100644 --- a/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/DispatchHandlerInvokeHandlerMethodInterceptor.java +++ b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/DispatchHandlerInvokeHandlerMethodInterceptor.java @@ -34,25 +34,39 @@ public DispatchHandlerInvokeHandlerMethodInterceptor(TraceContext traceContext, super(traceContext, methodDescriptor); } + // BEFORE @Override - protected void doInBeforeTrace(SpanEventRecorder recorder, AsyncContext asyncContext, Object target, Object[] args) { + public AsyncContext getAsyncContext(Object target, Object[] args) { + if (validate(args)) { + return AsyncContextAccessorUtils.getAsyncContext(args[0]); + } + return null; + } + + @Override + public void doInBeforeTrace(SpanEventRecorder recorder, AsyncContext asyncContext, Object target, Object[] args) { } + // AFTER @Override - protected AsyncContext getAsyncContext(Object target, Object[] args) { - if (args != null && args.length >= 1) { + public AsyncContext getAsyncContext(Object target, Object[] args, Object result, Throwable throwable) { + if (validate(args)) { return AsyncContextAccessorUtils.getAsyncContext(args[0]); } return null; } @Override - protected void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) { + public void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[] args, Object result, Throwable throwable) { recorder.recordApi(methodDescriptor); recorder.recordServiceType(SpringWebFluxConstants.SPRING_WEBFLUX); recorder.recordException(throwable); - final AsyncContext publisherAsyncContext = getAsyncContext(target, args); + if (Boolean.FALSE == validate(args)) { + return; + } + + final AsyncContext publisherAsyncContext = AsyncContextAccessorUtils.getAsyncContext(args[0]); if (publisherAsyncContext != null) { // Set AsyncContext to CoreSubscriber if (result instanceof AsyncContextAccessor) { @@ -63,4 +77,14 @@ protected void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[ } } } + + private boolean validate(final Object[] args) { + if (args == null || args.length < 1) { + if (isDebug) { + logger.debug("Invalid args object. args={}.", args); + } + return false; + } + return true; + } } \ No newline at end of file diff --git a/plugins/undertow/src/main/java/com/navercorp/pinpoint/plugin/undertow/interceptor/HttpServerExchangeAdaptor.java b/plugins/undertow/src/main/java/com/navercorp/pinpoint/plugin/undertow/interceptor/HttpServerExchangeAdaptor.java index d91862b503a1..a5b359677594 100644 --- a/plugins/undertow/src/main/java/com/navercorp/pinpoint/plugin/undertow/interceptor/HttpServerExchangeAdaptor.java +++ b/plugins/undertow/src/main/java/com/navercorp/pinpoint/plugin/undertow/interceptor/HttpServerExchangeAdaptor.java @@ -48,6 +48,7 @@ public String getRpcName(HttpServerExchange request) { public String getEndPoint(HttpServerExchange request) { final InetSocketAddress address = request.getDestinationAddress(); if (address != null) { + // TODO fix return HostAndPort.toHostAndPortString(SocketAddressUtils.getHostNameFirst(address), address.getPort()); } return "Unknown";