You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by ke...@apache.org on 2020/02/12 06:03:11 UTC

[skywalking] branch master updated: Tag annotation supports returned expression (#4327)

This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking.git


The following commit(s) were added to refs/heads/master by this push:
     new e5366c0  Tag annotation supports returned expression (#4327)
e5366c0 is described below

commit e5366c0921f0ec0052db33aa87ca72257cd3c7f1
Author: lxliuxuankb <38...@qq.com>
AuthorDate: Wed Feb 12 14:03:02 2020 +0800

    Tag annotation supports returned expression (#4327)
    
    Co-authored-by: 吴晟 Wu Sheng <wu...@foxmail.com>
    Co-authored-by: kezhenxu94 <ke...@163.com>
---
 .../apm/agent/core/util/CustomizeExpression.java   |  24 +++
 .../trace/TagAnnotationMethodInterceptor.java      |  39 +++--
 .../trace/TraceAnnotationMethodInterceptor.java    |  37 +++--
 .../apm/toolkit/activation/util/TagUtil.java       |  43 ++++++
 .../activation/trace/TagAnnotationTest.java        | 169 +++++++++++++++++++++
 .../activation/trace/TraceAnnotationTest.java      |  76 ++++++++-
 .../java-agent/Application-toolkit-trace.md        |   8 +-
 .../config/expectedData.yaml                       |  15 ++
 .../apache/skywalking/apm/toolkit/model/User.java  |  45 ++++++
 .../toolkit/controller/TestController.java         |   1 +
 .../testcase/toolkit/controller/TestService.java   |   6 +
 11 files changed, 435 insertions(+), 28 deletions(-)

diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/util/CustomizeExpression.java b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/util/CustomizeExpression.java
index df261c7..c03a121 100644
--- a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/util/CustomizeExpression.java
+++ b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/util/CustomizeExpression.java
@@ -47,6 +47,20 @@ public class CustomizeExpression {
         return context;
     }
 
+    public static Map<String, Object> evaluationReturnContext(Object ret)  {
+        Map<String, Object> context = new HashMap<>();
+        Field[] fields = ret.getClass().getDeclaredFields();
+        for (Field field : fields) {
+            field.setAccessible(true);
+            try {
+                context.put(field.getName(), field.get(ret));
+            } catch (Exception e) {
+                logger.debug("evaluationReturnContext error, ret is {}, exception is {}", ret, e.getMessage());
+            }
+        }
+        return context;
+    }
+
     public static String parseExpression(String expression, Map<String, Object> context) {
         try {
             String[] es = expression.split("\\.");
@@ -56,7 +70,17 @@ public class CustomizeExpression {
             logger.debug("parse expression error, expression is {}, exception is {}", expression, e.getMessage());
         }
         return "null";
+    }
 
+    public static String parseReturnExpression(String expression, Map<String, Object> context) {
+        try {
+            String[] es = expression.split("\\.");
+            Object o = context.get(es[1]);
+            return o == null ? "null" : String.valueOf(parse(es, o, 1));
+        } catch (Exception e) {
+            logger.debug("parse expression error, expression is {}, exception is {}", expression, e.getMessage());
+        }
+        return "null";
     }
 
     private static Object parse(String[] expressions, Object o, int i) {
diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/trace/TagAnnotationMethodInterceptor.java b/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/trace/TagAnnotationMethodInterceptor.java
index 50bcf33..fcb701c 100644
--- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/trace/TagAnnotationMethodInterceptor.java
+++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/trace/TagAnnotationMethodInterceptor.java
@@ -22,8 +22,8 @@ import java.lang.reflect.Method;
 import java.util.Map;
 
 import org.apache.skywalking.apm.agent.core.context.ContextManager;
-import org.apache.skywalking.apm.agent.core.context.tag.StringTag;
 import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.apache.skywalking.apm.toolkit.activation.util.TagUtil;
 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;
@@ -46,23 +46,42 @@ public class TagAnnotationMethodInterceptor implements InstanceMethodsAroundInte
         final Tags tags = method.getAnnotation(Tags.class);
         if (tags != null && tags.value().length > 0) {
             for (final Tag tag : tags.value()) {
-                tagSpan(activeSpan, tag, context);
+                if (!TagUtil.isReturnTag(tag.value())) {
+                    TagUtil.tagParamsSpan(activeSpan, context, tag);
+                }
             }
         }
 
         final Tag tag = method.getAnnotation(Tag.class);
-        if (tag != null) {
-            tagSpan(activeSpan, tag, context);
+        if (tag != null && !TagUtil.isReturnTag(tag.value())) {
+            TagUtil.tagParamsSpan(activeSpan, context, tag);
         }
     }
 
-    private void tagSpan(final AbstractSpan span, final Tag tag, final Map<String, Object> context) {
-        new StringTag(tag.key()).set(span, CustomizeExpression.parseExpression(tag.value(), context));
-    }
-
     @Override
-    public Object afterMethod(final EnhancedInstance objInst, final Method method, final Object[] allArguments,
-        final Class<?>[] argumentsTypes, final Object ret) {
+    public Object afterMethod(
+        final EnhancedInstance objInst,
+        final Method method,
+        final Object[] allArguments,
+        final Class<?>[] argumentsTypes,
+        final Object ret) {
+        if (ret == null || !ContextManager.isActive()) {
+            return ret;
+        }
+        final AbstractSpan localSpan = ContextManager.activeSpan();
+        final Map<String, Object> context = CustomizeExpression.evaluationReturnContext(ret);
+        final Tags tags = method.getAnnotation(Tags.class);
+        if (tags != null && tags.value().length > 0) {
+            for (final Tag tag : tags.value()) {
+                if (TagUtil.isReturnTag(tag.value())) {
+                    TagUtil.tagReturnSpanSpan(localSpan, context, tag);
+                }
+            }
+        }
+        final Tag tag = method.getAnnotation(Tag.class);
+        if (tag != null && TagUtil.isReturnTag(tag.value())) {
+            TagUtil.tagReturnSpanSpan(localSpan, context, tag);
+        }
         return ret;
     }
 
diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/trace/TraceAnnotationMethodInterceptor.java b/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/trace/TraceAnnotationMethodInterceptor.java
index 04de351..95c4fb5 100644
--- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/trace/TraceAnnotationMethodInterceptor.java
+++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/trace/TraceAnnotationMethodInterceptor.java
@@ -22,8 +22,8 @@ import java.lang.reflect.Method;
 import java.util.Map;
 
 import org.apache.skywalking.apm.agent.core.conf.Config;
-import org.apache.skywalking.apm.agent.core.context.tag.StringTag;
 import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.apache.skywalking.apm.toolkit.activation.util.TagUtil;
 import org.apache.skywalking.apm.agent.core.util.CustomizeExpression;
 import org.apache.skywalking.apm.toolkit.trace.Tag;
 import org.apache.skywalking.apm.toolkit.trace.Tags;
@@ -56,24 +56,41 @@ public class TraceAnnotationMethodInterceptor implements InstanceMethodsAroundIn
         final Tags tags = method.getAnnotation(Tags.class);
         if (tags != null && tags.value().length > 0) {
             for (final Tag tag : tags.value()) {
-                tagSpan(localSpan, tag, context);
+                if (!TagUtil.isReturnTag(tag.value())) {
+                    TagUtil.tagParamsSpan(localSpan, context, tag);
+                }
             }
         }
-
         final Tag tag = method.getAnnotation(Tag.class);
-        if (tag != null) {
-            tagSpan(localSpan, tag, context);
+        if (tag != null && !TagUtil.isReturnTag(tag.value())) {
+            TagUtil.tagParamsSpan(localSpan, context, tag);
         }
     }
 
-    private void tagSpan(final AbstractSpan span, final Tag tag, final Map<String, Object> context) {
-        new StringTag(tag.key()).set(span, CustomizeExpression.parseExpression(tag.value(), context));
-    }
-
     @Override
     public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
         Object ret) throws Throwable {
-        ContextManager.stopSpan();
+        try {
+            if (ret == null) {
+                return ret;
+            }
+            final AbstractSpan localSpan = ContextManager.activeSpan();
+            final Map<String, Object> context = CustomizeExpression.evaluationReturnContext(ret);
+            final Tags tags = method.getAnnotation(Tags.class);
+            if (tags != null && tags.value().length > 0) {
+                for (final Tag tag : tags.value()) {
+                    if (TagUtil.isReturnTag(tag.value())) {
+                        TagUtil.tagReturnSpanSpan(localSpan, context, tag);
+                    }
+                }
+            }
+            final Tag tag = method.getAnnotation(Tag.class);
+            if (tag != null && TagUtil.isReturnTag(tag.value())) {
+                TagUtil.tagReturnSpanSpan(localSpan, context, tag);
+            }
+        } finally {
+            ContextManager.stopSpan();
+        }
         return ret;
     }
 
diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/util/TagUtil.java b/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/util/TagUtil.java
new file mode 100644
index 0000000..f5b5153
--- /dev/null
+++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/main/java/org/apache/skywalking/apm/toolkit/activation/util/TagUtil.java
@@ -0,0 +1,43 @@
+/*
+ * 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.toolkit.activation.util;
+
+import java.util.Map;
+
+import org.apache.skywalking.apm.agent.core.context.tag.StringTag;
+import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.apache.skywalking.apm.agent.core.util.CustomizeExpression;
+import org.apache.skywalking.apm.toolkit.trace.Tag;
+
+public class TagUtil {
+    public static void tagParamsSpan(final AbstractSpan span, final Map<String, Object> context,
+                                     final Tag tag) {
+        new StringTag(tag.key()).set(span, CustomizeExpression.parseExpression(tag.value(), context));
+    }
+
+    public static void tagReturnSpanSpan(final AbstractSpan span, final Map<String, Object> context,
+                                         final Tag tag) {
+        new StringTag(tag.key()).set(span, CustomizeExpression.parseReturnExpression(tag.value(), context));
+    }
+
+    public static Boolean isReturnTag(String expression) {
+        String[] es = expression.split("\\.");
+        return es.length == 2 && "returnedObj".equals(es[0]);
+    }
+}
diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/test/java/org/apache/skywalking/apm/toolkit/activation/trace/TagAnnotationTest.java b/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/test/java/org/apache/skywalking/apm/toolkit/activation/trace/TagAnnotationTest.java
new file mode 100644
index 0000000..02ddf68
--- /dev/null
+++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/test/java/org/apache/skywalking/apm/toolkit/activation/trace/TagAnnotationTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.toolkit.activation.trace;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import org.apache.skywalking.apm.agent.core.context.ContextManager;
+import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan;
+import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment;
+import org.apache.skywalking.apm.agent.core.context.util.TagValuePair;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.apache.skywalking.apm.agent.test.helper.SegmentHelper;
+import org.apache.skywalking.apm.agent.test.helper.SpanHelper;
+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.SpanAssert;
+import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner;
+import org.apache.skywalking.apm.toolkit.trace.Tag;
+import org.apache.skywalking.apm.toolkit.trace.Tags;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.modules.junit4.PowerMockRunnerDelegate;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+@RunWith(PowerMockRunner.class)
+@PowerMockRunnerDelegate(TracingSegmentRunner.class)
+public class TagAnnotationTest {
+
+    @SegmentStoragePoint
+    private SegmentStorage storage;
+
+    @Rule
+    public AgentServiceRule serviceRule = new AgentServiceRule();
+
+    @Mock
+    private EnhancedInstance enhancedInstance;
+
+    private TagAnnotationMethodInterceptor methodInterceptor;
+    private ActiveSpanTagInterceptor tagInterceptor;
+    private Object[] tagParameters;
+    private Class[] tagParameterTypes;
+
+    @Before
+    public void setUp() throws Exception {
+        methodInterceptor = new TagAnnotationMethodInterceptor();
+        tagInterceptor = new ActiveSpanTagInterceptor();
+        tagParameters = new Object[] {"testTagKey", "testTagValue"};
+        tagParameterTypes = new Class[] {String.class, String.class};
+
+        String operationName = "testMethod";
+        ContextManager.createLocalSpan(operationName);
+    }
+
+    @Test
+    public void testTraceWithTag() throws Throwable {
+        Method testMethodWithTag = TestAnnotationMethodClass.class.getDeclaredMethod("testMethodWithTag", String.class);
+        methodInterceptor.beforeMethod(enhancedInstance, testMethodWithTag, new Object[]{"zhangsan"}, null, null);
+        methodInterceptor.afterMethod(enhancedInstance, testMethodWithTag, null, null, null);
+        ContextManager.stopSpan();
+        assertThat(storage.getTraceSegments().size(), is(1));
+        TraceSegment traceSegment = storage.getTraceSegments().get(0);
+        List<AbstractTracingSpan> spans = SegmentHelper.getSpans(traceSegment);
+        assertThat(spans.size(), is(1));
+
+        AbstractTracingSpan tracingSpan = spans.get(0);
+        assertThat(tracingSpan.getOperationName(), is("testMethod"));
+        SpanAssert.assertLogSize(tracingSpan, 0);
+        SpanAssert.assertTagSize(tracingSpan, 1);
+        List<TagValuePair> tags = SpanHelper.getTags(tracingSpan);
+        assertThat(tags.get(0).getKey().key(), is("username"));
+        assertThat(tags.get(0).getValue(), is("zhangsan"));
+    }
+
+    @Test
+    public void testTraceWithReturnTag() throws Throwable {
+        Method testMethodWithTag = TestAnnotationMethodClass.class.getDeclaredMethod("testMethodWithReturnTag", String.class, Integer.class);
+        methodInterceptor.beforeMethod(enhancedInstance, testMethodWithTag, new Object[]{"lisi", 14}, null, null);
+        methodInterceptor.afterMethod(enhancedInstance, testMethodWithTag, null, null, new User("lisi", 14));
+        ContextManager.stopSpan();
+        assertThat(storage.getTraceSegments().size(), is(1));
+        TraceSegment traceSegment = storage.getTraceSegments().get(0);
+        List<AbstractTracingSpan> spans = SegmentHelper.getSpans(traceSegment);
+        assertThat(spans.size(), is(1));
+
+        AbstractTracingSpan tracingSpan = spans.get(0);
+        assertThat(tracingSpan.getOperationName(), is("testMethod"));
+        SpanAssert.assertLogSize(tracingSpan, 0);
+        SpanAssert.assertTagSize(tracingSpan, 1);
+        List<TagValuePair> tags = SpanHelper.getTags(tracingSpan);
+
+        assertThat(tags.get(0).getKey().key(), is("username"));
+        assertThat(tags.get(0).getValue(), is("lisi"));
+
+    }
+
+    @Test
+    public void testTraceWithTags() throws Throwable {
+        Method testMethodWithTags = TestAnnotationMethodClass.class.getDeclaredMethod("testMethodWithTags", String.class, Integer.class);
+        methodInterceptor.beforeMethod(enhancedInstance, testMethodWithTags, new Object[]{"lisi", 14}, null, null);
+        methodInterceptor.afterMethod(enhancedInstance, testMethodWithTags, null, null, new User("lisi", 14));
+        ContextManager.stopSpan();
+        assertThat(storage.getTraceSegments().size(), is(1));
+        TraceSegment traceSegment = storage.getTraceSegments().get(0);
+        List<AbstractTracingSpan> spans = SegmentHelper.getSpans(traceSegment);
+        assertThat(spans.size(), is(1));
+        AbstractTracingSpan tracingSpan = spans.get(0);
+        assertThat(tracingSpan.getOperationName(), is("testMethod"));
+        SpanAssert.assertLogSize(tracingSpan, 0);
+        SpanAssert.assertTagSize(tracingSpan, 2);
+        List<TagValuePair> tags = SpanHelper.getTags(tracingSpan);
+
+        assertThat(tags.get(0).getKey().key(), is("username"));
+        assertThat(tags.get(0).getValue(), is("lisi"));
+        assertThat(tags.get(1).getKey().key(), is("info"));
+        assertThat(tags.get(1).getValue(), is("username=lisi,age=14"));
+
+    }
+
+    private class TestAnnotationMethodClass {
+
+        @Tag(key = "username", value = "arg[0]")
+        public void testMethodWithTag(String username) {
+        }
+
+        @Tag(key = "username", value = "returnedObj.username")
+        public User testMethodWithReturnTag(String username, Integer age) {
+            return new User(username, age);
+        }
+
+        @Tags({@Tag(key = "username", value = "arg[0]"), @Tag(key = "info", value = "returnedObj.info")})
+        public User testMethodWithTags(String username, Integer age) {
+            return new User(username, age);
+        }
+    }
+
+    private class User {
+        private String username;
+        private Integer age;
+        private String info;
+
+        public User(String username, Integer age) {
+            this.username = username;
+            this.age = age;
+            info = String.format("username=%s,age=%s", username, age);
+        }
+    }
+}
diff --git a/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/test/java/org/apache/skywalking/apm/toolkit/activation/trace/TraceAnnotationTest.java b/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/test/java/org/apache/skywalking/apm/toolkit/activation/trace/TraceAnnotationTest.java
index 86b9a4a..9c09881 100644
--- a/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/test/java/org/apache/skywalking/apm/toolkit/activation/trace/TraceAnnotationTest.java
+++ b/apm-sniffer/apm-toolkit-activation/apm-toolkit-trace-activation/src/test/java/org/apache/skywalking/apm/toolkit/activation/trace/TraceAnnotationTest.java
@@ -20,6 +20,7 @@ package org.apache.skywalking.apm.toolkit.activation.trace;
 
 import java.lang.reflect.Method;
 import java.util.List;
+import lombok.AllArgsConstructor;
 import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan;
 import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment;
 import org.apache.skywalking.apm.agent.core.context.util.TagValuePair;
@@ -31,6 +32,7 @@ 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.SpanAssert;
 import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner;
+import org.apache.skywalking.apm.toolkit.trace.Tag;
 import org.apache.skywalking.apm.toolkit.trace.Trace;
 import org.junit.Before;
 import org.junit.Rule;
@@ -98,10 +100,12 @@ public class TraceAnnotationTest {
     }
 
     @Test
-    public void testTrace() throws Throwable {
-        Method withOperationNameMethod = TestAnnotationMethodClass.class.getDeclaredMethod("testMethodWithDefaultValue");
-        methodInterceptor.beforeMethod(enhancedInstance, withOperationNameMethod, null, null, null);
-        methodInterceptor.afterMethod(enhancedInstance, withOperationNameMethod, null, null, null);
+    public void testTraceWithTag() throws Throwable {
+        Method testMethodWithTag = TestAnnotationMethodClass.class.getDeclaredMethod("testMethodWithTag", String.class);
+        methodInterceptor.beforeMethod(enhancedInstance, testMethodWithTag, new Object[]{"zhangsan"}, null, null);
+        tagInterceptor.beforeMethod(TestAnnotationMethodClass.class, testMethodWithTag, tagParameters, tagParameterTypes, null);
+        tagInterceptor.afterMethod(TestAnnotationMethodClass.class, testMethodWithTag, tagParameters, tagParameterTypes, null);
+        methodInterceptor.afterMethod(enhancedInstance, testMethodWithTag, null, null, null);
 
         assertThat(storage.getTraceSegments().size(), is(1));
         TraceSegment traceSegment = storage.getTraceSegments().get(0);
@@ -109,8 +113,51 @@ public class TraceAnnotationTest {
         assertThat(spans.size(), is(1));
 
         AbstractTracingSpan tracingSpan = spans.get(0);
-        assertThat(tracingSpan.getOperationName(), is(TestAnnotationMethodClass.class.getName() + "." + withOperationNameMethod
-            .getName() + "()"));
+        assertThat(tracingSpan.getOperationName(), is("testMethod"));
+        SpanAssert.assertLogSize(tracingSpan, 0);
+        SpanAssert.assertTagSize(tracingSpan, 2);
+        List<TagValuePair> tags = SpanHelper.getTags(tracingSpan);
+        assertThat(tags.get(0).getKey().key(), is("username"));
+        assertThat(tags.get(0).getValue(), is("zhangsan"));
+        assertThat(tags.get(1).getKey().key(), is("testTagKey"));
+        assertThat(tags.get(1).getValue(), is("testTagValue"));
+    }
+
+    @Test
+    public void testTraceWithReturnTag() throws Throwable {
+        Method testMethodWithReturnTag = TestAnnotationMethodClass.class.getDeclaredMethod("testMethodWithReturnTag", String.class, Integer.class);
+        methodInterceptor.beforeMethod(enhancedInstance, testMethodWithReturnTag, new Object[]{"lisi", 14}, null, null);
+        tagInterceptor.beforeMethod(TestAnnotationMethodClass.class, testMethodWithReturnTag, tagParameters, tagParameterTypes, null);
+        tagInterceptor.afterMethod(TestAnnotationMethodClass.class, testMethodWithReturnTag, tagParameters, tagParameterTypes, null);
+        methodInterceptor.afterMethod(enhancedInstance, testMethodWithReturnTag, null, null, new User("lisi", 14));
+
+        assertThat(storage.getTraceSegments().size(), is(1));
+        TraceSegment traceSegment = storage.getTraceSegments().get(0);
+        List<AbstractTracingSpan> spans = SegmentHelper.getSpans(traceSegment);
+        assertThat(spans.size(), is(1));
+        AbstractTracingSpan tracingSpan = spans.get(0);
+        assertThat(tracingSpan.getOperationName(), is("testMethod"));
+        SpanAssert.assertLogSize(tracingSpan, 0);
+        SpanAssert.assertTagSize(tracingSpan, 2);
+        List<TagValuePair> tags = SpanHelper.getTags(tracingSpan);
+        assertThat(tags.get(0).getKey().key(), is("testTagKey"));
+        assertThat(tags.get(0).getValue(), is("testTagValue"));
+        assertThat(tags.get(1).getKey().key(), is("username"));
+        assertThat(tags.get(1).getValue(), is("lisi"));
+    }
+
+    @Test
+    public void testTrace() throws Throwable {
+        Method testMethodWithDefaultValue = TestAnnotationMethodClass.class.getDeclaredMethod("testMethodWithDefaultValue");
+        methodInterceptor.beforeMethod(enhancedInstance, testMethodWithDefaultValue, null, null, null);
+        methodInterceptor.afterMethod(enhancedInstance, testMethodWithDefaultValue, null, null, null);
+
+        assertThat(storage.getTraceSegments().size(), is(1));
+        TraceSegment traceSegment = storage.getTraceSegments().get(0);
+        List<AbstractTracingSpan> spans = SegmentHelper.getSpans(traceSegment);
+        assertThat(spans.size(), is(1));
+        AbstractTracingSpan tracingSpan = spans.get(0);
+        assertThat(tracingSpan.getOperationName(), is(TestAnnotationMethodClass.class.getName() + "." + testMethodWithDefaultValue.getName() + "()"));
         SpanAssert.assertLogSize(tracingSpan, 0);
         SpanAssert.assertTagSize(tracingSpan, 0);
     }
@@ -120,8 +167,25 @@ public class TraceAnnotationTest {
         public void testMethodWithOperationName() {
         }
 
+        @Trace(operationName = "testMethod")
+        @Tag(key = "username", value = "arg[0]")
+        public void testMethodWithTag(String username) {
+        }
+
+        @Trace(operationName = "testMethod")
+        @Tag(key = "username", value = "returnedObj.username")
+        public User testMethodWithReturnTag(String username, Integer age) {
+            return new User(username, age);
+        }
+
         @Trace
         public void testMethodWithDefaultValue() {
         }
     }
+
+    @AllArgsConstructor
+    private class User {
+        private String username;
+        private Integer age;
+    }
 }
diff --git a/docs/en/setup/service-agent/java-agent/Application-toolkit-trace.md b/docs/en/setup/service-agent/java-agent/Application-toolkit-trace.md
index 2c96e31..4b61f31 100644
--- a/docs/en/setup/service-agent/java-agent/Application-toolkit-trace.md
+++ b/docs/en/setup/service-agent/java-agent/Application-toolkit-trace.md
@@ -39,12 +39,16 @@ ActiveSpan.debug("Test-debug-Msg");
 
 /**
  * The codes below will generate a span,
- * and two tags, keys are `tag1` and `tag2`, values are the passed-in parameters, respectively
+ * and two types of tags, 
+      one type tag: keys are `tag1` and `tag2`, values are the passed-in parameters, respectively, 
+      the other type tag: keys are `username`  and `age`, values are the return value in User, respectively
  */
 @Trace
 @Tag(key = "tag1", value = "arg[0]")
 @Tag(key = "tag2", value = "arg[1]")
-public void methodYouWantToTrace(String param1, String param2) {
+@Tag(key = "username", value = "returnedObj.username")
+@Tag(key = "age", value = "returnedObj.age")
+public User methodYouWantToTrace(String param1, String param2) {
     // ...
 }
 ```
diff --git a/test/plugin/scenarios/apm-toolkit-trace-scenario/config/expectedData.yaml b/test/plugin/scenarios/apm-toolkit-trace-scenario/config/expectedData.yaml
index 307453d..683c3dd 100644
--- a/test/plugin/scenarios/apm-toolkit-trace-scenario/config/expectedData.yaml
+++ b/test/plugin/scenarios/apm-toolkit-trace-scenario/config/expectedData.yaml
@@ -145,6 +145,21 @@ segmentItems:
             tags:
               - {key: p1, value: testTagAnnotationParam1}
               - {key: p2, value: testTagAnnotationParam2}
+          - operationName: test.org.apache.skywalking.apm.testcase.toolkit.controller.TestService.testTagAnnotationReturnInfo(java.lang.String,java.lang.Integer)
+            operationId: 0
+            parentSpanId: 0
+            spanId: 8
+            spanLayer: Unknown
+            startTime: nq 0
+            endTime: nq 0
+            componentId: 0
+            componentName: ''
+            isError: false
+            spanType: Local
+            peer: ''
+            peerId: 0
+            tags:
+              - {key: username, value: zhangsan}
           - operationName: /case/tool-kit
             operationId: 0
             parentSpanId: -1
diff --git a/test/plugin/scenarios/apm-toolkit-trace-scenario/src/main/java/org/apache/skywalking/apm/toolkit/model/User.java b/test/plugin/scenarios/apm-toolkit-trace-scenario/src/main/java/org/apache/skywalking/apm/toolkit/model/User.java
new file mode 100644
index 0000000..0ae2339
--- /dev/null
+++ b/test/plugin/scenarios/apm-toolkit-trace-scenario/src/main/java/org/apache/skywalking/apm/toolkit/model/User.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.toolkit.model;
+
+public class User {
+    private String username;
+    private Integer age;
+
+    public User(String username, Integer age) {
+        this.username = username;
+        this.age = age;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public Integer getAge() {
+        return age;
+    }
+
+    public void setAge(Integer age) {
+        this.age = age;
+    }
+}
diff --git a/test/plugin/scenarios/apm-toolkit-trace-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/toolkit/controller/TestController.java b/test/plugin/scenarios/apm-toolkit-trace-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/toolkit/controller/TestController.java
index 3b9af9e..cf7941a 100644
--- a/test/plugin/scenarios/apm-toolkit-trace-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/toolkit/controller/TestController.java
+++ b/test/plugin/scenarios/apm-toolkit-trace-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/toolkit/controller/TestController.java
@@ -47,6 +47,7 @@ public class TestController {
         testService.testErrorMsg();
         testService.testErrorThrowable();
         testService.testTagAnnotation("testTagAnnotationParam1", "testTagAnnotationParam2");
+        testService.testTagAnnotationReturnInfo("zhangsan", 15);
         testService.asyncCallable(() -> {
             visit("http://localhost:8080/apm-toolkit-trace-scenario/case/asyncVisit/callable");
             return true;
diff --git a/test/plugin/scenarios/apm-toolkit-trace-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/toolkit/controller/TestService.java b/test/plugin/scenarios/apm-toolkit-trace-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/toolkit/controller/TestService.java
index a193bf1..4c8827d 100644
--- a/test/plugin/scenarios/apm-toolkit-trace-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/toolkit/controller/TestService.java
+++ b/test/plugin/scenarios/apm-toolkit-trace-scenario/src/main/java/test/org/apache/skywalking/apm/testcase/toolkit/controller/TestService.java
@@ -18,6 +18,7 @@
 
 package test.org.apache.skywalking.apm.testcase.toolkit.controller;
 
+import org.apache.skywalking.apm.toolkit.model.User;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
@@ -73,6 +74,11 @@ public class TestService {
     }
 
     @Trace
+    @Tag(key = "username", value = "returnedObj.username")
+    public User testTagAnnotationReturnInfo(final String username, final Integer age) {
+        return new User(username, age);
+    }
+    @Trace
     @Tag(key = "testTag", value = "arg[0]")
     public void testInfo(final String testInfoParam) {
         ActiveSpan.info("TestInfoMsg");