This is an automated email from the ASF dual-hosted git repository. wusheng pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/skywalking-java.git
The following commit(s) were added to refs/heads/main by this push: new d53f04b7bf Add Caffeine plugin as optional (#743) d53f04b7bf is described below commit d53f04b7bfd534e8611312711a0885988ffca4fa Author: Zixin Zhou <zhouzi...@apache.org> AuthorDate: Sun Dec 29 08:16:05 2024 +0800 Add Caffeine plugin as optional (#743) --- .github/workflows/ci.yaml | 2 +- .github/workflows/codeql.yaml | 4 +- .github/workflows/plugins-jdk17-test.1.yaml | 1 + CHANGES.md | 1 + .../network/trace/component/ComponentsDefine.java | 1 + apm-sniffer/config/agent.config | 4 + .../optional-plugins/caffeine-3.x-plugin/pom.xml | 46 ++++++ .../caffeine/v3/AbstractCaffeineInterceptor.java | 71 +++++++++ .../caffeine/v3/CaffeineIterableInterceptor.java | 46 ++++++ .../plugin/caffeine/v3/CaffeineMapInterceptor.java | 45 ++++++ .../caffeine/v3/CaffeineOperationTransform.java | 34 +++++ .../plugin/caffeine/v3/CaffeinePluginConfig.java | 50 +++++++ .../caffeine/v3/CaffeineStringInterceptor.java | 40 +++++ .../v3/define/CaffeineInstrumentation.java | 119 +++++++++++++++ .../src/main/resources/skywalking-plugin.def | 17 +++ .../caffeine/v3/CaffeineInterceptorTest.java | 149 +++++++++++++++++++ .../org.mockito.plugins.MockMaker | 1 + apm-sniffer/optional-plugins/pom.xml | 1 + .../setup/service-agent/java-agent/Plugin-list.md | 1 + .../service-agent/java-agent/Supported-list.md | 3 +- .../service-agent/java-agent/configurations.md | 23 +-- .../scenarios/caffeine-3.x-scenario/bin/startup.sh | 21 +++ .../caffeine-3.x-scenario/config/expectedData.yaml | 163 +++++++++++++++++++++ .../caffeine-3.x-scenario/configuration.yml | 22 +++ .../plugin/scenarios/caffeine-3.x-scenario/pom.xml | 111 ++++++++++++++ .../src/main/assembly/assembly.xml | 41 ++++++ .../apm/testcase/caffeine/Application.java | 30 ++++ .../caffeine/controller/CaffeineController.java | 56 +++++++ .../caffeine/controller/HeartbeatController.java | 31 ++++ .../src/main/resources/application.yaml | 21 +++ .../caffeine-3.x-scenario/support-version.list | 18 +++ 31 files changed, 1158 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 20e2bae6d2..e2078dd4ab 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -42,7 +42,7 @@ jobs: build: name: Java ${{ matrix.java-version }} / ${{ matrix.os }} runs-on: ${{ matrix.os }}-latest - timeout-minutes: 60 + timeout-minutes: 90 needs: [ license ] strategy: fail-fast: true diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml index 0932d63cee..1abc057ad0 100644 --- a/.github/workflows/codeql.yaml +++ b/.github/workflows/codeql.yaml @@ -54,11 +54,11 @@ jobs: java-version: 17 - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} - run: ./mvnw -q -Dmaven.test.skip=true clean install || ./mvnw -q -Dmaven.test.skip=true clean install - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/plugins-jdk17-test.1.yaml b/.github/workflows/plugins-jdk17-test.1.yaml index 949e69cfa1..80ff29bb67 100644 --- a/.github/workflows/plugins-jdk17-test.1.yaml +++ b/.github/workflows/plugins-jdk17-test.1.yaml @@ -63,6 +63,7 @@ jobs: - c3p0-0.9.0.x-0.9.1.x-scenario - c3p0-0.9.2.x-0.10.x-scenario - spring-scheduled-6.x-scenario + - caffeine-3.x-scenario steps: - uses: actions/checkout@v2 with: diff --git a/CHANGES.md b/CHANGES.md index 5e959990cd..8a74479321 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -25,6 +25,7 @@ Release Notes. Plugin, Kotlin Coroutine Plugin, and Spring Gateway Plugin * Change context and parent entry span propagation mechanism from gRPC ThreadLocal context to SkyWalking native dynamic field as new propagation mechanism, to better support async scenarios. +* Add Caffeine plugin as optional. All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/222?closed=1) 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 101abcdb5e..e10b207ce5 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 @@ -259,4 +259,5 @@ public class ComponentsDefine { public static final OfficialComponent SOLON_MVC = new OfficialComponent(158, "SolonMVC"); + public static final OfficialComponent CAFFEINE = new OfficialComponent(160, "Caffeine"); } diff --git a/apm-sniffer/config/agent.config b/apm-sniffer/config/agent.config index 695e47349f..f27572a1e0 100755 --- a/apm-sniffer/config/agent.config +++ b/apm-sniffer/config/agent.config @@ -332,3 +332,7 @@ plugin.solon.http_params_length_threshold=${SW_PLUGIN_SOLON_HTTP_PARAMS_LENGTH_T plugin.solon.include_http_headers=${SW_PLUGIN_SOLON_INCLUDE_HTTP_HEADERS:} # Define the max length of collected HTTP body. The default value(=0) means not collecting. plugin.solon.http_body_length_threshold=${SW_PLUGIN_SOLON_HTTP_BODY_LENGTH_THRESHOLD:0} +# Specify which command should be converted to write operation +plugin.caffeine.operation_mapping_write=${SW_PLUGIN_CAFFEINE_OPERATION_MAPPING_WRITE:put,putAll,remove,clear} +# Specify which command should be converted to read operation +plugin.caffeine.operation_mapping_read=${SW_PLUGIN_CAFFEINE_OPERATION_MAPPING_READ:getIfPresent,getAllPresent,computeIfAbsent} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/pom.xml b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/pom.xml new file mode 100644 index 0000000000..3170ed81a2 --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/pom.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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. + ~ + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.skywalking</groupId> + <artifactId>optional-plugins</artifactId> + <version>9.4.0-SNAPSHOT</version> + </parent> + + <artifactId>apm-caffeine-3.x-plugin</artifactId> + <packaging>jar</packaging> + <name>caffeine-3.x-plugin</name> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <caffeine.version>3.1.8</caffeine.version> + </properties> + + <dependencies> + <dependency> + <groupId>com.github.ben-manes.caffeine</groupId> + <artifactId>caffeine</artifactId> + <version>${caffeine.version}</version> + <scope>provided</scope> + </dependency> + </dependencies> +</project> diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/AbstractCaffeineInterceptor.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/AbstractCaffeineInterceptor.java new file mode 100644 index 0000000000..9c66cabd08 --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/AbstractCaffeineInterceptor.java @@ -0,0 +1,71 @@ +/* + * 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.caffeine.v3; + +import java.lang.reflect.Method; +import org.apache.skywalking.apm.agent.core.context.ContextManager; +import org.apache.skywalking.apm.agent.core.context.tag.Tags; +import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan; +import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor; +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult; +import org.apache.skywalking.apm.network.trace.component.ComponentsDefine; + +import static org.apache.skywalking.apm.plugin.caffeine.v3.CaffeineOperationTransform.transformOperation; + +abstract public class AbstractCaffeineInterceptor implements InstanceMethodsAroundInterceptor { + + protected AbstractSpan generateSpanInfo(String methodName) { + AbstractSpan span = ContextManager.createLocalSpan("Caffeine/" + methodName); + span.setComponent(ComponentsDefine.CAFFEINE); + Tags.CACHE_TYPE.set(span, ComponentsDefine.CAFFEINE.getName()); + Tags.CACHE_CMD.set(span, methodName); + transformOperation(methodName).ifPresent(op -> Tags.CACHE_OP.set(span, op)); + SpanLayer.asCache(span); + return span; + } + + @Override + public void beforeMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class<?>[] argumentsTypes, + final MethodInterceptResult result) throws Throwable { + } + + @Override + public Object afterMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class<?>[] argumentsTypes, + final Object ret) throws Throwable { + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class<?>[] argumentsTypes, + final Throwable t) { + ContextManager.activeSpan().log(t); + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineIterableInterceptor.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineIterableInterceptor.java new file mode 100644 index 0000000000..ef5860e46f --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineIterableInterceptor.java @@ -0,0 +1,46 @@ +/* + * 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.caffeine.v3; + +import java.lang.reflect.Method; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +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.MethodInterceptResult; + +public class CaffeineIterableInterceptor extends AbstractCaffeineInterceptor { + + @Override + public void beforeMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class<?>[] argumentsTypes, + final MethodInterceptResult result) throws Throwable { + AbstractSpan span = generateSpanInfo(method.getName()); + if (allArguments != null && allArguments.length > 0) { + String keys = StreamSupport + .stream(((Iterable<?>) allArguments[0]).spliterator(), false) + .map(String::valueOf) + .collect(Collectors.joining(",")); + Tags.CACHE_KEY.set(span, keys); + } + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineMapInterceptor.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineMapInterceptor.java new file mode 100644 index 0000000000..98258bbb38 --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineMapInterceptor.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.caffeine.v3; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.stream.Collectors; +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.MethodInterceptResult; + +public class CaffeineMapInterceptor extends AbstractCaffeineInterceptor { + + @Override + public void beforeMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class<?>[] argumentsTypes, + final MethodInterceptResult result) throws Throwable { + AbstractSpan span = generateSpanInfo(method.getName()); + if (allArguments != null && allArguments.length > 0) { + String keys = ((Map<?, ?>) allArguments[0]) + .keySet().stream().map(String::valueOf) + .collect(Collectors.joining(",")); + Tags.CACHE_KEY.set(span, keys); + } + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineOperationTransform.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineOperationTransform.java new file mode 100644 index 0000000000..0f4a128a92 --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineOperationTransform.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.skywalking.apm.plugin.caffeine.v3; + +import java.util.Optional; + +public class CaffeineOperationTransform { + + public static Optional<String> transformOperation(String cmd) { + if (CaffeinePluginConfig.Plugin.Caffeine.OPERATION_MAPPING_READ.contains(cmd)) { + return Optional.of("read"); + } + if (CaffeinePluginConfig.Plugin.Caffeine.OPERATION_MAPPING_WRITE.contains(cmd)) { + return Optional.of("write"); + } + return Optional.empty(); + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeinePluginConfig.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeinePluginConfig.java new file mode 100644 index 0000000000..f775668bee --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeinePluginConfig.java @@ -0,0 +1,50 @@ +/* + * 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.caffeine.v3; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import org.apache.skywalking.apm.agent.core.boot.PluginConfig; + +/** + * Operation represent a cache span is "write" or "read" action , and "op"(operation) is tagged with key "cache.op" + * usually This config term define which command should be converted to write Operation . + * + * @see org.apache.skywalking.apm.agent.core.context.tag.Tags#CACHE_OP + * @see CaffeineOperationTransform#transformOperation(String) + */ +public class CaffeinePluginConfig { + public static class Plugin { + @PluginConfig(root = CaffeinePluginConfig.class) + public static class Caffeine { + public static Set<String> OPERATION_MAPPING_WRITE = new HashSet<>(Arrays.asList( + "put", + "putAll", + "remove", + "clear" + )); + public static Set<String> OPERATION_MAPPING_READ = new HashSet<>(Arrays.asList( + "getIfPresent", + "getAllPresent", + "computeIfAbsent" + )); + } + } +} \ No newline at end of file diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineStringInterceptor.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineStringInterceptor.java new file mode 100644 index 0000000000..82b34ca48d --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineStringInterceptor.java @@ -0,0 +1,40 @@ +/* + * 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.caffeine.v3; + +import java.lang.reflect.Method; +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.MethodInterceptResult; + +public class CaffeineStringInterceptor extends AbstractCaffeineInterceptor { + + @Override + public void beforeMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class<?>[] argumentsTypes, + final MethodInterceptResult result) throws Throwable { + AbstractSpan span = generateSpanInfo(method.getName()); + if (allArguments != null && allArguments.length > 0 && allArguments[0] instanceof String) { + Tags.CACHE_KEY.set(span, allArguments[0].toString()); + } + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/define/CaffeineInstrumentation.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/define/CaffeineInstrumentation.java new file mode 100644 index 0000000000..50e5b3751a --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/define/CaffeineInstrumentation.java @@ -0,0 +1,119 @@ +/* + * 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.caffeine.v3.define; + +import java.util.Map; +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.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import static org.apache.skywalking.apm.agent.core.plugin.match.MultiClassNameMatch.byMultiClassMatch; + +public class CaffeineInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + public static final String BOUNDED_LOCAL_INTERCEPT_CLASS = "com.github.benmanes.caffeine.cache.BoundedLocalCache"; + public static final String UNBOUNDED_LOCAL_INTERCEPT_CLASS = "com.github.benmanes.caffeine.cache.UnboundedLocalCache"; + public static final String CAFFEINE_ITERABLE_ENHANCE_CLASS = "org.apache.skywalking.apm.plugin.caffeine.v3.CaffeineIterableInterceptor"; + public static final String CAFFEINE_MAP_ENHANCE_CLASS = "org.apache.skywalking.apm.plugin.caffeine.v3.CaffeineMapInterceptor"; + public static final String CAFFEINE_STRING_ENHANCE_CLASS = "org.apache.skywalking.apm.plugin.caffeine.v3.CaffeineStringInterceptor"; + + // read/write operations + public static final String GET_IF_PRESENT_METHOD = "getIfPresent"; + public static final String GET_ALL_PRESENT_METHOD = "getAllPresent"; + public static final String COMPUTE_IF_ABSENT_METHOD = "computeIfAbsent"; + public static final String PUT_METHOD = "put"; + public static final String PUT_ALL_METHOD = "putAll"; + public static final String REMOVE_METHOD = "remove"; + public static final String CLEAR_METHOD = "clear"; + + @Override + protected ClassMatch enhanceClass() { + return byMultiClassMatch(BOUNDED_LOCAL_INTERCEPT_CLASS, UNBOUNDED_LOCAL_INTERCEPT_CLASS); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher<MethodDescription> getMethodsMatcher() { + return named(GET_IF_PRESENT_METHOD) + .and(takesArguments(2)) + .or(named(COMPUTE_IF_ABSENT_METHOD).and(takesArguments(4))) + .or(named(PUT_METHOD).and(takesArguments(2))) + .or(named(REMOVE_METHOD).and(takesArguments(1))) + .or(named(CLEAR_METHOD).and(takesArguments(0))); + } + + @Override + public String getMethodsInterceptor() { + return CAFFEINE_STRING_ENHANCE_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher<MethodDescription> getMethodsMatcher() { + return named(GET_ALL_PRESENT_METHOD).and(takesArgument(0, Iterable.class)); + } + + @Override + public String getMethodsInterceptor() { + return CAFFEINE_ITERABLE_ENHANCE_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher<MethodDescription> getMethodsMatcher() { + return named(PUT_ALL_METHOD).and(takesArgument(0, Map.class)); + } + + @Override + public String getMethodsInterceptor() { + return CAFFEINE_MAP_ENHANCE_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 0000000000..1edf1e3a0e --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-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. + +caffeine-3.x=org.apache.skywalking.apm.plugin.caffeine.v3.define.CaffeineInstrumentation diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptorTest.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptorTest.java new file mode 100644 index 0000000000..7642ae110d --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptorTest.java @@ -0,0 +1,149 @@ +/* + * 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.caffeine.v3; + +import com.github.benmanes.caffeine.cache.Expiry; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment; +import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule; +import org.apache.skywalking.apm.agent.test.tools.SegmentStorage; +import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint; +import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import static org.hamcrest.CoreMatchers.is; + +@RunWith(TracingSegmentRunner.class) +public class CaffeineInterceptorTest { + + @SegmentStoragePoint + private SegmentStorage segmentStorage; + + private CaffeineIterableInterceptor caffeineIterableInterceptor; + private CaffeineMapInterceptor caffeineMapInterceptor; + private CaffeineStringInterceptor caffeineStringInterceptor; + private Object[] operateObjectArguments; + + private Exception exception; + + private Method getAllPresentMethod; + private Method getIfPresentMethod; + private Method computeIfAbsentMethod; + + private Method putMethod; + private Method putAllMethod; + private Method removeMethod; + private Method cleanMethod; + + @Rule + public AgentServiceRule serviceRule = new AgentServiceRule(); + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + @Before + public void setUp() throws Exception { + caffeineIterableInterceptor = new CaffeineIterableInterceptor(); + caffeineMapInterceptor = new CaffeineMapInterceptor(); + caffeineStringInterceptor = new CaffeineStringInterceptor(); + exception = new Exception(); + operateObjectArguments = new Object[] {"dataKey"}; + Class<?> cache = Class.forName("com.github.benmanes.caffeine.cache.BoundedLocalCache"); + getAllPresentMethod = cache.getDeclaredMethod("getAllPresent", Iterable.class); + getIfPresentMethod = cache.getDeclaredMethod("getIfPresent", Object.class, boolean.class); + computeIfAbsentMethod = cache.getDeclaredMethod( + "computeIfAbsent", Object.class, Function.class, boolean.class, boolean.class); + putMethod = cache.getDeclaredMethod("put", Object.class, Object.class, Expiry.class, boolean.class); + putAllMethod = cache.getDeclaredMethod("putAll", Map.class); + removeMethod = cache.getDeclaredMethod("remove", Object.class); + cleanMethod = cache.getDeclaredMethod("clear"); + } + + @Test + public void testGetAllPresentSuccess() throws Throwable { + caffeineIterableInterceptor.beforeMethod(null, getAllPresentMethod, null, null, null); + caffeineIterableInterceptor.handleMethodException(null, getAllPresentMethod, null, null, exception); + caffeineIterableInterceptor.afterMethod(null, getAllPresentMethod, null, null, null); + List<TraceSegment> traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testGetIfPresentSuccess() throws Throwable { + caffeineStringInterceptor.beforeMethod(null, getIfPresentMethod, operateObjectArguments, null, null); + caffeineStringInterceptor.handleMethodException( + null, getIfPresentMethod, operateObjectArguments, null, exception); + caffeineStringInterceptor.afterMethod(null, getIfPresentMethod, operateObjectArguments, null, null); + List<TraceSegment> traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testComputeIfAbsentMethodSuccess() throws Throwable { + caffeineStringInterceptor.beforeMethod(null, computeIfAbsentMethod, null, null, null); + caffeineStringInterceptor.handleMethodException(null, computeIfAbsentMethod, null, null, exception); + caffeineStringInterceptor.afterMethod(null, computeIfAbsentMethod, null, null, null); + List<TraceSegment> traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testPutMethodSuccess() throws Throwable { + caffeineStringInterceptor.beforeMethod(null, putMethod, operateObjectArguments, null, null); + caffeineStringInterceptor.handleMethodException(null, putMethod, operateObjectArguments, null, exception); + caffeineStringInterceptor.afterMethod(null, putMethod, operateObjectArguments, null, null); + List<TraceSegment> traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testPutAllMethodSuccess() throws Throwable { + caffeineMapInterceptor.beforeMethod(null, putAllMethod, null, null, null); + caffeineMapInterceptor.handleMethodException(null, putAllMethod, null, null, exception); + caffeineMapInterceptor.afterMethod(null, putAllMethod, null, null, null); + List<TraceSegment> traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testRemoveMethodSuccess() throws Throwable { + caffeineStringInterceptor.beforeMethod(null, removeMethod, operateObjectArguments, null, null); + caffeineStringInterceptor.handleMethodException(null, removeMethod, operateObjectArguments, null, exception); + caffeineStringInterceptor.afterMethod(null, removeMethod, operateObjectArguments, null, null); + List<TraceSegment> traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testClearMethodSuccess() throws Throwable { + caffeineStringInterceptor.beforeMethod(null, cleanMethod, null, null, null); + caffeineStringInterceptor.handleMethodException(null, cleanMethod, null, null, exception); + caffeineStringInterceptor.afterMethod(null, cleanMethod, null, null, null); + List<TraceSegment> traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } +} diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..1f0955d450 --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline diff --git a/apm-sniffer/optional-plugins/pom.xml b/apm-sniffer/optional-plugins/pom.xml index 4a55525da8..6369484e82 100644 --- a/apm-sniffer/optional-plugins/pom.xml +++ b/apm-sniffer/optional-plugins/pom.xml @@ -59,6 +59,7 @@ <module>trace-sampler-cpu-policy-plugin</module> <module>nacos-client-2.x-plugin</module> <module>netty-http-4.1.x-plugin</module> + <module>caffeine-3.x-plugin</module> </modules> <dependencies> 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 2c7b51a6c3..847a81dbec 100644 --- a/docs/en/setup/service-agent/java-agent/Plugin-list.md +++ b/docs/en/setup/service-agent/java-agent/Plugin-list.md @@ -179,3 +179,4 @@ - activemq-artemis-jakarta-client-2.x - c3p0-0.9.x - solon-2.x +- caffeine-3.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 fe3bcd231d..cd51b8d2ce 100644 --- a/docs/en/setup/service-agent/java-agent/Supported-list.md +++ b/docs/en/setup/service-agent/java-agent/Supported-list.md @@ -139,8 +139,9 @@ metrics based on the tracing data. * JRE Callable and Runnable (Optional²) * JRE ForkJoinPool (Optional²) * Cache - * [Ehcache](https://www.ehcache.org/) 2.x + * [Ehcache](https://www.ehcache.org/) 2.x (Optional²) * [GuavaCache](https://github.com/google/guava) 18.x -> 23.x (Optional²) + * [Caffeine](https://github.com/ben-manes/caffeine) 3.x (Optional²) * Kotlin * [Coroutine](https://kotlinlang.org/docs/coroutines-overview.html) 1.0.1 -> 1.3.x (Optional²) * GraphQL diff --git a/docs/en/setup/service-agent/java-agent/configurations.md b/docs/en/setup/service-agent/java-agent/configurations.md index 83837d33b7..9c6cc2d7f6 100644 --- a/docs/en/setup/service-agent/java-agent/configurations.md +++ b/docs/en/setup/service-agent/java-agent/configurations.md @@ -126,17 +126,18 @@ This is the properties list supported in `agent/config/agent.config`. | `plugin.memcached.operation_mapping_read` | Specify which command should be converted to `read` operation [...] | `plugin.ehcache.operation_mapping_write` | Specify which command should be converted to `write` operation [...] | `plugin.ehcache.operation_mapping_read` | Specify which command should be converted to `read` operation [...] -| `plugin.guavacache.operation_mapping_write` | Specify which command should be converted to `write` operation [...] -| `plugin.guavacache.operation_mapping_read` | Specify which command should be converted to `read` operation [...] -| `plugin.nettyhttp.collect_request_body` | This config item controls that whether the Netty-http plugin should collect the http body of the request. [...] -| `plugin.nettyhttp.filter_length_limit` | When `COLLECT_REQUEST_BODY` is enabled, how many characters to keep and send to the OAP backend, use negative values to keep and send the complete body. [...] -| `plugin.nettyhttp.supported_content_types_prefix` | When `COLLECT_REQUEST_BODY` is enabled and content-type start with `HTTP_SUPPORTED_CONTENT_TYPES_PREFIX`, collect the body of the request , multiple paths should be separated by `,` [...] -| `plugin.rocketmqclient.collect_message_keys` | If set to true, the keys of messages would be collected by the plugin for RocketMQ Java client. -| `plugin.rocketmqclient.collect_message_tags` | If set to true, the tags of messages would be collected by the plugin for RocketMQ Java client. -| `plugin.solon.http_params_length_threshold` | Define the max length of collected HTTP parameters. The default value(=0) means not collecting. [...] -| `plugin.solon.include_http_headers` | It controls what header data should be collected, values must be in lower case, if empty, no header data will be collected. default is empty. [...] -| `plugin.solon.http_body_length_threshold` | Define the max length of collected HTTP body. The default value(=0) means not collecting. [...] - +| `plugin.guavacache.operation_mapping_write` | Specify which command should be converted to `write` operation [...] +| `plugin.guavacache.operation_mapping_read` | Specify which command should be converted to `read` operation [...] +| `plugin.nettyhttp.collect_request_body` | This config item controls that whether the Netty-http plugin should collect the http body of the request. [...] +| `plugin.nettyhttp.filter_length_limit` | When `COLLECT_REQUEST_BODY` is enabled, how many characters to keep and send to the OAP backend, use negative values to keep and send the complete body. [...] +| `plugin.nettyhttp.supported_content_types_prefix` | When `COLLECT_REQUEST_BODY` is enabled and content-type start with `HTTP_SUPPORTED_CONTENT_TYPES_PREFIX`, collect the body of the request , multiple paths should be separated by `,` [...] +| `plugin.rocketmqclient.collect_message_keys` | If set to true, the keys of messages would be collected by the plugin for RocketMQ Java client. [...] +| `plugin.rocketmqclient.collect_message_tags` | If set to true, the tags of messages would be collected by the plugin for RocketMQ Java client. [...] +| `plugin.solon.http_params_length_threshold` | Define the max length of collected HTTP parameters. The default value(=0) means not collecting. [...] +| `plugin.solon.include_http_headers` | It controls what header data should be collected, values must be in lower case, if empty, no header data will be collected. default is empty. [...] +| `plugin.solon.http_body_length_threshold` | Define the max length of collected HTTP body. The default value(=0) means not collecting. [...] +| `plugin.caffeine.operation_mapping_write` | Specify which command should be converted to `write` operation [...] +| `plugin.caffeine.operation_mapping_read` | Specify which command should be converted to `read` operation [...] # Reset Collection/Map type configurations as empty collection. diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/bin/startup.sh b/test/plugin/scenarios/caffeine-3.x-scenario/bin/startup.sh new file mode 100755 index 0000000000..01883d186d --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/bin/startup.sh @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +home="$(cd "$(dirname $0)"; pwd)" + +java -jar ${agent_opts} ${home}/../libs/caffeine-3.x-scenario.jar & \ No newline at end of file diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/caffeine-3.x-scenario/config/expectedData.yaml new file mode 100755 index 0000000000..133ede4755 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/config/expectedData.yaml @@ -0,0 +1,163 @@ +# 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: caffeine-3.x-scenario + segmentSize: nq 0 + segments: + - segmentId: not null + spans: + - operationName: Caffeine/put + parentSpanId: 0 + spanId: 1 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: put } + - { key: cache.op, value: write } + - { key: cache.key, value: "1" } + skipAnalysis: 'false' + - operationName: Caffeine/putAll + parentSpanId: 0 + spanId: 2 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: putAll } + - { key: cache.op, value: write } + - { key: cache.key, value: "2" } + skipAnalysis: 'false' + - operationName: Caffeine/computeIfAbsent + parentSpanId: 0 + spanId: 3 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: computeIfAbsent } + - { key: cache.op, value: read } + - { key: cache.key, value: "1" } + - operationName: Caffeine/getIfPresent + parentSpanId: 0 + spanId: 4 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: getIfPresent } + - { key: cache.op, value: read } + - { key: cache.key, value: "1" } + skipAnalysis: 'false' + - operationName: Caffeine/getAllPresent + parentSpanId: 0 + spanId: 5 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: getAllPresent } + - { key: cache.op, value: read } + - { key: cache.key, value: '2' } + - operationName: Caffeine/remove + parentSpanId: 0 + spanId: 6 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: remove } + - { key: cache.op, value: write } + - { key: cache.key, value: '1' } + - operationName: Caffeine/remove + parentSpanId: 0 + spanId: 7 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: remove } + - { key: cache.op, value: write } + - { key: cache.key, value: '2' } + - operationName: Caffeine/clear + parentSpanId: 0 + spanId: 8 + spanLayer: Cache + startTime: nq 0 + endTime: nq 0 + componentId: 160 + isError: false + spanType: Local + peer: '' + skipAnalysis: 'false' + tags: + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: clear } + - { key: cache.op, value: write } + - operationName: GET:/caffeine-3.x-scenario/case/caffeine + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: nq 0 + endTime: nq 0 + componentId: 1 + isError: false + spanType: Entry + peer: '' + tags: + - { key: url, value: 'http://localhost:8080/caffeine-3.x-scenario/case/caffeine' } + - { key: http.method, value: GET } + - { key: http.status_code, value: '200' } + skipAnalysis: 'false' diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/configuration.yml b/test/plugin/scenarios/caffeine-3.x-scenario/configuration.yml new file mode 100755 index 0000000000..00208eefa9 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/configuration.yml @@ -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. + +type: jvm +entryService: http://localhost:8080/caffeine-3.x-scenario/case/caffeine +healthCheck: http://localhost:8080/caffeine-3.x-scenario/case/healthCheck +startScript: ./bin/startup.sh +runningMode: with_optional +withPlugins: apm-caffeine-3.x-plugin-*.jar \ No newline at end of file diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/pom.xml b/test/plugin/scenarios/caffeine-3.x-scenario/pom.xml new file mode 100755 index 0000000000..a627109082 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/pom.xml @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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. + ~ + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <groupId>org.apache.skywalking.apm.testcase</groupId> + <artifactId>caffeine-3.x-scenario</artifactId> + <version>1.0.0</version> + <packaging>jar</packaging> + + <modelVersion>4.0.0</modelVersion> + + <properties> + <compiler.version>17</compiler.version> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version> + <test.framework.version>6.0.2</test.framework.version> + <docker.image.version>${test.framework.version}</docker.image.version> + <spring.boot.version>3.0.0</spring.boot.version> + <caffeine.version>3.1.8</caffeine.version> + </properties> + + <name>skywalking-caffeine-3.x-scenario</name> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-dependencies</artifactId> + <version>${spring.boot.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + </dependency> + <dependency> + <groupId>com.github.ben-manes.caffeine</groupId> + <artifactId>caffeine</artifactId> + <version>${caffeine.version}</version> + </dependency> + </dependencies> + + <build> + <finalName>caffeine-3.x-scenario</finalName> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <version>${spring.boot.version}</version> + <executions> + <execution> + <goals> + <goal>repackage</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <version>${maven-compiler-plugin.version}</version> + <configuration> + <source>${compiler.version}</source> + <target>${compiler.version}</target> + <encoding>${project.build.sourceEncoding}</encoding> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-assembly-plugin</artifactId> + <executions> + <execution> + <id>assemble</id> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + <configuration> + <descriptors> + <descriptor>src/main/assembly/assembly.xml</descriptor> + </descriptors> + <outputDirectory>./target/</outputDirectory> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> \ No newline at end of file diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/assembly/assembly.xml new file mode 100755 index 0000000000..382a68db61 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/assembly/assembly.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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. + ~ + --> +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"> + <formats> + <format>zip</format> + </formats> + + <fileSets> + <fileSet> + <directory>./bin</directory> + <fileMode>0775</fileMode> + </fileSet> + </fileSets> + + <files> + <file> + <source>${project.build.directory}/caffeine-3.x-scenario.jar</source> + <outputDirectory>./libs</outputDirectory> + <fileMode>0775</fileMode> + </file> + </files> +</assembly> diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/Application.java b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/Application.java new file mode 100644 index 0000000000..a4739cfbfe --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/Application.java @@ -0,0 +1,30 @@ +/* + * 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.caffeine; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/CaffeineController.java b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/CaffeineController.java new file mode 100644 index 0000000000..f8bad1d288 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/CaffeineController.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.testcase.caffeine.controller; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import java.util.HashMap; +import java.util.Map; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class CaffeineController { + + Cache<Object, Object> caffeine = Caffeine.newBuilder().build(); + + @GetMapping("/case/caffeine") + public void testCase() { + try { + Map<String, String> data = new HashMap<>(); + data.put("2", "value-2"); + + // put operations + caffeine.put("1", "value-1"); + caffeine.putAll(data); + + // get operations + caffeine.get("1", o -> "default"); + caffeine.getIfPresent("1"); + caffeine.getAllPresent(data.keySet()); + + // delete operations + caffeine.invalidate("1"); + caffeine.invalidateAll(data.keySet()); + caffeine.invalidateAll(); + } catch (Exception e) { + throw e; + } + } +} diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/HeartbeatController.java b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/HeartbeatController.java new file mode 100644 index 0000000000..9dbc01dc77 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/HeartbeatController.java @@ -0,0 +1,31 @@ +/* + * 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.caffeine.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class HeartbeatController { + + @GetMapping("/case/healthCheck") + public String healthCheck() { + return "success"; + } +} diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/resources/application.yaml new file mode 100755 index 0000000000..ac1fb9a734 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/src/main/resources/application.yaml @@ -0,0 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +server: + port: 8080 + servlet: + context-path: /caffeine-3.x-scenario \ No newline at end of file diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/support-version.list b/test/plugin/scenarios/caffeine-3.x-scenario/support-version.list new file mode 100755 index 0000000000..0c44c26905 --- /dev/null +++ b/test/plugin/scenarios/caffeine-3.x-scenario/support-version.list @@ -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. + +3.0.0 +3.1.8 \ No newline at end of file