Skip to content

Commit feed497

Browse files
ziquanziquan.miao
ziquan
and
ziquan.miao
authored
Extend DD_TRACE_METHODS to support wildcarded method argument (#2171)
* add support for wildcards in place of method names * update warning message * update transformer to support wildcard method config * spotless * add private method support * fix instrumentation and add initial test * add configtest * experiment with any() * modify element matchers and add initial tests * add testing for interfaces and abstract classes * accommodate codenarc tests * codenarc * explicit imports, remove whitespace, add finalizer support * ignore getters and setters and update test for abstract classes * accommodate interface tests * add more tests * fix codenarc test failure Co-authored-by: ziquan.miao <[email protected]>
1 parent 97bc072 commit feed497

File tree

2 files changed

+224
-8
lines changed

2 files changed

+224
-8
lines changed

dd-java-agent/instrumentation/trace-annotation/src/main/java/datadog/trace/instrumentation/trace_annotation/TraceConfigInstrumentation.java

+26-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,16 @@
22

33
import static datadog.trace.agent.tooling.ClassLoaderMatcher.hasClassesNamed;
44
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.safeHasSuperType;
5+
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
6+
import static net.bytebuddy.matcher.ElementMatchers.isEquals;
7+
import static net.bytebuddy.matcher.ElementMatchers.isFinalizer;
8+
import static net.bytebuddy.matcher.ElementMatchers.isGetter;
9+
import static net.bytebuddy.matcher.ElementMatchers.isHashCode;
10+
import static net.bytebuddy.matcher.ElementMatchers.isSetter;
11+
import static net.bytebuddy.matcher.ElementMatchers.isSynthetic;
12+
import static net.bytebuddy.matcher.ElementMatchers.isToString;
513
import static net.bytebuddy.matcher.ElementMatchers.named;
14+
import static net.bytebuddy.matcher.ElementMatchers.not;
615

716
import com.google.auto.service.AutoService;
817
import datadog.trace.agent.tooling.Instrumenter;
@@ -33,7 +42,8 @@
3342
public class TraceConfigInstrumentation implements Instrumenter {
3443

3544
static final String PACKAGE_CLASS_NAME_REGEX = "[\\w.\\$]+";
36-
private static final String METHOD_LIST_REGEX = "\\s*(?:\\w+\\s*,)*\\s*(?:\\w+\\s*,?)\\s*";
45+
private static final String METHOD_LIST_REGEX =
46+
"\\s*(?:\\*|(?:\\w+\\s*,)*\\s*(?:\\w+\\s*,?))\\s*";
3747
private static final String CONFIG_FORMAT =
3848
"(?:\\s*"
3949
+ PACKAGE_CLASS_NAME_REGEX
@@ -63,7 +73,7 @@ public TraceConfigInstrumentation() {
6373

6474
} else if (!validateConfigString(configString)) {
6575
log.warn(
66-
"Invalid trace method config '{}'. Must match 'package.Class$Name[method1,method2];*'.",
76+
"Invalid trace method config '{}'. Must match 'package.Class$Name[method1,method2];*' or 'package.Class$Name[*];*'.",
6777
configString);
6878
classMethodsToTrace = Collections.emptyMap();
6979

@@ -153,7 +163,20 @@ public Map<ElementMatcher<? super MethodDescription>, String> transformers() {
153163
ElementMatcher.Junction<MethodDescription> methodMatchers = null;
154164
for (final String methodName : methodNames) {
155165
if (methodMatchers == null) {
156-
methodMatchers = named(methodName);
166+
if (methodName.equals("*")) {
167+
methodMatchers =
168+
not(
169+
isHashCode()
170+
.or(isEquals())
171+
.or(isToString())
172+
.or(isFinalizer())
173+
.or(isGetter())
174+
.or(isConstructor())
175+
.or(isSetter())
176+
.or(isSynthetic()));
177+
} else {
178+
methodMatchers = named(methodName);
179+
}
157180
} else {
158181
methodMatchers = methodMatchers.or(named(methodName));
159182
}

dd-java-agent/instrumentation/trace-annotation/src/test/groovy/TraceConfigTest.groovy

+198-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class TraceConfigTest extends AgentTestRunner {
99
@Override
1010
void configurePreAgent() {
1111
super.configurePreAgent()
12-
injectSysConfig("dd.trace.methods", "package.ClassName[method1,method2];${ConfigTracedCallable.name}[call]")
12+
injectSysConfig("dd.trace.methods", "package.ClassName[method1,method2];${ConfigTracedCallable.name}[call];${ConfigTracedCallable2.name}[*];${Animal.name}[animalSound];${DictionaryElement.name}[*];${Floor.name}[setNumber];${Mammal.name}[*]")
1313
}
1414

1515
class ConfigTracedCallable implements Callable<String> {
@@ -19,18 +19,207 @@ class TraceConfigTest extends AgentTestRunner {
1919
}
2020
}
2121

22+
class ConfigTracedCallable2 implements Callable<String> {
23+
int g
24+
25+
ConfigTracedCallable2(){
26+
g = 4
27+
}
28+
@Override
29+
String call() throws Exception {
30+
return call_helper()
31+
}
32+
33+
String call_helper() throws Exception {
34+
return "Hello2!"
35+
}
36+
37+
String getValue() {
38+
return "hello"
39+
}
40+
void setValue(int value) {
41+
g = value
42+
}
43+
}
44+
45+
interface Mammal {
46+
void name(String newName)
47+
void height(int newHeight)
48+
}
49+
50+
interface Floor {
51+
void setNumber()
52+
}
53+
54+
class Fifth implements Floor {
55+
int floorNumber
56+
57+
void setNumber() {
58+
floorNumber = 5
59+
}
60+
}
61+
62+
class Human implements Mammal {
63+
String name
64+
String height
65+
66+
void name(String newName){
67+
name = newName
68+
}
69+
void height(int newHeight){
70+
height = newHeight
71+
}
72+
}
73+
74+
75+
abstract class Animal {
76+
abstract void animalSound()
77+
void sleep() {
78+
System.out.println("Zzz")
79+
}
80+
}
81+
82+
abstract class DictionaryElement{
83+
abstract void produceDefinition()
84+
}
85+
86+
class Sophisticated extends DictionaryElement{
87+
void produceDefinition() {
88+
System.out.println("of such excellence, grandeur, or beauty as to inspire great admiration or awe.")
89+
}
90+
}
91+
class Pig extends Animal {
92+
void animalSound() {
93+
System.out.println("The pig says: wee wee")
94+
}
95+
}
96+
2297
def "test configuration based trace"() {
23-
expect:
98+
when:
2499
new ConfigTracedCallable().call() == "Hello!"
100+
new Pig().animalSound()
101+
then:
102+
assertTraces(2) {
103+
trace(1) {
104+
span {
105+
resourceName "ConfigTracedCallable.call"
106+
operationName "trace.annotation"
107+
tags {
108+
"$Tags.COMPONENT" "trace"
109+
defaultTags()
110+
}
111+
}
112+
}
113+
trace(1) {
114+
span {
115+
resourceName "Pig.animalSound"
116+
operationName "trace.annotation"
117+
tags {
118+
"$Tags.COMPONENT" "trace"
119+
defaultTags()
120+
}
121+
}
122+
}
123+
}
124+
}
25125

126+
def "test wildcard configuration"() {
26127
when:
27-
TEST_WRITER.waitForTraces(1)
128+
ConfigTracedCallable2 object = new ConfigTracedCallable2()
129+
object.call()
130+
object.hashCode()
131+
object == new ConfigTracedCallable2()
132+
object.toString()
133+
object.finalize()
134+
object.getValue()
135+
object.setValue(5)
136+
new Sophisticated().produceDefinition()
137+
28138

29139
then:
30-
assertTraces(1) {
140+
assertTraces(2) {
141+
trace(2) {
142+
span {
143+
resourceName "ConfigTracedCallable2.call"
144+
operationName "trace.annotation"
145+
tags {
146+
"$Tags.COMPONENT" "trace"
147+
defaultTags()
148+
}
149+
}
150+
span {
151+
resourceName "ConfigTracedCallable2.call_helper"
152+
operationName "trace.annotation"
153+
tags {
154+
"$Tags.COMPONENT" "trace"
155+
defaultTags()
156+
}
157+
}
158+
}
31159
trace(1) {
32160
span {
33-
resourceName "ConfigTracedCallable.call"
161+
resourceName "Sophisticated.produceDefinition"
162+
operationName "trace.annotation"
163+
tags {
164+
"$Tags.COMPONENT" "trace"
165+
defaultTags()
166+
}
167+
}
168+
}
169+
}
170+
}
171+
172+
def "test wildcard configuration with class implementing interface"() {
173+
174+
when:
175+
Human charlie = new Human()
176+
charlie.name("Charlie")
177+
charlie.height(4)
178+
179+
then:
180+
assertTraces(2) {
181+
trace(1) {
182+
span {
183+
resourceName "Human.name"
184+
operationName "trace.annotation"
185+
tags {
186+
"$Tags.COMPONENT" "trace"
187+
defaultTags()
188+
}
189+
}
190+
}
191+
trace(1) {
192+
span {
193+
resourceName "Human.height"
194+
operationName "trace.annotation"
195+
tags {
196+
"$Tags.COMPONENT" "trace"
197+
defaultTags()
198+
}
199+
}
200+
}
201+
}
202+
}
203+
def "test wildcard configuration based on class extending abstract class"() {
204+
205+
when:
206+
new Pig().animalSound()
207+
new Fifth().setNumber()
208+
then:
209+
assertTraces(2) {
210+
trace(1) {
211+
span {
212+
resourceName "Pig.animalSound"
213+
operationName "trace.annotation"
214+
tags {
215+
"$Tags.COMPONENT" "trace"
216+
defaultTags()
217+
}
218+
}
219+
}
220+
trace(1) {
221+
span {
222+
resourceName "Fifth.setNumber"
34223
operationName "trace.annotation"
35224
tags {
36225
"$Tags.COMPONENT" "trace"
@@ -64,5 +253,9 @@ class TraceConfigTest extends AgentTestRunner {
64253
"ClassName[method1 , method2]" | ["ClassName": ["method1", "method2"].toSet()]
65254
"Class\$1[method1 ] ; Class\$2[ method2];" | ["Class\$1": ["method1"].toSet(), "Class\$2": ["method2"].toSet()]
66255
"Duplicate[method1] ; Duplicate[method2] ;Duplicate[method3];" | ["Duplicate": ["method3"].toSet()]
256+
"ClassName[*]" | ["ClassName": ["*"].toSet()]
257+
"ClassName[*,asdfg]" | [:]
258+
"ClassName[asdfg,*]" | [:]
259+
"Class[*] ; Class\$2[ method2];" | ["Class": ["*"].toSet(), "Class\$2": ["method2"].toSet()]
67260
}
68261
}

0 commit comments

Comments
 (0)