From 1c0d0701cf5c924b8f58d2106b59e42f8a4e9062 Mon Sep 17 00:00:00 2001 From: Seoyoung Park Date: Tue, 2 Apr 2024 15:04:31 +0900 Subject: [PATCH] [#10832] Backport: Support `getStatusCode()` for compatibility with Spring WebFlux 6.1 & Boot 3.x. [#NotAssigned] Removed getRawStatusCode() method for removal in Spring Webflux 6.1 [#10736] Separate dummy java class not to disturb spring-web dependency [#10736] Remove dummy classes after install the plugin [#10736] Use maven-clean-plugin instead of exec [#10736] Split spring-stub into separate modules --- plugins/assembly/pom.xml | 5 ++ plugins/pom.xml | 1 + plugins/resttemplate/pom.xml | 22 +++++++- .../resttemplate/RestTemplatePlugin.java | 7 ++- .../plugin/resttemplate/SpringVersion.java | 56 +++++++++++++++++++ .../ClientHttpResponseInterceptor.java | 9 ++- .../interceptor/HttpRequestInterceptor.java | 10 +++- .../ListenableFutureInterceptor.java | 14 +++-- .../interceptor/util/HttpStatusProvider.java | 23 ++++++++ .../util/HttpStatusProviderFactory.java | 38 +++++++++++++ .../util/Spring5HttpStatusProvider.java | 38 +++++++++++++ .../util/Spring6HttpStatusProvider.java | 38 +++++++++++++ .../util/UnsupportedHttpStatusProvider.java | 27 +++++++++ plugins/spring-stub/pom.xml | 46 +++++++++++++++ .../springframework/http/HttpStatusCode.java | 23 ++++++++ .../http/client/ClientHttpResponse.java | 31 ++++++++++ .../client/reactive/ClientHttpResponse.java | 30 ++++++++++ plugins/spring-webflux/pom.xml | 23 +++++++- .../plugin/spring/webflux/SpringVersion.java | 56 +++++++++++++++++++ .../spring/webflux/SpringWebFluxPlugin.java | 3 +- .../ClientResponseFunctionInterceptor.java | 11 +++- .../interceptor/util/HttpStatusProvider.java | 23 ++++++++ .../util/HttpStatusProviderFactory.java | 38 +++++++++++++ .../util/Spring5HttpStatusProvider.java | 36 ++++++++++++ .../util/Spring6HttpStatusProvider.java | 36 ++++++++++++ .../util/UnsupportedHttpStatusProvider.java | 27 +++++++++ 26 files changed, 653 insertions(+), 18 deletions(-) create mode 100644 plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/SpringVersion.java create mode 100644 plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/HttpStatusProvider.java create mode 100644 plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/HttpStatusProviderFactory.java create mode 100644 plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/Spring5HttpStatusProvider.java create mode 100644 plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/Spring6HttpStatusProvider.java create mode 100644 plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/UnsupportedHttpStatusProvider.java create mode 100644 plugins/spring-stub/pom.xml create mode 100644 plugins/spring-stub/src/main/java/org/springframework/http/HttpStatusCode.java create mode 100644 plugins/spring-stub/src/main/java/org/springframework/http/client/ClientHttpResponse.java create mode 100644 plugins/spring-stub/src/main/java/org/springframework/http/client/reactive/ClientHttpResponse.java create mode 100644 plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/SpringVersion.java create mode 100644 plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/HttpStatusProvider.java create mode 100644 plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/HttpStatusProviderFactory.java create mode 100644 plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/Spring5HttpStatusProvider.java create mode 100644 plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/Spring6HttpStatusProvider.java create mode 100644 plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/UnsupportedHttpStatusProvider.java diff --git a/plugins/assembly/pom.xml b/plugins/assembly/pom.xml index 545b664cea7e..281e547973b6 100644 --- a/plugins/assembly/pom.xml +++ b/plugins/assembly/pom.xml @@ -417,5 +417,10 @@ pinpoint-spring-tx-plugin ${project.version} + + + + + \ No newline at end of file diff --git a/plugins/pom.xml b/plugins/pom.xml index 252d9cbbaef9..8cf1f0d62e07 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -112,6 +112,7 @@ agentsdk-async spring-data-r2dbc spring-tx + spring-stub diff --git a/plugins/resttemplate/pom.xml b/plugins/resttemplate/pom.xml index 54b163413207..d87a7547b898 100644 --- a/plugins/resttemplate/pom.xml +++ b/plugins/resttemplate/pom.xml @@ -22,6 +22,12 @@ pinpoint-bootstrap-core provided + + com.navercorp.pinpoint + pinpoint-spring-stub + ${project.version} + provided + org.springframework @@ -29,5 +35,19 @@ provided - + + + + + org.apache.maven.plugins + maven-jar-plugin + + + com/navercorp/**/* + META-INF/**/* + + + + + \ No newline at end of file diff --git a/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/RestTemplatePlugin.java b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/RestTemplatePlugin.java index 988488f52e87..2995f4526996 100644 --- a/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/RestTemplatePlugin.java +++ b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/RestTemplatePlugin.java @@ -117,8 +117,10 @@ public static class HttpRequestTransformer implements TransformCallback { 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); InstrumentMethod executeMethod = InstrumentUtils.findMethod(target, "execute"); + + final int springVersion = SpringVersion.getVersion(classLoader); if (executeMethod != null) { - executeMethod.addScopedInterceptor(HttpRequestInterceptor.class, RestTemplateConstants.SCOPE, ExecutionPolicy.BOUNDARY); + executeMethod.addScopedInterceptor(HttpRequestInterceptor.class, va(springVersion), RestTemplateConstants.SCOPE, ExecutionPolicy.BOUNDARY); } return target.toBytecode(); @@ -167,8 +169,9 @@ public byte[] doInTransform(Instrumentor instrumentor, ClassLoader classLoader, final List constructors = target.getDeclaredConstructors(); if (constructors != null && constructors.size() == 1) { //only intercept one-constructor response, no overloading + final int springVersion = SpringVersion.getVersion(classLoader); for (InstrumentMethod constructor : constructors) { - constructor.addScopedInterceptor(ClientHttpResponseInterceptor.class, "HttpResponse", ExecutionPolicy.BOUNDARY); + constructor.addScopedInterceptor(ClientHttpResponseInterceptor.class, va(springVersion), "HttpResponse", ExecutionPolicy.BOUNDARY); } } diff --git a/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/SpringVersion.java b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/SpringVersion.java new file mode 100644 index 000000000000..aa53f4622528 --- /dev/null +++ b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/SpringVersion.java @@ -0,0 +1,56 @@ +package com.navercorp.pinpoint.plugin.resttemplate;/* + * Copyright 2024 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. + */ + +import java.util.Objects; + +/** + * @author intr3p1d + */ +public class SpringVersion { + + public static final int SPRING_VERSION_UNKNOWN = -1; + public static final int SPRING_VERSION_5 = 5_00_00; + public static final int SPRING_VERSION_6 = 6_00_00; + + static final String SPRING5_HTTP_STATUS_INTERFACE_NAME = "org.springframework.http.HttpStatus"; + static final String SPRING6_HTTP_STATUS_INTERFACE_NAME = "org.springframework.http.HttpStatusCode"; + + + public static int getVersion(ClassLoader classLoader) { + // Spring 6.0 + (boot 3.0 + ) + final Class httpStatusCode = getClass(classLoader, SPRING6_HTTP_STATUS_INTERFACE_NAME); + if (httpStatusCode != null) { + return SpringVersion.SPRING_VERSION_6; + } + + // ~ Spring 5.x (boot 2.0 -) + final Class httpStatus = getClass(classLoader, SPRING5_HTTP_STATUS_INTERFACE_NAME); + if (httpStatus != null) { + return SpringVersion.SPRING_VERSION_5; + } + return SpringVersion.SPRING_VERSION_UNKNOWN; + } + + + static Class getClass(ClassLoader classLoader, String className) { + Objects.requireNonNull(className, "className"); + try { + return Class.forName(className, false, classLoader); + } catch (ClassNotFoundException ignored) { + return null; + } + } +} diff --git a/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/ClientHttpResponseInterceptor.java b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/ClientHttpResponseInterceptor.java index 6233a858ceb4..1eb707482444 100644 --- a/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/ClientHttpResponseInterceptor.java +++ b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/ClientHttpResponseInterceptor.java @@ -25,6 +25,8 @@ import com.navercorp.pinpoint.common.trace.AnnotationKey; import com.navercorp.pinpoint.plugin.resttemplate.RestTemplateConstants; import com.navercorp.pinpoint.plugin.resttemplate.RestTemplateResponseHeaderAdaptor; +import com.navercorp.pinpoint.plugin.resttemplate.interceptor.util.HttpStatusProvider; +import com.navercorp.pinpoint.plugin.resttemplate.interceptor.util.HttpStatusProviderFactory; import org.springframework.http.client.ClientHttpResponse; import java.io.IOException; @@ -35,10 +37,13 @@ public class ClientHttpResponseInterceptor extends SpanEventSimpleAroundInterceptorForPlugin { private final ServerResponseHeaderRecorder responseHeaderRecorder; + private final HttpStatusProvider statusCodeProvider; - public ClientHttpResponseInterceptor(TraceContext traceContext, MethodDescriptor descriptor) { + public ClientHttpResponseInterceptor(TraceContext traceContext, MethodDescriptor descriptor, + int springVersion) { super(traceContext, descriptor); this.responseHeaderRecorder = ResponseHeaderRecorderFactory.newResponseHeaderRecorder(traceContext.getProfilerConfig(), new RestTemplateResponseHeaderAdaptor()); + this.statusCodeProvider = HttpStatusProviderFactory.getHttpStatusProvider(springVersion); } @Override @@ -53,7 +58,7 @@ protected void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[ if (target instanceof ClientHttpResponse) { ClientHttpResponse clientHttpResponse = (ClientHttpResponse) target; - recorder.recordAttribute(AnnotationKey.HTTP_STATUS_CODE, clientHttpResponse.getRawStatusCode()); + recorder.recordAttribute(AnnotationKey.HTTP_STATUS_CODE, statusCodeProvider.getStatusCode(clientHttpResponse)); this.responseHeaderRecorder.recordHeader(recorder, clientHttpResponse); } } diff --git a/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/HttpRequestInterceptor.java b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/HttpRequestInterceptor.java index 24e681695ec0..3c463e694426 100644 --- a/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/HttpRequestInterceptor.java +++ b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/HttpRequestInterceptor.java @@ -23,6 +23,8 @@ import com.navercorp.pinpoint.common.trace.AnnotationKey; import com.navercorp.pinpoint.plugin.resttemplate.RestTemplateConstants; +import com.navercorp.pinpoint.plugin.resttemplate.interceptor.util.HttpStatusProvider; +import com.navercorp.pinpoint.plugin.resttemplate.interceptor.util.HttpStatusProviderFactory; import org.springframework.http.client.ClientHttpResponse; import java.io.IOException; @@ -32,8 +34,12 @@ */ public class HttpRequestInterceptor extends SpanEventSimpleAroundInterceptorForPlugin { - public HttpRequestInterceptor(TraceContext traceContext, MethodDescriptor descriptor) { + private final HttpStatusProvider statusCodeProvider; + + public HttpRequestInterceptor(TraceContext traceContext, MethodDescriptor descriptor, + int springVersion) { super(traceContext, descriptor); + this.statusCodeProvider = HttpStatusProviderFactory.getHttpStatusProvider(springVersion); } @Override @@ -48,7 +54,7 @@ protected void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[ if (result instanceof ClientHttpResponse) { ClientHttpResponse clientHttpResponse = (ClientHttpResponse) result; - recorder.recordAttribute(AnnotationKey.HTTP_STATUS_CODE, clientHttpResponse.getRawStatusCode()); + recorder.recordAttribute(AnnotationKey.HTTP_STATUS_CODE, statusCodeProvider.getStatusCode(clientHttpResponse)); } } diff --git a/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/ListenableFutureInterceptor.java b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/ListenableFutureInterceptor.java index faaa9d840fb0..71b5be663f95 100644 --- a/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/ListenableFutureInterceptor.java +++ b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/ListenableFutureInterceptor.java @@ -27,6 +27,8 @@ import com.navercorp.pinpoint.plugin.resttemplate.RestTemplateConstants; import com.navercorp.pinpoint.plugin.resttemplate.RestTemplateResponseHeaderAdaptor; import com.navercorp.pinpoint.plugin.resttemplate.field.accessor.TraceFutureFlagAccessor; +import com.navercorp.pinpoint.plugin.resttemplate.interceptor.util.HttpStatusProvider; +import com.navercorp.pinpoint.plugin.resttemplate.interceptor.util.HttpStatusProviderFactory; import org.springframework.http.HttpStatus; import org.springframework.http.client.AbstractClientHttpResponse; import org.springframework.http.client.ClientHttpResponse; @@ -39,10 +41,13 @@ public class ListenableFutureInterceptor extends AsyncContextSpanEventSimpleAroundInterceptor { private final ServerResponseHeaderRecorder responseHeaderRecorder; + private final HttpStatusProvider statusCodeProvider; - public ListenableFutureInterceptor(MethodDescriptor methodDescriptor, TraceContext traceContext) { + public ListenableFutureInterceptor(MethodDescriptor methodDescriptor, TraceContext traceContext, + int springVersion) { super(traceContext, methodDescriptor); this.responseHeaderRecorder = ResponseHeaderRecorderFactory.newResponseHeaderRecorder(traceContext.getProfilerConfig(), new RestTemplateResponseHeaderAdaptor()); + this.statusCodeProvider = HttpStatusProviderFactory.getHttpStatusProvider(springVersion); } @Override @@ -89,12 +94,9 @@ protected void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[ if (args.length == 1 && args[0] instanceof AbstractClientHttpResponse) { AbstractClientHttpResponse response = (AbstractClientHttpResponse) args[0]; try { - HttpStatus statusCode = response.getStatusCode(); - if (statusCode != null) { - recorder.recordAttribute(AnnotationKey.HTTP_STATUS_CODE, statusCode.value()); - } + recorder.recordAttribute(AnnotationKey.HTTP_STATUS_CODE, statusCodeProvider.getStatusCode(response)); this.responseHeaderRecorder.recordHeader(recorder, response); - } catch (IOException ioException) { + } catch (Exception ioException) { logger.warn("Failed to after process. {}", ioException.getMessage(), ioException); } } diff --git a/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/HttpStatusProvider.java b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/HttpStatusProvider.java new file mode 100644 index 000000000000..17f8bc764e63 --- /dev/null +++ b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/HttpStatusProvider.java @@ -0,0 +1,23 @@ +/* + * Copyright 2024 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.resttemplate.interceptor.util; + +/** + * @author intr3p1d + */ +public interface HttpStatusProvider { + int getStatusCode(Object target); +} diff --git a/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/HttpStatusProviderFactory.java b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/HttpStatusProviderFactory.java new file mode 100644 index 000000000000..9e0565811cd3 --- /dev/null +++ b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/HttpStatusProviderFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright 2024 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.resttemplate.interceptor.util; + +import com.navercorp.pinpoint.plugin.resttemplate.SpringVersion; + +/** + * @author intr3p1d + */ +public class HttpStatusProviderFactory { + + public HttpStatusProviderFactory() { + } + + public static HttpStatusProvider getHttpStatusProvider(int version) { + switch (version) { + case SpringVersion.SPRING_VERSION_5: + return new Spring5HttpStatusProvider(); + case SpringVersion.SPRING_VERSION_6: + return new Spring6HttpStatusProvider(); + default: + return new UnsupportedHttpStatusProvider(); + } + } +} diff --git a/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/Spring5HttpStatusProvider.java b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/Spring5HttpStatusProvider.java new file mode 100644 index 000000000000..6d2985278ce0 --- /dev/null +++ b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/Spring5HttpStatusProvider.java @@ -0,0 +1,38 @@ +/* + * Copyright 2024 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.resttemplate.interceptor.util; + +import org.springframework.http.client.ClientHttpResponse; + +import java.io.IOException; + +/** + * @author intr3p1d + */ +public class Spring5HttpStatusProvider implements HttpStatusProvider { + @Override + public int getStatusCode(Object target) { + if (target instanceof ClientHttpResponse) { + final ClientHttpResponse response = (ClientHttpResponse) target; + try { + return response.getRawStatusCode(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return -1; + } +} diff --git a/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/Spring6HttpStatusProvider.java b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/Spring6HttpStatusProvider.java new file mode 100644 index 000000000000..723bfaae5e20 --- /dev/null +++ b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/Spring6HttpStatusProvider.java @@ -0,0 +1,38 @@ +/* + * Copyright 2024 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.resttemplate.interceptor.util; + +import org.springframework.http.client.ClientHttpResponse; + +import java.io.IOException; + +/** + * @author intr3p1d + */ +public class Spring6HttpStatusProvider implements HttpStatusProvider { + @Override + public int getStatusCode(Object target) { + if (target instanceof ClientHttpResponse) { + final ClientHttpResponse response = (ClientHttpResponse) target; + try { + return response.getStatusCode().value(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return -1; + } +} diff --git a/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/UnsupportedHttpStatusProvider.java b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/UnsupportedHttpStatusProvider.java new file mode 100644 index 000000000000..8ccb1a0e4890 --- /dev/null +++ b/plugins/resttemplate/src/main/java/com/navercorp/pinpoint/plugin/resttemplate/interceptor/util/UnsupportedHttpStatusProvider.java @@ -0,0 +1,27 @@ +/* + * Copyright 2024 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.resttemplate.interceptor.util; + +/** + * @author intr3p1d + */ +public class UnsupportedHttpStatusProvider implements HttpStatusProvider { + + @Override + public int getStatusCode(Object target) { + return -1; + } +} diff --git a/plugins/spring-stub/pom.xml b/plugins/spring-stub/pom.xml new file mode 100644 index 000000000000..137ef995b6fc --- /dev/null +++ b/plugins/spring-stub/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + com.navercorp.pinpoint + pinpoint-plugins + 2.5.4-SNAPSHOT + + + pinpoint-spring-stub + pinpoint-spring-stub + jar + + + 1.8 + ${env.JAVA_8_HOME} + ${env.JAVA_8_HOME} + ${spring5.version} + + + + + com.navercorp.pinpoint + pinpoint-bootstrap-core + provided + + + + org.springframework + spring-webflux + provided + + + org.springframework + spring-core + provided + + + org.springframework + spring-web + provided + + + + \ No newline at end of file diff --git a/plugins/spring-stub/src/main/java/org/springframework/http/HttpStatusCode.java b/plugins/spring-stub/src/main/java/org/springframework/http/HttpStatusCode.java new file mode 100644 index 000000000000..7259f84e8e3c --- /dev/null +++ b/plugins/spring-stub/src/main/java/org/springframework/http/HttpStatusCode.java @@ -0,0 +1,23 @@ +/* + * Copyright 2024 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 org.springframework.http; + +/** + * @author intr3p1d + */ +public interface HttpStatusCode { + int value(); +} diff --git a/plugins/spring-stub/src/main/java/org/springframework/http/client/ClientHttpResponse.java b/plugins/spring-stub/src/main/java/org/springframework/http/client/ClientHttpResponse.java new file mode 100644 index 000000000000..cb3c095ae3ad --- /dev/null +++ b/plugins/spring-stub/src/main/java/org/springframework/http/client/ClientHttpResponse.java @@ -0,0 +1,31 @@ +/* + * Copyright 2024 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 org.springframework.http.client; + +import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpStatusCode; + +import java.io.Closeable; +import java.io.IOException; + +/** + * @author intr3p1d + */ +public interface ClientHttpResponse extends HttpInputMessage, Closeable { + HttpStatusCode getStatusCode() throws IOException; + + int getRawStatusCode() throws IOException; +} \ No newline at end of file diff --git a/plugins/spring-stub/src/main/java/org/springframework/http/client/reactive/ClientHttpResponse.java b/plugins/spring-stub/src/main/java/org/springframework/http/client/reactive/ClientHttpResponse.java new file mode 100644 index 000000000000..983ff4fe05ad --- /dev/null +++ b/plugins/spring-stub/src/main/java/org/springframework/http/client/reactive/ClientHttpResponse.java @@ -0,0 +1,30 @@ +/* + * Copyright 2024 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 org.springframework.http.client.reactive; + +import org.springframework.http.HttpStatusCode; +import org.springframework.http.HttpInputMessage; + +import java.io.Closeable; + +/** + * @author intr3p1d + */ +public interface ClientHttpResponse extends HttpInputMessage, Closeable { + HttpStatusCode getStatusCode(); + + int getRawStatusCode(); +} diff --git a/plugins/spring-webflux/pom.xml b/plugins/spring-webflux/pom.xml index e9fd1297b3ef..8e82e772ea0f 100644 --- a/plugins/spring-webflux/pom.xml +++ b/plugins/spring-webflux/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 com.navercorp.pinpoint @@ -24,6 +24,12 @@ pinpoint-bootstrap-core provided + + com.navercorp.pinpoint + pinpoint-spring-stub + ${project.version} + provided + org.springframework @@ -38,4 +44,19 @@ + + + + + org.apache.maven.plugins + maven-jar-plugin + + + com/navercorp/**/* + META-INF/**/* + + + + + diff --git a/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/SpringVersion.java b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/SpringVersion.java new file mode 100644 index 000000000000..af243691416b --- /dev/null +++ b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/SpringVersion.java @@ -0,0 +1,56 @@ +package com.navercorp.pinpoint.plugin.spring.webflux;/* + * Copyright 2024 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. + */ + +import java.util.Objects; + +/** + * @author intr3p1d + */ +public class SpringVersion { + + public static final int SPRING_VERSION_UNKNOWN = -1; + public static final int SPRING_VERSION_5 = 5_00_00; + public static final int SPRING_VERSION_6 = 6_00_00; + + static final String SPRING5_HTTP_STATUS_INTERFACE_NAME = "org.springframework.http.HttpStatus"; + static final String SPRING6_HTTP_STATUS_INTERFACE_NAME = "org.springframework.http.HttpStatusCode"; + + + public static int getVersion(ClassLoader classLoader) { + // Spring 6.0 + (boot 3.0 + ) + final Class httpStatusCode = getClass(classLoader, SPRING6_HTTP_STATUS_INTERFACE_NAME); + if (httpStatusCode != null) { + return SpringVersion.SPRING_VERSION_6; + } + + // ~ Spring 5.x (boot 2.0 -) + final Class httpStatus = getClass(classLoader, SPRING5_HTTP_STATUS_INTERFACE_NAME); + if (httpStatus != null) { + return SpringVersion.SPRING_VERSION_5; + } + return SpringVersion.SPRING_VERSION_UNKNOWN; + } + + + static Class getClass(ClassLoader classLoader, String className) { + Objects.requireNonNull(className, "className"); + try { + return Class.forName(className, false, classLoader); + } catch (ClassNotFoundException ignored) { + return null; + } + } +} 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 13ee2610b32f..a23200761be8 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 @@ -190,7 +190,8 @@ public byte[] doInTransform(Instrumentor instrumentor, ClassLoader loader, Strin final InstrumentMethod logResponseMethod = target.getDeclaredMethod("logResponse", "org.springframework.http.client.reactive.ClientHttpResponse", "java.lang.String"); if (logResponseMethod != null) { - logResponseMethod.addInterceptor(ClientResponseFunctionInterceptor.class); + final int springVersion = SpringVersion.getVersion(loader); + logResponseMethod.addInterceptor(ClientResponseFunctionInterceptor.class, va(springVersion)); } return target.toBytecode(); diff --git a/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/ClientResponseFunctionInterceptor.java b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/ClientResponseFunctionInterceptor.java index d6c5381cf9ce..ae8382dad2c2 100644 --- a/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/ClientResponseFunctionInterceptor.java +++ b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/ClientResponseFunctionInterceptor.java @@ -26,6 +26,8 @@ import com.navercorp.pinpoint.plugin.spring.webflux.SpringWebFluxConstants; import com.navercorp.pinpoint.plugin.spring.webflux.SpringWebFluxResponseHeaderAdaptor; +import com.navercorp.pinpoint.plugin.spring.webflux.interceptor.util.HttpStatusProvider; +import com.navercorp.pinpoint.plugin.spring.webflux.interceptor.util.HttpStatusProviderFactory; import org.springframework.http.client.reactive.ClientHttpResponse; /** @@ -34,10 +36,13 @@ public class ClientResponseFunctionInterceptor extends SpanEventSimpleAroundInterceptorForPlugin { private final ServerResponseHeaderRecorder responseHeaderRecorder; + private final HttpStatusProvider statusCodeProvider; - public ClientResponseFunctionInterceptor(TraceContext traceContext, MethodDescriptor descriptor) { + public ClientResponseFunctionInterceptor(TraceContext traceContext, MethodDescriptor descriptor, + int springVersion) { super(traceContext, descriptor); this.responseHeaderRecorder = ResponseHeaderRecorderFactory.newResponseHeaderRecorder(traceContext.getProfilerConfig(), new SpringWebFluxResponseHeaderAdaptor()); + this.statusCodeProvider = HttpStatusProviderFactory.getHttpStatusProvider(springVersion); } @Override @@ -52,8 +57,8 @@ protected void doInAfterTrace(SpanEventRecorder recorder, Object target, Object[ if (args[0] instanceof ClientHttpResponse) { ClientHttpResponse response = (ClientHttpResponse) args[0]; - recorder.recordAttribute(AnnotationKey.HTTP_STATUS_CODE, response.getRawStatusCode()); + recorder.recordAttribute(AnnotationKey.HTTP_STATUS_CODE, statusCodeProvider.getStatusCode(response)); this.responseHeaderRecorder.recordHeader(recorder, response); } } -} \ No newline at end of file +} diff --git a/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/HttpStatusProvider.java b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/HttpStatusProvider.java new file mode 100644 index 000000000000..f983f65435d4 --- /dev/null +++ b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/HttpStatusProvider.java @@ -0,0 +1,23 @@ +/* + * Copyright 2024 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.spring.webflux.interceptor.util; + +/** + * @author intr3p1d + */ +public interface HttpStatusProvider { + int getStatusCode(Object target); +} diff --git a/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/HttpStatusProviderFactory.java b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/HttpStatusProviderFactory.java new file mode 100644 index 000000000000..e256b58998b7 --- /dev/null +++ b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/HttpStatusProviderFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright 2024 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.spring.webflux.interceptor.util; + +import com.navercorp.pinpoint.plugin.spring.webflux.SpringVersion; + +/** + * @author intr3p1d + */ +public class HttpStatusProviderFactory { + + public HttpStatusProviderFactory() { + } + + public static HttpStatusProvider getHttpStatusProvider(int version) { + switch (version) { + case SpringVersion.SPRING_VERSION_5: + return new Spring5HttpStatusProvider(); + case SpringVersion.SPRING_VERSION_6: + return new Spring6HttpStatusProvider(); + default: + return new UnsupportedHttpStatusProvider(); + } + } +} diff --git a/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/Spring5HttpStatusProvider.java b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/Spring5HttpStatusProvider.java new file mode 100644 index 000000000000..e307c01a30c6 --- /dev/null +++ b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/Spring5HttpStatusProvider.java @@ -0,0 +1,36 @@ +/* + * Copyright 2024 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.spring.webflux.interceptor.util; + +import org.springframework.http.client.reactive.ClientHttpResponse; + +/** + * @author intr3p1d + */ +public class Spring5HttpStatusProvider implements HttpStatusProvider { + @Override + public int getStatusCode(Object target) { + if (target instanceof ClientHttpResponse) { + final ClientHttpResponse response = (ClientHttpResponse) target; + try { + return response.getRawStatusCode(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return -1; + } +} diff --git a/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/Spring6HttpStatusProvider.java b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/Spring6HttpStatusProvider.java new file mode 100644 index 000000000000..d8c52a4d3e12 --- /dev/null +++ b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/Spring6HttpStatusProvider.java @@ -0,0 +1,36 @@ +/* + * Copyright 2024 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.spring.webflux.interceptor.util; + +import org.springframework.http.client.reactive.ClientHttpResponse; + +/** + * @author intr3p1d + */ +public class Spring6HttpStatusProvider implements HttpStatusProvider { + @Override + public int getStatusCode(Object target) { + if (target instanceof ClientHttpResponse) { + final ClientHttpResponse response = (ClientHttpResponse) target; + try { + return response.getStatusCode().value(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return -1; + } +} diff --git a/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/UnsupportedHttpStatusProvider.java b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/UnsupportedHttpStatusProvider.java new file mode 100644 index 000000000000..35c5c0b5eb73 --- /dev/null +++ b/plugins/spring-webflux/src/main/java/com/navercorp/pinpoint/plugin/spring/webflux/interceptor/util/UnsupportedHttpStatusProvider.java @@ -0,0 +1,27 @@ +/* + * Copyright 2024 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.spring.webflux.interceptor.util; + +/** + * @author intr3p1d + */ +public class UnsupportedHttpStatusProvider implements HttpStatusProvider { + + @Override + public int getStatusCode(Object target) { + return -1; + } +}