From 88d8e6398fd3fe5684ca04bd77eaa40e69e099af Mon Sep 17 00:00:00 2001 From: peachisai <96932958+peachisai@users.noreply.github.com> Date: Sun, 2 Jul 2023 21:16:23 +0800 Subject: [PATCH] Support to trace redisson lock (#567) --- CHANGES.md | 1 + .../apm/agent/core/context/tag/Tags.java | 16 ++++ .../v3/RedissonHighLevelLockInterceptor.java | 59 ++++++++++++ .../redisson/v3/RedissonLockInterceptor.java | 59 ++++++++++++ .../define/RedissonLockInstrumentation.java | 91 +++++++++++++++++++ .../src/main/resources/skywalking-plugin.def | 3 +- .../config/expectedData.yaml | 64 +++++++++++++ .../redisson/controller/CaseController.java | 11 +++ 8 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedissonHighLevelLockInterceptor.java create mode 100644 apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedissonLockInterceptor.java create mode 100644 apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/define/RedissonLockInstrumentation.java diff --git a/CHANGES.md b/CHANGES.md index 8b411b04c..0e56e0014 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -140,6 +140,7 @@ Callable { * Add WebSphere Liberty 23.x plugin * Add Plugin to support aerospike Java client * Add ClickHouse parsing to the jdbc-common plugin. +* Support to trace redisson lock #### Documentation diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/tag/Tags.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/tag/Tags.java index df6988c5e..e53824136 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/tag/Tags.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/context/tag/Tags.java @@ -139,6 +139,22 @@ public final class Tags { public static final StringTag SQL_PARAMETERS = new StringTag(19, "db.sql.parameters"); + /** + * LOCK_NAME records the lock name such as redisson lock name + */ + public static final StringTag LOCK_NAME = new StringTag(21, "lock.name"); + + /** + * LEASE_TIME represents the maximum time to hold the lock after it's acquisition + * in redisson plugin,it's unit is ms + */ + public static final StringTag LEASE_TIME = new StringTag(22, "lease.time"); + + /** + * THREAD_ID records the thread id + */ + public static final StringTag THREAD_ID = new StringTag(23, "thread.id"); + /** * Creates a {@code StringTag} with the given key and cache it, if it's created before, simply return it without * creating a new one. diff --git a/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedissonHighLevelLockInterceptor.java b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedissonHighLevelLockInterceptor.java new file mode 100644 index 000000000..ef0a0585f --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedissonHighLevelLockInterceptor.java @@ -0,0 +1,59 @@ +/* + * 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.redisson.v3; + +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 org.redisson.api.RLock; + +import java.lang.reflect.Method; +import java.util.concurrent.TimeUnit; + +public class RedissonHighLevelLockInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + AbstractSpan span = ContextManager.createLocalSpan("Redisson/LOCK"); + span.setComponent(ComponentsDefine.REDISSON); + SpanLayer.asCache(span); + RLock rLock = (RLock) objInst; + Tags.LOCK_NAME.set(span, rLock.getName()); + Tags.CACHE_TYPE.set(span, "Redis"); + TimeUnit unit = (TimeUnit) allArguments[2]; + Tags.LEASE_TIME.set(span, String.valueOf(unit.toMillis((Long) allArguments[1]))); + Tags.THREAD_ID.set(span, String.valueOf(allArguments[3])); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedissonLockInterceptor.java b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedissonLockInterceptor.java new file mode 100644 index 000000000..77a31a28a --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/RedissonLockInterceptor.java @@ -0,0 +1,59 @@ +/* + * 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.redisson.v3; + +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 org.redisson.api.RLock; + +import java.lang.reflect.Method; +import java.util.concurrent.TimeUnit; + +public class RedissonLockInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { + AbstractSpan span = ContextManager.createLocalSpan("Redisson/LOCK"); + span.setComponent(ComponentsDefine.REDISSON); + SpanLayer.asCache(span); + RLock rLock = (RLock) objInst; + Tags.LOCK_NAME.set(span, rLock.getName()); + Tags.CACHE_TYPE.set(span, "Redis"); + TimeUnit unit = (TimeUnit) allArguments[1]; + Tags.LEASE_TIME.set(span, String.valueOf(unit.toMillis((Long) allArguments[0]))); + Tags.THREAD_ID.set(span, String.valueOf(allArguments[2])); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Object ret) throws Throwable { + ContextManager.stopSpan(); + return ret; + } + + @Override + public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/define/RedissonLockInstrumentation.java b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/define/RedissonLockInstrumentation.java new file mode 100644 index 000000000..f17c0af81 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/redisson/v3/define/RedissonLockInstrumentation.java @@ -0,0 +1,91 @@ +/* + * 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.redisson.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 org.apache.skywalking.apm.agent.core.plugin.match.HierarchyMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.MultiClassNameMatch; +import org.apache.skywalking.apm.agent.core.plugin.match.logical.LogicalMatchOperation; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; + +public class RedissonLockInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String REDISSON_LOCK_CLASS = "org.redisson.RedissonLock"; + + private static final String REDISSON_SPIN_LOCK_CLASS = "org.redisson.RedissonSpinLock"; + + private static final String REDISSON_LOCK_INTERCEPTOR = "org.apache.skywalking.apm.plugin.redisson.v3.RedissonLockInterceptor"; + + private static final String REDISSON_HIGH_LEVEL_LOCK_INTERCEPTOR = "org.apache.skywalking.apm.plugin.redisson.v3.RedissonHighLevelLockInterceptor"; + + @Override + protected ClassMatch enhanceClass() { + return LogicalMatchOperation.or(HierarchyMatch.byHierarchyMatch(REDISSON_LOCK_CLASS), MultiClassNameMatch.byMultiClassMatch(REDISSON_LOCK_CLASS, REDISSON_SPIN_LOCK_CLASS)); + } + + @Override + public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[]{ + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("tryLockInnerAsync").and(takesArgumentWithType(1, "java.util.concurrent.TimeUnit")); + } + + @Override + public String getMethodsInterceptor() { + return REDISSON_LOCK_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("tryLockInnerAsync").and(takesArgumentWithType(2, "java.util.concurrent.TimeUnit")); + } + + @Override + public String getMethodsInterceptor() { + return REDISSON_HIGH_LEVEL_LOCK_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/resources/skywalking-plugin.def index fc7b5bf5a..765de7cd4 100644 --- a/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/apm-sdk-plugin/redisson-3.x-plugin/src/main/resources/skywalking-plugin.def @@ -16,4 +16,5 @@ redisson-3.x=org.apache.skywalking.apm.plugin.redisson.v3.define.ConnectionManagerInstrumentation redisson-3.x=org.apache.skywalking.apm.plugin.redisson.v3.define.RedisConnectionInstrumentation -redisson-3.x=org.apache.skywalking.apm.plugin.redisson.v3.define.RedisClientInstrumentation \ No newline at end of file +redisson-3.x=org.apache.skywalking.apm.plugin.redisson.v3.define.RedisClientInstrumentation +redisson-3.x=org.apache.skywalking.apm.plugin.redisson.v3.define.RedissonLockInstrumentation \ No newline at end of file diff --git a/test/plugin/scenarios/redisson-scenario/config/expectedData.yaml b/test/plugin/scenarios/redisson-scenario/config/expectedData.yaml index 2b54ed3b8..e925f320b 100644 --- a/test/plugin/scenarios/redisson-scenario/config/expectedData.yaml +++ b/test/plugin/scenarios/redisson-scenario/config/expectedData.yaml @@ -51,6 +51,70 @@ segmentItems: - {key: cache.instance, value: not null} - {key: cache.cmd, value: BATCH_EXECUTE} skipAnalysis: 'false' + - operationName: Redisson/EVAL + parentSpanId: 3 + spanId: nq 0 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 56 + isError: false + spanType: Exit + peer: not null + tags: + - { key: cache.type, value: Redis } + - { key: cache.instance, value: not null } + - { key: cache.cmd, value: EVAL } + - { key: cache.key, value: not null } + skipAnalysis: 'false' + - operationName: Redisson/LOCK + parentSpanId: 0 + spanId: nq 0 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 56 + isError: false + spanType: Local + peer: '' + tags: + - { key: lock.name, value: lock_a } + - { key: cache.type, value: Redis } + - { key: lease.time, value: not null } + - { key: thread.id, value: not null } + skipAnalysis: false + - operationName: Redisson/EVAL + parentSpanId: 5 + spanId: nq 0 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 56 + isError: false + spanType: Exit + peer: not null + tags: + - { key: cache.type, value: Redis } + - { key: cache.instance, value: not null } + - { key: cache.cmd, value: EVAL } + - { key: cache.key, value: not null } + skipAnalysis: 'false' + - operationName: Redisson/LOCK + parentSpanId: 0 + spanId: nq 0 + spanLayer: Cache + startTime: not null + endTime: not null + componentId: 56 + isError: false + spanType: Local + peer: '' + tags: + - { key: lock.name, value: lock_b } + - { key: cache.type, value: Redis } + - { key: lease.time, value: not null } + - { key: thread.id, value: not null } + skipAnalysis: false - operationName: GET:/case/redisson-case parentSpanId: -1 spanId: 0 diff --git a/test/plugin/scenarios/redisson-scenario/src/main/java/test/apache/skywalking/apm/testcase/redisson/controller/CaseController.java b/test/plugin/scenarios/redisson-scenario/src/main/java/test/apache/skywalking/apm/testcase/redisson/controller/CaseController.java index e45d53ebb..ed12c5927 100644 --- a/test/plugin/scenarios/redisson-scenario/src/main/java/test/apache/skywalking/apm/testcase/redisson/controller/CaseController.java +++ b/test/plugin/scenarios/redisson-scenario/src/main/java/test/apache/skywalking/apm/testcase/redisson/controller/CaseController.java @@ -23,6 +23,7 @@ import javax.annotation.PostConstruct; import org.redisson.Redisson; import org.redisson.api.RBatch; import org.redisson.api.RBucket; +import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.beans.factory.annotation.Value; @@ -58,6 +59,16 @@ public class CaseController { batch.getBucket("batch_k_b").setAsync("batch_v_b"); batch.getBucket("batch_k_b").expireAsync(20, TimeUnit.SECONDS); batch.execute(); + + RLock lockA = client.getLock("lock_a"); + lockA.lock(10L, TimeUnit.SECONDS); + RLock lockB = client.getLock("lock_b"); + try { + lockB.tryLock(10L, 20L, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return "Success"; }