diff --git a/.github/workflows/plugins-test.3.yaml b/.github/workflows/plugins-test.3.yaml index 8fce605da..0629de603 100644 --- a/.github/workflows/plugins-test.3.yaml +++ b/.github/workflows/plugins-test.3.yaml @@ -68,6 +68,7 @@ jobs: - vertx-web-3.54minus-scenario - vertx-web-3.6plus-scenario - mariadb-scenario + - micronaut-http-scenario - quasar-scenario - baidu-brpc-scenario - retransform-class-scenario diff --git a/CHANGES.md b/CHANGES.md index 20e0b3516..3cf412f66 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,7 @@ Release Notes. * Fix race condition causing agent to not reconnect after network error * Force the injected high-priority classes in order to avoid NoClassDefFoundError. * Plugin to support xxl-job 2.3.x. +* Add plugin to support Micronaut(HTTP Client/Server) 3.2.x-3.6.x #### Documentation diff --git a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java index c3671ff69..23d7f9f1d 100755 --- a/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java +++ b/apm-protocol/apm-network/src/main/java/org/apache/skywalking/apm/network/trace/component/ComponentsDefine.java @@ -224,4 +224,7 @@ public class ComponentsDefine { public static final OfficialComponent APACHE_SHENYU = new OfficialComponent(127, "Apache-ShenYu"); public static final OfficialComponent HUTOOL = new OfficialComponent(128, "Hutool"); + + public static final OfficialComponent MICRONAUT = new OfficialComponent(131, "Micronaut"); + } diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/pom.xml new file mode 100644 index 000000000..1fb5f2804 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/pom.xml @@ -0,0 +1,53 @@ + + + + + + org.apache.skywalking + micronaut-plugins + 8.12.0-SNAPSHOT + + 4.0.0 + + micronaut-http-client-plugin + + + 8 + 8 + + + + + io.projectreactor + reactor-core + 3.4.21 + provided + + + + io.micronaut + micronaut-http-client + 3.6.0 + provided + + + + \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/client/BuildExchangeStreamPublisherInterceptor.java b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/client/BuildExchangeStreamPublisherInterceptor.java new file mode 100644 index 000000000..0eb23c51e --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/client/BuildExchangeStreamPublisherInterceptor.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.micronaut.http.client; + +import io.micronaut.http.HttpResponse; +import io.micronaut.http.MutableHttpRequest; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.reactivestreams.Publisher; +import java.lang.reflect.Method; +import java.net.URI; +import java.util.function.Function; +import static org.apache.skywalking.apm.plugin.micronaut.http.client.MicronautCommons.beginTrace; +import static org.apache.skywalking.apm.plugin.micronaut.http.client.MicronautCommons.buildTracePublisher; +import static org.apache.skywalking.apm.plugin.micronaut.http.client.MicronautCommons.finish; + +public class BuildExchangeStreamPublisherInterceptor implements InstanceMethodsAroundInterceptor { + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + MutableHttpRequest request = (MutableHttpRequest) allArguments[1]; + Function>> function = (Function>>) ret; + return (Function>>) uri -> { + beginTrace(request, uri); + return buildTracePublisher(request, function.apply(uri)); + }; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + MutableHttpRequest request = (MutableHttpRequest) allArguments[1]; + finish(request, span -> span.log(t).errorOccurred()); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/client/ExchangeImplInterceptor.java b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/client/ExchangeImplInterceptor.java new file mode 100644 index 000000000..eb8dd1bcb --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/client/ExchangeImplInterceptor.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.micronaut.http.client; + +import io.micronaut.http.HttpResponse; +import io.micronaut.http.MutableHttpRequest; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.reactivestreams.Publisher; + +import java.lang.reflect.Method; +import java.net.URI; + +import static org.apache.skywalking.apm.plugin.micronaut.http.client.MicronautCommons.finish; +import static org.apache.skywalking.apm.plugin.micronaut.http.client.MicronautCommons.buildTracePublisher; +import static org.apache.skywalking.apm.plugin.micronaut.http.client.MicronautCommons.beginTrace; + +public class ExchangeImplInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + MutableHttpRequest request = (MutableHttpRequest) allArguments[2]; + URI requestURI = (URI) allArguments[0]; + beginTrace(request, requestURI); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + MutableHttpRequest request = (MutableHttpRequest) allArguments[2]; + Publisher> retPublisher = (Publisher>) ret; + return buildTracePublisher(request, retPublisher); + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + finish((MutableHttpRequest) allArguments[0], span -> span.errorOccurred().log(t)); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/client/MicronautCommons.java b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/client/MicronautCommons.java new file mode 100644 index 000000000..e71d5b1e8 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/client/MicronautCommons.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.micronaut.http.client; + +import io.micronaut.http.HttpResponse; +import io.micronaut.http.MutableHttpRequest; +import io.micronaut.http.context.ServerRequestContext; +import org.apache.skywalking.apm.agent.core.context.CarrierItem; +import org.apache.skywalking.apm.agent.core.context.ContextCarrier; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.util.StringUtil; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Flux; + +import java.net.URI; +import java.util.function.Consumer; + +public class MicronautCommons { + + private static final String SPAN_KEY = "CORS_SPAN"; + private static final String SKY_CONTEXT_SNAPSHOT_KEY = "CORS_SNAPSHOT"; + + static void beginTrace(MutableHttpRequest request, URI requestURI) { + String requestMethod = request.getMethod().name(); + AbstractSpan span = ContextManager.createExitSpan(requestMethod + ":" + request.getPath(), requestURI.getHost() + ":" + requestURI.getPort()); + //The key is set in micronaut-http-server-plugin . See org.apache.skywalking.apm.plugin.micronaut.http.server#beginTrace + ServerRequestContext.currentRequest().flatMap(req -> req.getAttribute(SKY_CONTEXT_SNAPSHOT_KEY)).ifPresent(e -> ContextManager.continued((ContextSnapshot) e)); + final ContextCarrier contextCarrier = new ContextCarrier(); + ContextManager.inject(contextCarrier); + span.setComponent(ComponentsDefine.MICRONAUT); + Tags.HTTP.METHOD.set(span, requestMethod); + Tags.URL.set(span, String.format("%s://%s:%s%s", requestURI.getScheme(), requestURI.getHost(), requestURI.getPort(), requestURI.getPath())); + SpanLayer.asHttp(span); + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + request.header(next.getHeadKey(), next.getHeadValue()); + } + span.prepareForAsync(); + ContextManager.stopSpan(span); + request.setAttribute(SPAN_KEY, span); + if (MicronautHttpClientPluginConfig.Plugin.MicronautHttpClient.COLLECT_HTTP_PARAMS) { + collectHttpParam(request, span); + } + } + + static Publisher> buildTracePublisher(MutableHttpRequest request, Publisher> retPublisher) { + return Flux.from(retPublisher).doOnError(ex -> finish(request, span -> span.log(ex).errorOccurred())) + .doOnNext(resp -> + finish(request, span -> { + Tags.HTTP_RESPONSE_STATUS_CODE.set(span, resp.code()); + if (resp.code() >= 400) { + span.errorOccurred(); + } + //Activate HTTP parameters collecting automatically in the profiling context. + if (!MicronautHttpClientPluginConfig.Plugin.MicronautHttpClient.COLLECT_HTTP_PARAMS && span.isProfiling()) { + collectHttpParam(request, span); + } + }) + ); + } + + static void collectHttpParam(MutableHttpRequest httpRequest, AbstractSpan span) { + String tag = httpRequest.getUri().getQuery(); + tag = MicronautHttpClientPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD > 0 ? + StringUtil.cut(tag, MicronautHttpClientPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD) : tag; + if (StringUtil.isNotEmpty(tag)) { + Tags.HTTP.PARAMS.set(span, tag); + } + } + + static void finish(MutableHttpRequest request, Consumer action) { + try { + request.getAttribute(SPAN_KEY) + .map(span -> (AbstractSpan) span) + .ifPresent(span -> { + action.accept(span); + span.asyncFinish(); + }); + } finally { + request.removeAttribute(SPAN_KEY, AbstractSpan.class); + } + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/client/MicronautHttpClientPluginConfig.java b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/client/MicronautHttpClientPluginConfig.java new file mode 100644 index 000000000..54f3aca77 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/client/MicronautHttpClientPluginConfig.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.micronaut.http.client; + +import org.apache.skywalking.apm.agent.core.boot.PluginConfig; + +public class MicronautHttpClientPluginConfig { + + public static class Plugin { + @PluginConfig(root = MicronautHttpClientPluginConfig.class) + public static class MicronautHttpClient { + /** + * This config item controls that whether the HttpClient plugin should collect the parameters of the request. + */ + public static boolean COLLECT_HTTP_PARAMS = false; + } + + @PluginConfig(root = MicronautHttpClientPluginConfig.class) + public static class Http { + /** + * When either {@link MicronautHttpClient#COLLECT_HTTP_PARAMS} is enabled, how many characters to keep and send to the + * OAP backend, use negative values to keep and send the complete parameters, NB. this config item is added + * for the sake of performance + */ + public static int HTTP_PARAMS_LENGTH_THRESHOLD = 1024; + } + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/client/define/MicronautClientInstrumentation.java b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/client/define/MicronautClientInstrumentation.java new file mode 100644 index 000000000..202a75b65 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/client/define/MicronautClientInstrumentation.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.micronaut.http.client.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +public class MicronautClientInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "io.micronaut.http.client.netty.DefaultHttpClient"; + private static final String CLIENT_EXCHANGE_INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.micronaut.http.client.ExchangeImplInterceptor"; + private static final String STREAM_EXCHANGE_INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.micronaut.http.client.BuildExchangeStreamPublisherInterceptor"; + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + //microaut 3.4.4-3.6.x (As of now, the latest version is 3.6.0) + @Override + public ElementMatcher getMethodsMatcher() { + return named("exchangeImpl").and(takesArguments(5)); + } + + @Override + public String getMethodsInterceptor() { + return CLIENT_EXCHANGE_INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + // microaut 3.2.0-3.4.3 + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("buildExchangePublisher").and(takesArguments(4)); + } + + @Override + public String getMethodsInterceptor() { + return STREAM_EXCHANGE_INTERCEPT_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return byName(ENHANCE_CLASS); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 000000000..560b24df1 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-client-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,17 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +micronaut-http-client-3.2.x-3.6.x=org.apache.skywalking.apm.plugin.micronaut.http.client.define.MicronautClientInstrumentation diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/pom.xml new file mode 100644 index 000000000..4b282e46f --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/pom.xml @@ -0,0 +1,55 @@ + + + + + + org.apache.skywalking + micronaut-plugins + 8.12.0-SNAPSHOT + + 4.0.0 + + micronaut-http-server-plugin + jar + + + 8 + 8 + + + + + io.micronaut + micronaut-http + 3.6.0 + provided + + + + io.micronaut + micronaut-http-server-netty + 3.6.0 + provided + + + + + \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/HandleStatusErrorInterceptor.java b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/HandleStatusErrorInterceptor.java new file mode 100644 index 000000000..094c2d108 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/HandleStatusErrorInterceptor.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.micronaut.http.server; + +import io.micronaut.http.server.netty.NettyHttpRequest; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +import java.lang.reflect.Method; + +import static org.apache.skywalking.apm.plugin.micronaut.http.server.MicronautCommons.cleanup; +import static org.apache.skywalking.apm.plugin.micronaut.http.server.MicronautCommons.beginTrace; + +public class HandleStatusErrorInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + beginTrace((NettyHttpRequest) allArguments[1]); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + cleanup((NettyHttpRequest) allArguments[1]); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/MicronautCommons.java b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/MicronautCommons.java new file mode 100644 index 000000000..f3b24f769 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/MicronautCommons.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.micronaut.http.server; + +import io.micronaut.http.HttpRequest; +import org.apache.skywalking.apm.agent.core.context.CarrierItem; +import org.apache.skywalking.apm.agent.core.context.ContextCarrier; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.ContextSnapshot; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; +import org.apache.skywalking.apm.util.StringUtil; + +class MicronautCommons { + static final String SPAN_KEY = "CORS_SPAN"; + static final String SKY_CONTEXT_SNAPSHOT_KEY = "CORS_SNAPSHOT"; + + static void collectHttpParam(HttpRequest httpRequest, AbstractSpan span) { + String tag = httpRequest.getUri().getQuery(); + tag = MicronautHttpServerPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD > 0 ? + StringUtil.cut(tag, MicronautHttpServerPluginConfig.Plugin.Http.HTTP_PARAMS_LENGTH_THRESHOLD) : tag; + if (StringUtil.isNotEmpty(tag)) { + Tags.HTTP.PARAMS.set(span, tag); + } + } + + static void cleanup(HttpRequest request) { + request.removeAttribute(MicronautCommons.SKY_CONTEXT_SNAPSHOT_KEY, ContextSnapshot.class); + request.removeAttribute(MicronautCommons.SPAN_KEY, AbstractSpan.class); + } + + static void beginTrace(HttpRequest request) { + ContextCarrier contextCarrier = new ContextCarrier(); + CarrierItem next = contextCarrier.items(); + while (next.hasNext()) { + next = next.next(); + next.setHeadValue(request.getHeaders().get(next.getHeadKey())); + } + String operationName = String.join(":", request.getMethod(), request.getPath()); + AbstractSpan span = ContextManager.createEntrySpan(operationName, contextCarrier); + ContextSnapshot capture = ContextManager.capture(); + request.setAttribute(SPAN_KEY, span); + request.setAttribute(SKY_CONTEXT_SNAPSHOT_KEY, capture); + Tags.URL.set(span, String.format("%s://%s:%s%s", request.isSecure() ? "https" : "http", request.getServerName(), request.getServerAddress().getPort(), request.getUri().getPath())); + Tags.HTTP.METHOD.set(span, request.getMethodName()); + span.setComponent(ComponentsDefine.MICRONAUT); + SpanLayer.asHttp(span); + span.prepareForAsync(); + ContextManager.stopSpan(span); + if (MicronautHttpServerPluginConfig.Plugin.MicronautHttpServer.COLLECT_HTTP_PARAMS) { + collectHttpParam(request, span); + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/MicronautHttpServerPluginConfig.java b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/MicronautHttpServerPluginConfig.java new file mode 100644 index 000000000..5abbf2227 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/MicronautHttpServerPluginConfig.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.micronaut.http.server; + +import org.apache.skywalking.apm.agent.core.boot.PluginConfig; + +public class MicronautHttpServerPluginConfig { + + public static class Plugin { + @PluginConfig(root = MicronautHttpServerPluginConfig.class) + public static class MicronautHttpServer { + /** + * This config item controls that whether the Http plugin should collect the parameters of the request. + */ + public static boolean COLLECT_HTTP_PARAMS = false; + } + + @PluginConfig(root = MicronautHttpServerPluginConfig.class) + public static class Http { + /** + * When either {@link MicronautHttpServer#COLLECT_HTTP_PARAMS} is enabled, how many characters to keep and send to the + * OAP backend, use negative values to keep and send the complete parameters, NB. this config item is added + * for the sake of performance + */ + public static int HTTP_PARAMS_LENGTH_THRESHOLD = 1024; + } + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/RouteMatchExecuteInterceptor.java b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/RouteMatchExecuteInterceptor.java new file mode 100644 index 000000000..4f4891857 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/RouteMatchExecuteInterceptor.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.micronaut.http.server; + +import io.micronaut.http.context.ServerRequestContext; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +import java.lang.reflect.Method; + +public class RouteMatchExecuteInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + ServerRequestContext.currentRequest().ifPresent(MicronautCommons::beginTrace); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + ServerRequestContext.currentRequest().ifPresent(MicronautCommons::cleanup); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/WriteFinalNettyResponseInterceptor.java b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/WriteFinalNettyResponseInterceptor.java new file mode 100644 index 000000000..f6b9113f8 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/WriteFinalNettyResponseInterceptor.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.micronaut.http.server; + +import io.micronaut.http.HttpRequest; +import io.micronaut.http.MutableHttpResponse; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; + +import java.lang.reflect.Method; + +import static org.apache.skywalking.apm.plugin.micronaut.http.server.MicronautCommons.cleanup; +import static org.apache.skywalking.apm.plugin.micronaut.http.server.MicronautCommons.collectHttpParam; + +public class WriteFinalNettyResponseInterceptor implements InstanceMethodsAroundInterceptor { + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + MutableHttpResponse message = (MutableHttpResponse) allArguments[0]; + HttpRequest request = (HttpRequest) allArguments[1]; + request.getAttribute(MicronautCommons.SPAN_KEY) + .map(span -> (AbstractSpan) span) + .ifPresent(span -> { + int code = message.status().getCode(); + Tags.HTTP_RESPONSE_STATUS_CODE.set(span, code); + if (code >= 400) { + span.errorOccurred(); + } + //Activate HTTP parameters collecting automatically in the profiling context. + if (!MicronautHttpServerPluginConfig.Plugin.MicronautHttpServer.COLLECT_HTTP_PARAMS && span.isProfiling()) { + collectHttpParam((HttpRequest) allArguments[1], span); + } + span.asyncFinish(); + }); + cleanup(request); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + cleanup((HttpRequest) allArguments[1]); + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/define/RouteMatchInstrumentation.java b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/define/RouteMatchInstrumentation.java new file mode 100644 index 000000000..ff8445556 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/define/RouteMatchInstrumentation.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.micronaut.http.server.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +public class RouteMatchInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String EXECUTE_INTERCEPTOR = "org.apache.skywalking.apm.plugin.micronaut.http.server.RouteMatchExecuteInterceptor"; + + private static final String ROUTE_MATCH_CLASS_NAME = "io.micronaut.web.router.AbstractRouteMatch"; + + @Override + protected ClassMatch enhanceClass() { + return byName(ROUTE_MATCH_CLASS_NAME); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("execute").and(takesArguments(1)); + } + + @Override + public String getMethodsInterceptor() { + return EXECUTE_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/define/RoutingInBoundHandlerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/define/RoutingInBoundHandlerInstrumentation.java new file mode 100644 index 000000000..3eef57aea --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/java/org/apache/skywalking/apm/plugin/micronaut/http/server/define/RoutingInBoundHandlerInstrumentation.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.micronaut.http.server.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine; +import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +public class RoutingInBoundHandlerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String CLASS_NAME = "io.micronaut.http.server.netty.RoutingInBoundHandler"; + + private static final String FINAL_RESPONSE_INTERCEPTOR = "org.apache.skywalking.apm.plugin.micronaut.http.server.WriteFinalNettyResponseInterceptor"; + + private static final String HANDLE_ERROR_INTERCEPTOR = "org.apache.skywalking.apm.plugin.micronaut.http.server.HandleStatusErrorInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return byName(CLASS_NAME); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("handleStatusError").and(takesArguments(4)); + } + + @Override + public String getMethodsInterceptor() { + return HANDLE_ERROR_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("writeFinalNettyResponse").and(takesArguments(3)); + } + + @Override + public String getMethodsInterceptor() { + return FINAL_RESPONSE_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 000000000..36bd74e91 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/micronaut-http-server-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +micronaut-http-server-3.2.x-3.6.x=org.apache.skywalking.apm.plugin.micronaut.http.server.define.RouteMatchInstrumentation +micronaut-http-server-3.2.x-3.6.x=org.apache.skywalking.apm.plugin.micronaut.http.server.define.RoutingInBoundHandlerInstrumentation \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/micronaut-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/pom.xml new file mode 100644 index 000000000..2ce0a6b9e --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/micronaut-plugins/pom.xml @@ -0,0 +1,42 @@ + + + + + apm-sdk-plugin + org.apache.skywalking + 8.12.0-SNAPSHOT + + 4.0.0 + + micronaut-plugins + pom + + + UTF-8 + /.. + + + + micronaut-http-server-plugin + micronaut-http-client-plugin + + + diff --git a/apm-sniffer/apm-sdk-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/pom.xml index 93fabad88..538162693 100644 --- a/apm-sniffer/apm-sdk-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/pom.xml @@ -122,6 +122,7 @@ tomcat-thread-pool-plugin guava-eventbus-plugin hutool-plugins + micronaut-plugins pom diff --git a/docs/en/setup/service-agent/java-agent/Plugin-list.md b/docs/en/setup/service-agent/java-agent/Plugin-list.md index 3029b158a..60b9c2d10 100644 --- a/docs/en/setup/service-agent/java-agent/Plugin-list.md +++ b/docs/en/setup/service-agent/java-agent/Plugin-list.md @@ -141,3 +141,5 @@ - shenyu-2.4.x - jdk-threadpool-plugin - hutool-http-5.x +- micronaut-http-client-3.2.x-3.6.x +- micronaut-http-server-3.2.x-3.6.x diff --git a/docs/en/setup/service-agent/java-agent/Supported-list.md b/docs/en/setup/service-agent/java-agent/Supported-list.md index 98cdc7658..7ac7c1826 100644 --- a/docs/en/setup/service-agent/java-agent/Supported-list.md +++ b/docs/en/setup/service-agent/java-agent/Supported-list.md @@ -20,6 +20,7 @@ metrics based on the tracing data. * [Play Framework](https://www.playframework.com/) 2.6.x -> 2.8.x * [Light4J Microservices Framework](https://doc.networknt.com/) 1.6.x -> 2.x * [Netty SocketIO](https://github.com/mrniko/netty-socketio) 1.x + * [Micrinaut Http Server](https://github.com/micronaut-projects/micronaut-core) 3.2.x -> 3.6.x * HTTP Client * [Feign](https://github.com/OpenFeign/feign) 9.x * [Netflix Spring Cloud Feign](https://github.com/spring-cloud/spring-cloud-openfeign) 1.1.x -> 2.x @@ -31,6 +32,7 @@ metrics based on the tracing data. * [AsyncHttpClient](https://github.com/AsyncHttpClient/async-http-client) 2.1+ * JRE HttpURLConnection (Optional²) * [Hutool-http](https://www.hutool.cn/) client 5.x + * [Micrinaut Http Client](https://github.com/micronaut-projects/micronaut-core) 3.2.x -> 3.6.x * HTTP Gateway * [Spring Cloud Gateway](https://spring.io/projects/spring-cloud-gateway) 2.0.2.RELEASE -> 3.x (Optional²) * [Apache ShenYu](https://shenyu.apache.org) (Rich protocol support: `HTTP`,`Spring Cloud`,`gRPC`,`Dubbo`,`SOFARPC`,`Motan`,`Tars`) 2.4.x (Optional²) diff --git a/docs/en/setup/service-agent/java-agent/configurations.md b/docs/en/setup/service-agent/java-agent/configurations.md index 54562d7ee..c729c9de1 100644 --- a/docs/en/setup/service-agent/java-agent/configurations.md +++ b/docs/en/setup/service-agent/java-agent/configurations.md @@ -111,6 +111,8 @@ This is the properties list supported in `agent/config/agent.config`. | `plugin.neo4j.cypher_parameters_max_length` | If set to positive number, the `db.cypher.parameters` would be truncated to this length, otherwise it would be completely saved, which may cause performance problem. | SW_PLUGIN_NEO4J_CYPHER_PARAMETERS_MAX_LENGTH | `512` | | `plugin.neo4j.cypher_body_max_length` | If set to positive number, the `db.statement` would be truncated to this length, otherwise it would be completely saved, which may cause performance problem. | SW_PLUGIN_NEO4J_CYPHER_BODY_MAX_LENGTH | `2048` | | `plugin.cpupolicy.sample_cpu_usage_percent_limit` | If set to a positive number and activate `trace sampler CPU policy plugin`, the trace would not be collected when agent process CPU usage percent is greater than `plugin.cpupolicy.sample_cpu_usage_percent_limit`. | SW_SAMPLE_CPU_USAGE_PERCENT_LIMIT | `-1` | +| `plugin.micronauthttpclient.collect_http_params` | This config item controls that whether the Micronaut http client plugin should collect the parameters of the request. Also, activate implicitly in the profiled trace. | SW_PLUGIN_MICRONAUTHTTPCLIENT_COLLECT_HTTP_PARAMS | `false` | +| `plugin.micronauthttpserver.collect_http_params` | This config item controls that whether the Micronaut http server plugin should collect the parameters of the request. Also, activate implicitly in the profiled trace. | SW_PLUGIN_MICRONAUTHTTPSERVER_COLLECT_HTTP_PARAMS | `false` | # Dynamic Configurations All configurations above are static, if you need to change some agent settings at runtime, please read [CDS - Configuration Discovery Service document](configuration-discovery.md) for more details. diff --git a/test/plugin/scenarios/micronaut-http-scenario/bin/startup.sh b/test/plugin/scenarios/micronaut-http-scenario/bin/startup.sh new file mode 100644 index 000000000..815d657d9 --- /dev/null +++ b/test/plugin/scenarios/micronaut-http-scenario/bin/startup.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" + +java -jar ${agent_opts} "-Dskywalking.agent.service_name=micronaut-scenario-8081" "-Dmicronaut.server.port=8081" "-Dskywalking.plugin.micronauthttpclient.collect_http_params=true" "-Dskywalking.plugin.micronauthttpserver.collect_http_params=true" ${home}/../libs/micronaut-http-scenario.jar & +java -jar ${agent_opts} "-Dskywalking.agent.service_name=micronaut-scenario-8080" "-Dmicronaut.server.port=8080" "-Dskywalking.plugin.micronauthttpclient.collect_http_params=true" "-Dskywalking.plugin.micronauthttpserver.collect_http_params=true" ${home}/../libs/micronaut-http-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/micronaut-http-scenario/config/expectedData.yaml b/test/plugin/scenarios/micronaut-http-scenario/config/expectedData.yaml new file mode 100644 index 000000000..2530b2ba9 --- /dev/null +++ b/test/plugin/scenarios/micronaut-http-scenario/config/expectedData.yaml @@ -0,0 +1,191 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +segmentItems: + - serviceName: micronaut-scenario-8080 + segmentSize: ge 4 + segments: + - segmentId: not null + spans: + - operationName: GET:/micronaut/success + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 131 + isError: false + spanType: Exit + peer: localhost:8081 + skipAnalysis: false + tags: + - {key: http.method, value: GET} + - {key: url, value: 'http://localhost:8081/micronaut/success'} + - {key: http.params, value: a=1&b=2} + - {key: http.status_code, value: '200'} + refs: + - {parentEndpoint: 'GET:/micronaut/start', networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: micronaut-scenario-8080, + traceId: not null} + - segmentId: not null + spans: + - operationName: GET:/micronaut/404 + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 131 + isError: true + spanType: Exit + peer: localhost:8081 + skipAnalysis: false + tags: + - {key: http.method, value: GET} + - {key: url, value: 'http://localhost:8081/micronaut/404'} + logs: + - logEvent: + - {key: event, value: error} + - {key: error.kind, value: io.micronaut.http.client.exceptions.HttpClientResponseException} + - {key: message, value: Not Found} + - {key: stack,value: not null} + refs: + - {parentEndpoint: 'GET:/micronaut/start', networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: micronaut-scenario-8080, + traceId: not null} + - segmentId: not null + spans: + - operationName: GET:/micronaut/error + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 131 + isError: true + spanType: Exit + peer: localhost:8081 + skipAnalysis: false + tags: + - {key: http.method, value: GET} + - {key: url, value: 'http://localhost:8081/micronaut/error'} + logs: + - logEvent: + - {key: event, value: error} + - {key: error.kind, value: io.micronaut.http.client.exceptions.HttpClientResponseException} + - {key: message, value: not null} + - {key: stack,value: not null} + refs: + - {parentEndpoint: 'GET:/micronaut/start', networkAddress: '', refType: CrossThread, + parentSpanId: 0, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: micronaut-scenario-8080, + traceId: not null} + - segmentId: not null + spans: + - operationName: GET:/micronaut/start + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 131 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: url, value: 'http://localhost:8080/micronaut/start'} + - {key: http.method, value: GET} + - {key: http.status_code, value: '200'} + - serviceName: micronaut-scenario-8081 + segmentSize: 3 + segments: + - segmentId: not null + spans: + - operationName: GET:/micronaut/success + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 131 + isError: false + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: url, value: 'http://localhost:8081/micronaut/success'} + - {key: http.method, value: GET} + - {key: http.params, value: a=1&b=2} + - {key: http.status_code, value: '200'} + refs: + - {parentEndpoint: 'GET:/micronaut/success', networkAddress: 'localhost:8081', + refType: CrossProcess, parentSpanId: 0, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: micronaut-scenario-8080, + traceId: not null} + - segmentId: not null + spans: + - operationName: GET:/micronaut/404 + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 131 + isError: true + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: url, value: 'http://localhost:8081/micronaut/404'} + - {key: http.method, value: GET} + - {key: http.status_code, value: '404'} + refs: + - {parentEndpoint: 'GET:/micronaut/404', networkAddress: 'localhost:8081', refType: CrossProcess, + parentSpanId: 0, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: micronaut-scenario-8080, + traceId: not null} + - segmentId: not null + spans: + - operationName: GET:/micronaut/error + operationId: 0 + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 131 + isError: true + spanType: Entry + peer: '' + skipAnalysis: false + tags: + - {key: url, value: 'http://localhost:8081/micronaut/error'} + - {key: http.method, value: GET} + - {key: http.status_code, value: '500'} + refs: + - {parentEndpoint: 'GET:/micronaut/error', networkAddress: 'localhost:8081', + refType: CrossProcess, parentSpanId: 0, parentTraceSegmentId: not null, + parentServiceInstance: not null, parentService: micronaut-scenario-8080, + traceId: not null} \ No newline at end of file diff --git a/test/plugin/scenarios/micronaut-http-scenario/configuration.yml b/test/plugin/scenarios/micronaut-http-scenario/configuration.yml new file mode 100644 index 000000000..295905807 --- /dev/null +++ b/test/plugin/scenarios/micronaut-http-scenario/configuration.yml @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +type: jvm +entryService: http://localhost:8080/micronaut/start +healthCheck: http://localhost:8080/micronaut/healthCheck +startScript: ./bin/startup.sh \ No newline at end of file diff --git a/test/plugin/scenarios/micronaut-http-scenario/pom.xml b/test/plugin/scenarios/micronaut-http-scenario/pom.xml new file mode 100644 index 000000000..f9706e1da --- /dev/null +++ b/test/plugin/scenarios/micronaut-http-scenario/pom.xml @@ -0,0 +1,226 @@ + + + + 4.0.0 + + org.apache.skywalking + micronaut-http-scenario + 1.0.0 + + jar + micronaut-scenario + + jar + 1.8 + + + 1.8 + netty + 1.8 + 1.8 + org.apache.skywalking.apm.testcase.micronaut.Application + + + + + io.micronaut + micronaut-inject + compile + + + io.micronaut + micronaut-validation + compile + + + io.micronaut + micronaut-inject-groovy + test + + + org.spockframework + spock-core + test + + + org.codehaus.groovy + groovy-all + + + + + io.micronaut.test + micronaut-test-spock + test + + + io.micronaut + micronaut-http-client + compile + + + io.micronaut + micronaut-http-server-netty + compile + + + io.micronaut + micronaut-jackson-databind + compile + + + ch.qos.logback + logback-classic + runtime + + + io.projectreactor + reactor-core + + + + + + + io.micronaut + micronaut-bom + ${test.framework.version} + pom + import + + + + + + + micronaut-http-scenario + + + + io.micronaut.build + micronaut-maven-plugin + 3.4.0 + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M7 + + + **/*Spec.* + **/*Test.* + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + + + io.micronaut + micronaut-inject-java + ${test.framework.version} + + + io.micronaut + micronaut-http-validation + ${test.framework.version} + + + + -Amicronaut.processing.group=org.apache.skywalking + -Amicronaut.processing.module=micronaut-http-scenario + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + 1.13.0 + + + + addSources + generateStubs + compile + removeStubs + addTestSources + generateTestStubs + compileTests + removeTestStubs + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.3.0 + + + default-shade + + false + + + ${exec.mainClass} + + + + + + package + + shade + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + false + + + + + + + diff --git a/test/plugin/scenarios/micronaut-http-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/micronaut-http-scenario/src/main/assembly/assembly.xml new file mode 100644 index 000000000..b969f0138 --- /dev/null +++ b/test/plugin/scenarios/micronaut-http-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ${project.build.directory}/micronaut-http-scenario.jar + ./libs + 0775 + + + diff --git a/test/plugin/scenarios/micronaut-http-scenario/src/main/java/org/apache/skywalking/apm/testcase/micronaut/Application.java b/test/plugin/scenarios/micronaut-http-scenario/src/main/java/org/apache/skywalking/apm/testcase/micronaut/Application.java new file mode 100644 index 000000000..f30ded120 --- /dev/null +++ b/test/plugin/scenarios/micronaut-http-scenario/src/main/java/org/apache/skywalking/apm/testcase/micronaut/Application.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.micronaut; + +import io.micronaut.runtime.Micronaut; + +public class Application { + public static void main(String[] args) { + Micronaut.run(Application.class, args); + } +} diff --git a/test/plugin/scenarios/micronaut-http-scenario/src/main/java/org/apache/skywalking/apm/testcase/micronaut/controller/ErrorHandlerController.java b/test/plugin/scenarios/micronaut-http-scenario/src/main/java/org/apache/skywalking/apm/testcase/micronaut/controller/ErrorHandlerController.java new file mode 100644 index 000000000..f67aedf6a --- /dev/null +++ b/test/plugin/scenarios/micronaut-http-scenario/src/main/java/org/apache/skywalking/apm/testcase/micronaut/controller/ErrorHandlerController.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.micronaut.controller; + +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Error; + +@Controller +public class ErrorHandlerController { + + @Error(global = true) + public HttpResponse error(HttpRequest request, Throwable e) { + return HttpResponse.serverError().contentType(MediaType.TEXT_HTML_TYPE) + .body("causeError -> "); + } +} diff --git a/test/plugin/scenarios/micronaut-http-scenario/src/main/java/org/apache/skywalking/apm/testcase/micronaut/controller/HelloController.java b/test/plugin/scenarios/micronaut-http-scenario/src/main/java/org/apache/skywalking/apm/testcase/micronaut/controller/HelloController.java new file mode 100644 index 000000000..f2f5b139a --- /dev/null +++ b/test/plugin/scenarios/micronaut-http-scenario/src/main/java/org/apache/skywalking/apm/testcase/micronaut/controller/HelloController.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.testcase.micronaut.controller; + +import io.micronaut.http.HttpRequest; +import io.micronaut.http.MediaType; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.annotation.Get; +import io.micronaut.http.client.HttpClient; +import io.micronaut.http.client.annotation.Client; +import jakarta.inject.Inject; + +@Controller("/micronaut") +public class HelloController { + @Inject + @Client("http://localhost:8081") + private HttpClient client; + + @Get(value = "healthCheck") + public String checkHealth() { + return "checked"; + } + + @Get(produces = MediaType.TEXT_PLAIN, value = "start") + public String start() throws InterruptedException { + try { + client.toBlocking().retrieve(HttpRequest.GET("/micronaut/success?a=1&b=2")); + } catch (Exception e) { + e.printStackTrace(); + } + try { + client.toBlocking().retrieve(HttpRequest.GET("/micronaut/404")); + } catch (Exception e) { + e.printStackTrace(); + } + try { + client.toBlocking().retrieve(HttpRequest.GET("/micronaut/error")); + } catch (Exception e) { + e.printStackTrace(); + } + return "end"; + } + + @Get(produces = MediaType.TEXT_PLAIN, value = "success") + public String success() { + return "success"; + } + + @Get(produces = MediaType.TEXT_PLAIN, value = "error") + public String error() { + throw new NullPointerException(); + } +} diff --git a/test/plugin/scenarios/micronaut-http-scenario/support-version.list b/test/plugin/scenarios/micronaut-http-scenario/support-version.list new file mode 100644 index 000000000..fe14e0896 --- /dev/null +++ b/test/plugin/scenarios/micronaut-http-scenario/support-version.list @@ -0,0 +1,22 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +3.6.0 +3.5.4 +3.4.3 +3.3.4 +3.2.7 +3.2.0 \ No newline at end of file