From 43b9a65cb6fb7d7f1b18a29b76e0479bc2db6291 Mon Sep 17 00:00:00 2001 From: Zixin Zhou Date: Fri, 27 Dec 2024 19:16:05 +0800 Subject: [PATCH 1/4] Add Caffeine plugin as optional --- .github/workflows/codeql.yaml | 4 +- .github/workflows/plugins-jdk17-test.1.yaml | 1 + CHANGES.md | 1 + .../trace/component/ComponentsDefine.java | 1 + apm-sniffer/config/agent.config | 4 + .../caffeine-3.x-plugin/pom.xml | 46 +++++ .../caffeine/v3/CaffeineInterceptor.java | 86 +++++++++ .../v3/CaffeineOperationTransform.java | 34 ++++ .../caffeine/v3/CaffeinePluginConfig.java | 50 ++++++ .../v3/define/CaffeineInstrumentation.java | 85 +++++++++ .../src/main/resources/skywalking-plugin.def | 17 ++ .../caffeine/v3/CaffeineInterceptorTest.java | 144 ++++++++++++++++ .../org.mockito.plugins.MockMaker | 1 + apm-sniffer/optional-plugins/pom.xml | 1 + .../service-agent/java-agent/Plugin-list.md | 1 + .../java-agent/Supported-list.md | 3 +- .../java-agent/configurations.md | 23 +-- .../caffeine-3.x-scenario/bin/startup.sh | 21 +++ .../config/expectedData.yaml | 163 ++++++++++++++++++ .../caffeine-3.x-scenario/configuration.yml | 22 +++ .../scenarios/caffeine-3.x-scenario/pom.xml | 111 ++++++++++++ .../src/main/assembly/assembly.xml | 41 +++++ .../apm/testcase/caffeine/Application.java | 30 ++++ .../controller/CaffeineController.java | 55 ++++++ .../controller/HeartbeatController.java | 31 ++++ .../src/main/resources/application.yaml | 21 +++ .../support-version.list | 18 ++ 27 files changed, 1001 insertions(+), 14 deletions(-) create mode 100644 apm-sniffer/optional-plugins/caffeine-3.x-plugin/pom.xml create mode 100644 apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptor.java create mode 100644 apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineOperationTransform.java create mode 100644 apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeinePluginConfig.java create mode 100644 apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/define/CaffeineInstrumentation.java create mode 100644 apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/resources/skywalking-plugin.def create mode 100644 apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptorTest.java create mode 100644 apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker create mode 100755 test/plugin/scenarios/caffeine-3.x-scenario/bin/startup.sh create mode 100755 test/plugin/scenarios/caffeine-3.x-scenario/config/expectedData.yaml create mode 100755 test/plugin/scenarios/caffeine-3.x-scenario/configuration.yml create mode 100755 test/plugin/scenarios/caffeine-3.x-scenario/pom.xml create mode 100755 test/plugin/scenarios/caffeine-3.x-scenario/src/main/assembly/assembly.xml create mode 100644 test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/Application.java create mode 100644 test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/CaffeineController.java create mode 100644 test/plugin/scenarios/caffeine-3.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/caffeine/controller/HeartbeatController.java create mode 100755 test/plugin/scenarios/caffeine-3.x-scenario/src/main/resources/application.yaml create mode 100755 test/plugin/scenarios/caffeine-3.x-scenario/support-version.list 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 @@ + + + + + 4.0.0 + + org.apache.skywalking + optional-plugins + 9.4.0-SNAPSHOT + + + apm-caffeine-3.x-plugin + jar + caffeine-3.x-plugin + + + UTF-8 + 3.1.8 + + + + + com.github.ben-manes.caffeine + caffeine + ${caffeine.version} + provided + + + diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptor.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptor.java new file mode 100644 index 0000000000..3051fba80e --- /dev/null +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptor.java @@ -0,0 +1,86 @@ +/* + * 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.Set; +import java.util.stream.Collectors; +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; + +public class CaffeineInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(final EnhancedInstance objInst, + final Method method, + final Object[] allArguments, + final Class[] argumentsTypes, + final MethodInterceptResult result) throws Throwable { + String methodName = method.getName(); + AbstractSpan span = ContextManager.createLocalSpan("Caffeine/" + methodName); + span.setComponent(ComponentsDefine.CAFFEINE); + if (allArguments != null && allArguments.length > 0 && allArguments[0] != null) { + if (allArguments[0] instanceof String) { + Tags.CACHE_KEY.set(span, allArguments[0].toString()); + } else if (allArguments[0] instanceof Map) { + String keys = ((Map) allArguments[0]) + .keySet().stream().map(String::valueOf) + .collect(Collectors.joining(",")); + Tags.CACHE_KEY.set(span, keys); + } else if (allArguments[0] instanceof Set) { + String keys = ((Set) allArguments[0]) + .stream().map(String::valueOf) + .collect(Collectors.joining(",")); + Tags.CACHE_KEY.set(span, keys); + } + } + 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); + } + + @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/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 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 OPERATION_MAPPING_WRITE = new HashSet<>(Arrays.asList( + "put", + "putAll", + "remove", + "clear" + )); + public static Set 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/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..bb2d138b86 --- /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,85 @@ +/* + * 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 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.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_ENHANCE_CLASS = "org.apache.skywalking.apm.plugin.caffeine.v3.CaffeineInterceptor"; + + // 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 getMethodsMatcher() { + return named(GET_IF_PRESENT_METHOD) + .and(takesArguments(2)) + .or(named(GET_ALL_PRESENT_METHOD).and(takesArguments(1))) + .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(PUT_ALL_METHOD).and(takesArguments(1))) + .or(named(CLEAR_METHOD).and(takesArguments(0))); + } + + @Override + public String getMethodsInterceptor() { + return CAFFEINE_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..27153dc843 --- /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,144 @@ +/* + * 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 CaffeineInterceptor caffeineInterceptor; + 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 { + caffeineInterceptor = new CaffeineInterceptor(); + 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 { + caffeineInterceptor.beforeMethod(null, getAllPresentMethod, null, null, null); + caffeineInterceptor.handleMethodException(null, getAllPresentMethod, null, null, exception); + caffeineInterceptor.afterMethod(null, getAllPresentMethod, null, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testGetIfPresentSuccess() throws Throwable { + caffeineInterceptor.beforeMethod(null, getIfPresentMethod, operateObjectArguments, null, null); + caffeineInterceptor.handleMethodException(null, getIfPresentMethod, operateObjectArguments, null, exception); + caffeineInterceptor.afterMethod(null, getIfPresentMethod, operateObjectArguments, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testComputeIfAbsentMethodSuccess() throws Throwable { + caffeineInterceptor.beforeMethod(null, computeIfAbsentMethod, null, null, null); + caffeineInterceptor.handleMethodException(null, computeIfAbsentMethod, null, null, exception); + caffeineInterceptor.afterMethod(null, computeIfAbsentMethod, null, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testPutMethodSuccess() throws Throwable { + caffeineInterceptor.beforeMethod(null, putMethod, operateObjectArguments, null, null); + caffeineInterceptor.handleMethodException(null, putMethod, operateObjectArguments, null, exception); + caffeineInterceptor.afterMethod(null, putMethod, operateObjectArguments, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testPutAllMethodSuccess() throws Throwable { + caffeineInterceptor.beforeMethod(null, putAllMethod, null, null, null); + caffeineInterceptor.handleMethodException(null, putAllMethod, null, null, exception); + caffeineInterceptor.afterMethod(null, putAllMethod, null, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testRemoveMethodSuccess() throws Throwable { + caffeineInterceptor.beforeMethod(null, removeMethod, operateObjectArguments, null, null); + caffeineInterceptor.handleMethodException(null, removeMethod, operateObjectArguments, null, exception); + caffeineInterceptor.afterMethod(null, removeMethod, operateObjectArguments, null, null); + List traceSegments = segmentStorage.getTraceSegments(); + Assert.assertThat(traceSegments.size(), is(1)); + } + + @Test + public void testClearMethodSuccess() throws Throwable { + caffeineInterceptor.beforeMethod(null, cleanMethod, null, null, null); + caffeineInterceptor.handleMethodException(null, cleanMethod, null, null, exception); + caffeineInterceptor.afterMethod(null, cleanMethod, null, null, null); + List 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 @@ trace-sampler-cpu-policy-plugin nacos-client-2.x-plugin netty-http-4.1.x-plugin + caffeine-3.x-plugin 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 | SW_PLUGIN_MEMCACHED_OPERATION_MAPPING_READ | `set,add,replace,append,prepend,cas,delete,touch,incr,decr` | | `plugin.ehcache.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_EHCACHE_OPERATION_MAPPING_WRITE | `get,getAll,getQuiet,getKeys,getKeysWithExpiryCheck,getKeysNoDuplicateCheck,releaseRead,tryRead,getWithLoader,getAll,loadAll,getAllWithLoader` | | `plugin.ehcache.operation_mapping_read` | Specify which command should be converted to `read` operation | SW_PLUGIN_EHCACHE_OPERATION_MAPPING_READ | `tryRemoveImmediately,remove,removeAndReturnElement,removeAll,removeQuiet,removeWithWriter,put,putAll,replace,removeQuiet,removeWithWriter,removeElement,removeAll,putWithWriter,putQuiet,putIfAbsent,putIfAbsent` | -| `plugin.guavacache.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_GUAVACACHE_OPERATION_MAPPING_WRITE | `getIfPresent,get,getAllPresent,size` | -| `plugin.guavacache.operation_mapping_read` | Specify which command should be converted to `read` operation | SW_PLUGIN_GUAVACACHE_OPERATION_MAPPING_READ | `put,putAll,invalidate,invalidateAll,invalidateAll,cleanUp` -| `plugin.nettyhttp.collect_request_body` | This config item controls that whether the Netty-http plugin should collect the http body of the request. | SW_PLUGIN_NETTY_HTTP_COLLECT_REQUEST_BODY | `false` | -| `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. | SW_PLUGIN_NETTY_HTTP_FILTER_LENGTH_LIMIT | `1024` | -| `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 `,` | SW_PLUGIN_NETTY_HTTP_SUPPORTED_CONTENT_TYPES_PREFIX | `application/json,text/` | -| `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. | SW_PLUGIN_SOLON_HTTP_PARAMS_LENGTH_THRESHOLD | `0` | -| `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. | SW_PLUGIN_SOLON_INCLUDE_HTTP_HEADERS | ``(No header would be collected) | -| `plugin.solon.http_body_length_threshold` | Define the max length of collected HTTP body. The default value(=0) means not collecting. | SW_PLUGIN_SOLON_HTTP_BODY_LENGTH_THRESHOLD | `0` | - +| `plugin.guavacache.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_GUAVACACHE_OPERATION_MAPPING_WRITE | `put,putAll,invalidate,invalidateAll,invalidateAll,cleanUp` | +| `plugin.guavacache.operation_mapping_read` | Specify which command should be converted to `read` operation | SW_PLUGIN_GUAVACACHE_OPERATION_MAPPING_READ | `getIfPresent,get,getAllPresent,size` | +| `plugin.nettyhttp.collect_request_body` | This config item controls that whether the Netty-http plugin should collect the http body of the request. | SW_PLUGIN_NETTY_HTTP_COLLECT_REQUEST_BODY | `false` | +| `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. | SW_PLUGIN_NETTY_HTTP_FILTER_LENGTH_LIMIT | `1024` | +| `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 `,` | SW_PLUGIN_NETTY_HTTP_SUPPORTED_CONTENT_TYPES_PREFIX | `application/json,text/` | +| `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. | SW_PLUGIN_SOLON_HTTP_PARAMS_LENGTH_THRESHOLD | `0` | +| `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. | SW_PLUGIN_SOLON_INCLUDE_HTTP_HEADERS | ``(No header would be collected) | +| `plugin.solon.http_body_length_threshold` | Define the max length of collected HTTP body. The default value(=0) means not collecting. | SW_PLUGIN_SOLON_HTTP_BODY_LENGTH_THRESHOLD | `0` | +| `plugin.caffeine.operation_mapping_write` | Specify which command should be converted to `write` operation | SW_PLUGIN_EHCACHE_OPERATION_MAPPING_WRITE | `put,putAll,remove,clear` | +| `plugin.caffeine.operation_mapping_read` | Specify which command should be converted to `read` operation | SW_PLUGIN_EHCACHE_OPERATION_MAPPING_READ | `getIfPresent,getAllPresent,computeIfAbsent` | # 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..2185eed8b7 --- /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.key, value: "1" } + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: put } + - { key: cache.op, value: write } + 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.key, value: "2" } + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: putAll } + - { key: cache.op, value: write } + 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.key, value: "1" } + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: computeIfAbsent } + - { key: cache.op, value: read } + - 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.key, value: "1" } + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: getIfPresent } + - { key: cache.op, value: read } + 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.key, value: '2' } + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: getAllPresent } + - { key: cache.op, value: read } + - 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.key, value: '1' } + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: remove } + - { key: cache.op, value: write } + - 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.key, value: '2' } + - { key: cache.type, value: Caffeine } + - { key: cache.cmd, value: remove } + - { key: cache.op, value: write } + - 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 @@ + + + + + org.apache.skywalking.apm.testcase + caffeine-3.x-scenario + 1.0.0 + jar + + 4.0.0 + + + 17 + UTF-8 + 3.8.1 + 6.0.2 + ${test.framework.version} + 3.0.0 + 3.1.8 + + + skywalking-caffeine-3.x-scenario + + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-starter-web + + + com.github.ben-manes.caffeine + caffeine + ${caffeine.version} + + + + + caffeine-3.x-scenario + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${compiler.version} + ${compiler.version} + ${project.build.sourceEncoding} + + + + org.apache.maven.plugins + maven-assembly-plugin + + + assemble + package + + single + + + + src/main/assembly/assembly.xml + + ./target/ + + + + + + + \ 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 @@ + + + + + zip + + + + + ./bin + 0775 + + + + + + ${project.build.directory}/caffeine-3.x-scenario.jar + ./libs + 0775 + + + 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..02e87ac9ee --- /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,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.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 { + + @GetMapping("/case/caffeine") + public void testCase() { + try { + Cache caffeine = Caffeine.newBuilder().build(); + Map 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 From c7ffa1d32f6c6e1c04966209b4d76f158e69ad02 Mon Sep 17 00:00:00 2001 From: Zixin Zhou Date: Sat, 28 Dec 2024 19:18:08 +0800 Subject: [PATCH 2/4] Increase the CI build timeout-minutes to 90 --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 7dd32cc78f5a3035b8785c7619d076d7d59db9c1 Mon Sep 17 00:00:00 2001 From: Zixin Zhou Date: Sat, 28 Dec 2024 22:30:11 +0800 Subject: [PATCH 3/4] Fix CR problems --- ....java => AbstractCaffeineInterceptor.java} | 37 +++++---------- .../v3/CaffeineIterableInterceptor.java | 46 +++++++++++++++++++ .../caffeine/v3/CaffeineMapInterceptor.java | 45 ++++++++++++++++++ .../v3/CaffeineStringInterceptor.java | 40 ++++++++++++++++ .../v3/define/CaffeineInstrumentation.java | 44 ++++++++++++++++-- .../config/expectedData.yaml | 14 +++--- .../controller/CaffeineController.java | 3 +- 7 files changed, 190 insertions(+), 39 deletions(-) rename apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/{CaffeineInterceptor.java => AbstractCaffeineInterceptor.java} (75%) create mode 100644 apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineIterableInterceptor.java create mode 100644 apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineMapInterceptor.java create mode 100644 apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineStringInterceptor.java diff --git a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptor.java b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/AbstractCaffeineInterceptor.java similarity index 75% rename from apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptor.java rename to apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/AbstractCaffeineInterceptor.java index 3051fba80e..9c66cabd08 100644 --- a/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/CaffeineInterceptor.java +++ b/apm-sniffer/optional-plugins/caffeine-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/caffeine/v3/AbstractCaffeineInterceptor.java @@ -19,9 +19,6 @@ package org.apache.skywalking.apm.plugin.caffeine.v3; import java.lang.reflect.Method; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; 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; @@ -33,36 +30,24 @@ import static org.apache.skywalking.apm.plugin.caffeine.v3.CaffeineOperationTransform.transformOperation; -public class CaffeineInterceptor implements InstanceMethodsAroundInterceptor { +abstract public class AbstractCaffeineInterceptor implements InstanceMethodsAroundInterceptor { - @Override - public void beforeMethod(final EnhancedInstance objInst, - final Method method, - final Object[] allArguments, - final Class[] argumentsTypes, - final MethodInterceptResult result) throws Throwable { - String methodName = method.getName(); + protected AbstractSpan generateSpanInfo(String methodName) { AbstractSpan span = ContextManager.createLocalSpan("Caffeine/" + methodName); span.setComponent(ComponentsDefine.CAFFEINE); - if (allArguments != null && allArguments.length > 0 && allArguments[0] != null) { - if (allArguments[0] instanceof String) { - Tags.CACHE_KEY.set(span, allArguments[0].toString()); - } else if (allArguments[0] instanceof Map) { - String keys = ((Map) allArguments[0]) - .keySet().stream().map(String::valueOf) - .collect(Collectors.joining(",")); - Tags.CACHE_KEY.set(span, keys); - } else if (allArguments[0] instanceof Set) { - String keys = ((Set) allArguments[0]) - .stream().map(String::valueOf) - .collect(Collectors.joining(",")); - Tags.CACHE_KEY.set(span, keys); - } - } 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 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/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 index bb2d138b86..50e5b3751a 100644 --- 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 @@ -18,6 +18,7 @@ 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; @@ -26,6 +27,7 @@ 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; @@ -33,7 +35,9 @@ public class CaffeineInstrumentation extends ClassInstanceMethodsEnhancePluginDe 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_ENHANCE_CLASS = "org.apache.skywalking.apm.plugin.caffeine.v3.CaffeineInterceptor"; + 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"; @@ -48,7 +52,7 @@ public class CaffeineInstrumentation extends ClassInstanceMethodsEnhancePluginDe protected ClassMatch enhanceClass() { return byMultiClassMatch(BOUNDED_LOCAL_INTERCEPT_CLASS, UNBOUNDED_LOCAL_INTERCEPT_CLASS); } - + @Override public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { return new ConstructorInterceptPoint[0]; @@ -62,17 +66,47 @@ public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { public ElementMatcher getMethodsMatcher() { return named(GET_IF_PRESENT_METHOD) .and(takesArguments(2)) - .or(named(GET_ALL_PRESENT_METHOD).and(takesArguments(1))) .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(PUT_ALL_METHOD).and(takesArguments(1))) .or(named(CLEAR_METHOD).and(takesArguments(0))); } @Override public String getMethodsInterceptor() { - return CAFFEINE_ENHANCE_CLASS; + return CAFFEINE_STRING_ENHANCE_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher 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 getMethodsMatcher() { + return named(PUT_ALL_METHOD).and(takesArgument(0, Map.class)); + } + + @Override + public String getMethodsInterceptor() { + return CAFFEINE_MAP_ENHANCE_CLASS; } @Override diff --git a/test/plugin/scenarios/caffeine-3.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/caffeine-3.x-scenario/config/expectedData.yaml index 2185eed8b7..133ede4755 100755 --- a/test/plugin/scenarios/caffeine-3.x-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/caffeine-3.x-scenario/config/expectedData.yaml @@ -31,10 +31,10 @@ segmentItems: spanType: Local peer: '' tags: - - { key: cache.key, value: "1" } - { 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 @@ -47,10 +47,10 @@ segmentItems: spanType: Local peer: '' tags: - - { key: cache.key, value: "2" } - { 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 @@ -63,10 +63,10 @@ segmentItems: spanType: Local peer: '' tags: - - { key: cache.key, value: "1" } - { 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 @@ -78,10 +78,10 @@ segmentItems: spanType: Local peer: '' tags: - - { key: cache.key, value: "1" } - { 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 @@ -95,10 +95,10 @@ segmentItems: peer: '' skipAnalysis: 'false' tags: - - { key: cache.key, value: '2' } - { 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 @@ -111,10 +111,10 @@ segmentItems: peer: '' skipAnalysis: 'false' tags: - - { key: cache.key, value: '1' } - { 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 @@ -127,10 +127,10 @@ segmentItems: peer: '' skipAnalysis: 'false' tags: - - { key: cache.key, value: '2' } - { 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 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 index 02e87ac9ee..f8bad1d288 100644 --- 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 @@ -28,10 +28,11 @@ @RestController public class CaffeineController { + Cache caffeine = Caffeine.newBuilder().build(); + @GetMapping("/case/caffeine") public void testCase() { try { - Cache caffeine = Caffeine.newBuilder().build(); Map data = new HashMap<>(); data.put("2", "value-2"); From 06059faf4a6090a5bb28ad39585739e7d5a46f48 Mon Sep 17 00:00:00 2001 From: Zixin Zhou Date: Sat, 28 Dec 2024 23:40:15 +0800 Subject: [PATCH 4/4] Fix CI problem --- .../caffeine/v3/CaffeineInterceptorTest.java | 53 ++++++++++--------- 1 file changed, 29 insertions(+), 24 deletions(-) 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 index 27153dc843..7642ae110d 100644 --- 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 @@ -44,7 +44,9 @@ public class CaffeineInterceptorTest { @SegmentStoragePoint private SegmentStorage segmentStorage; - private CaffeineInterceptor caffeineInterceptor; + private CaffeineIterableInterceptor caffeineIterableInterceptor; + private CaffeineMapInterceptor caffeineMapInterceptor; + private CaffeineStringInterceptor caffeineStringInterceptor; private Object[] operateObjectArguments; private Exception exception; @@ -65,7 +67,9 @@ public class CaffeineInterceptorTest { @Before public void setUp() throws Exception { - caffeineInterceptor = new CaffeineInterceptor(); + 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"); @@ -81,63 +85,64 @@ public void setUp() throws Exception { @Test public void testGetAllPresentSuccess() throws Throwable { - caffeineInterceptor.beforeMethod(null, getAllPresentMethod, null, null, null); - caffeineInterceptor.handleMethodException(null, getAllPresentMethod, null, null, exception); - caffeineInterceptor.afterMethod(null, getAllPresentMethod, null, null, null); + caffeineIterableInterceptor.beforeMethod(null, getAllPresentMethod, null, null, null); + caffeineIterableInterceptor.handleMethodException(null, getAllPresentMethod, null, null, exception); + caffeineIterableInterceptor.afterMethod(null, getAllPresentMethod, null, null, null); List traceSegments = segmentStorage.getTraceSegments(); Assert.assertThat(traceSegments.size(), is(1)); } @Test public void testGetIfPresentSuccess() throws Throwable { - caffeineInterceptor.beforeMethod(null, getIfPresentMethod, operateObjectArguments, null, null); - caffeineInterceptor.handleMethodException(null, getIfPresentMethod, operateObjectArguments, null, exception); - caffeineInterceptor.afterMethod(null, getIfPresentMethod, operateObjectArguments, null, null); + caffeineStringInterceptor.beforeMethod(null, getIfPresentMethod, operateObjectArguments, null, null); + caffeineStringInterceptor.handleMethodException( + null, getIfPresentMethod, operateObjectArguments, null, exception); + caffeineStringInterceptor.afterMethod(null, getIfPresentMethod, operateObjectArguments, null, null); List traceSegments = segmentStorage.getTraceSegments(); Assert.assertThat(traceSegments.size(), is(1)); } @Test public void testComputeIfAbsentMethodSuccess() throws Throwable { - caffeineInterceptor.beforeMethod(null, computeIfAbsentMethod, null, null, null); - caffeineInterceptor.handleMethodException(null, computeIfAbsentMethod, null, null, exception); - caffeineInterceptor.afterMethod(null, computeIfAbsentMethod, null, null, null); + caffeineStringInterceptor.beforeMethod(null, computeIfAbsentMethod, null, null, null); + caffeineStringInterceptor.handleMethodException(null, computeIfAbsentMethod, null, null, exception); + caffeineStringInterceptor.afterMethod(null, computeIfAbsentMethod, null, null, null); List traceSegments = segmentStorage.getTraceSegments(); Assert.assertThat(traceSegments.size(), is(1)); } @Test public void testPutMethodSuccess() throws Throwable { - caffeineInterceptor.beforeMethod(null, putMethod, operateObjectArguments, null, null); - caffeineInterceptor.handleMethodException(null, putMethod, operateObjectArguments, null, exception); - caffeineInterceptor.afterMethod(null, putMethod, operateObjectArguments, null, null); + caffeineStringInterceptor.beforeMethod(null, putMethod, operateObjectArguments, null, null); + caffeineStringInterceptor.handleMethodException(null, putMethod, operateObjectArguments, null, exception); + caffeineStringInterceptor.afterMethod(null, putMethod, operateObjectArguments, null, null); List traceSegments = segmentStorage.getTraceSegments(); Assert.assertThat(traceSegments.size(), is(1)); } @Test public void testPutAllMethodSuccess() throws Throwable { - caffeineInterceptor.beforeMethod(null, putAllMethod, null, null, null); - caffeineInterceptor.handleMethodException(null, putAllMethod, null, null, exception); - caffeineInterceptor.afterMethod(null, putAllMethod, null, null, null); + caffeineMapInterceptor.beforeMethod(null, putAllMethod, null, null, null); + caffeineMapInterceptor.handleMethodException(null, putAllMethod, null, null, exception); + caffeineMapInterceptor.afterMethod(null, putAllMethod, null, null, null); List traceSegments = segmentStorage.getTraceSegments(); Assert.assertThat(traceSegments.size(), is(1)); } @Test public void testRemoveMethodSuccess() throws Throwable { - caffeineInterceptor.beforeMethod(null, removeMethod, operateObjectArguments, null, null); - caffeineInterceptor.handleMethodException(null, removeMethod, operateObjectArguments, null, exception); - caffeineInterceptor.afterMethod(null, removeMethod, operateObjectArguments, null, null); + caffeineStringInterceptor.beforeMethod(null, removeMethod, operateObjectArguments, null, null); + caffeineStringInterceptor.handleMethodException(null, removeMethod, operateObjectArguments, null, exception); + caffeineStringInterceptor.afterMethod(null, removeMethod, operateObjectArguments, null, null); List traceSegments = segmentStorage.getTraceSegments(); Assert.assertThat(traceSegments.size(), is(1)); } - + @Test public void testClearMethodSuccess() throws Throwable { - caffeineInterceptor.beforeMethod(null, cleanMethod, null, null, null); - caffeineInterceptor.handleMethodException(null, cleanMethod, null, null, exception); - caffeineInterceptor.afterMethod(null, cleanMethod, null, null, null); + caffeineStringInterceptor.beforeMethod(null, cleanMethod, null, null, null); + caffeineStringInterceptor.handleMethodException(null, cleanMethod, null, null, exception); + caffeineStringInterceptor.afterMethod(null, cleanMethod, null, null, null); List traceSegments = segmentStorage.getTraceSegments(); Assert.assertThat(traceSegments.size(), is(1)); }