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 <389361...@qq.com> AuthorDate: Wed Feb 12 14:03:02 2020 +0800 Tag annotation supports returned expression (#4327) Co-authored-by: 吴晟 Wu Sheng <wu.sh...@foxmail.com> Co-authored-by: kezhenxu94 <kezhenx...@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");