Skip to content

Commit 3efbb9c

Browse files
authored
Merge pull request DataDog#2658 from darylrobbins/hazelcast
2 parents 959ef40 + 66d3aee commit 3efbb9c

28 files changed

Lines changed: 2706 additions & 0 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
muzzle {
2+
pass {
3+
group = "com.hazelcast"
4+
module = "hazelcast-all"
5+
versions = "[3.6,3.9)"
6+
assertInverse = true
7+
}
8+
}
9+
10+
apply from: "$rootDir/gradle/java.gradle"
11+
12+
apply plugin: 'org.unbroken-dome.test-sets'
13+
14+
testSets {
15+
latestDepTest {
16+
dirName = 'test'
17+
}
18+
}
19+
20+
dependencies {
21+
compileOnly group: 'com.hazelcast', name: 'hazelcast-all', version: '3.6'
22+
23+
// Using 3.8 to allow us to exercise the async capabilities that only exist in 3.8
24+
testCompile group: 'com.hazelcast', name: 'hazelcast-all', version: '3.8'
25+
26+
latestDepTestCompile group: 'com.hazelcast', name: 'hazelcast-all', version: '3.8.+'
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package datadog.trace.instrumentation.hazelcast36;
2+
3+
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
4+
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan;
5+
import static datadog.trace.instrumentation.hazelcast36.HazelcastConstants.HAZELCAST_INSTANCE;
6+
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
7+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
8+
9+
import com.google.auto.service.AutoService;
10+
import com.hazelcast.client.impl.HazelcastClientInstanceImpl;
11+
import com.hazelcast.client.proxy.ClientMapProxy;
12+
import com.hazelcast.client.spi.impl.ClientNonSmartInvocationServiceImpl;
13+
import com.hazelcast.spi.discovery.DiscoveryStrategy;
14+
import datadog.trace.agent.tooling.Instrumenter;
15+
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
16+
import java.util.Collections;
17+
import java.util.Map;
18+
import net.bytebuddy.asm.Advice;
19+
import net.bytebuddy.description.method.MethodDescription;
20+
import net.bytebuddy.description.type.TypeDescription;
21+
import net.bytebuddy.matcher.ElementMatcher;
22+
23+
@AutoService(Instrumenter.class)
24+
public class ClientInvocationInstrumentation extends Instrumenter.Tracing {
25+
26+
public ClientInvocationInstrumentation() {
27+
super("hazelcast_legacy");
28+
}
29+
30+
@Override
31+
protected boolean defaultEnabled() {
32+
return false;
33+
}
34+
35+
@Override
36+
public String[] helperClassNames() {
37+
return new String[] {
38+
packageName + ".HazelcastConstants",
39+
packageName + ".DistributedObjectDecorator",
40+
packageName + ".DistributedObjectDecorator$1"
41+
};
42+
}
43+
44+
@Override
45+
public ElementMatcher<? super TypeDescription> typeMatcher() {
46+
return named("com.hazelcast.client.spi.impl.ClientInvocation");
47+
}
48+
49+
@Override
50+
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
51+
return Collections.singletonMap(
52+
isConstructor()
53+
.and(takesArgument(0, named("com.hazelcast.client.impl.HazelcastClientInstanceImpl"))),
54+
getClass().getName() + "$ConstructAdvice");
55+
}
56+
57+
public static class ConstructAdvice {
58+
59+
@Advice.OnMethodExit(suppress = Throwable.class)
60+
public static void constructorExit(
61+
@Advice.Argument(0) final HazelcastClientInstanceImpl hazelcastInstance) {
62+
63+
final AgentSpan span = activeSpan();
64+
65+
if (span != null
66+
&& hazelcastInstance != null
67+
&& hazelcastInstance.getLifecycleService() != null
68+
&& hazelcastInstance.getLifecycleService().isRunning()) {
69+
70+
activeSpan().setTag(HAZELCAST_INSTANCE, hazelcastInstance.getName());
71+
}
72+
}
73+
74+
public static void muzzleCheck(
75+
// Moved in 4.0
76+
ClientMapProxy proxy,
77+
78+
// New in 3.6
79+
DiscoveryStrategy strategy,
80+
81+
// Renamed in 3.9
82+
ClientNonSmartInvocationServiceImpl invocationService) {
83+
strategy.start();
84+
proxy.getServiceName();
85+
invocationService.start();
86+
}
87+
}
88+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package datadog.trace.instrumentation.hazelcast36;
2+
3+
import static datadog.trace.instrumentation.hazelcast36.HazelcastConstants.COMPONENT_NAME;
4+
import static datadog.trace.instrumentation.hazelcast36.HazelcastConstants.HAZELCAST_NAME;
5+
import static datadog.trace.instrumentation.hazelcast36.HazelcastConstants.HAZELCAST_OPERATION;
6+
import static datadog.trace.instrumentation.hazelcast36.HazelcastConstants.HAZELCAST_SERVICE;
7+
8+
import com.hazelcast.core.DistributedObject;
9+
import datadog.trace.api.Function;
10+
import datadog.trace.api.Pair;
11+
import datadog.trace.api.cache.DDCache;
12+
import datadog.trace.api.cache.DDCaches;
13+
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
14+
import datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes;
15+
import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
16+
import datadog.trace.bootstrap.instrumentation.decorator.ClientDecorator;
17+
import datadog.trace.util.Strings;
18+
import org.slf4j.Logger;
19+
import org.slf4j.LoggerFactory;
20+
21+
/** Decorate Hazelcast distributed object span's with relevant contextual information. */
22+
public class DistributedObjectDecorator extends ClientDecorator {
23+
24+
private static final Logger log = LoggerFactory.getLogger(DistributedObjectDecorator.class);
25+
26+
public static final DistributedObjectDecorator DECORATE = new DistributedObjectDecorator();
27+
28+
private static final DDCache<Pair<String, String>, String> QUALIFIED_NAME_CACHE =
29+
DDCaches.newFixedSizeCache(64);
30+
31+
private static final Function<Pair<String, String>, String> COMPUTE_QUALIFIED_NAME =
32+
new Function<Pair<String, String>, String>() {
33+
@Override
34+
public String apply(Pair<String, String> input) {
35+
final String service = input.getLeft();
36+
final String objectName = input.getRight();
37+
38+
final StringBuilder qualifiedName = new StringBuilder();
39+
boolean terminateBracket = false;
40+
41+
if (service != null && service.length() > 15 && service.startsWith("hz:impl:")) {
42+
// Transform into just the service qualifiedName
43+
qualifiedName.append(
44+
service, "hz:impl:".length(), service.length() - "Service".length());
45+
qualifiedName.append('[');
46+
terminateBracket = true;
47+
}
48+
49+
qualifiedName.append(objectName);
50+
51+
if (terminateBracket) qualifiedName.append(']');
52+
53+
return qualifiedName.toString();
54+
}
55+
};
56+
57+
@Override
58+
protected CharSequence spanType() {
59+
return InternalSpanTypes.HTTP_CLIENT;
60+
}
61+
62+
@Override
63+
protected String[] instrumentationNames() {
64+
return new String[] {COMPONENT_NAME.toString()};
65+
}
66+
67+
@Override
68+
protected CharSequence component() {
69+
return COMPONENT_NAME;
70+
}
71+
72+
@Override
73+
protected String service() {
74+
return COMPONENT_NAME.toString();
75+
}
76+
77+
/** Decorate trace based on service execution metadata. */
78+
public AgentSpan onServiceExecution(
79+
final AgentSpan span, final DistributedObject object, final String methodName) {
80+
81+
final String objectName =
82+
QUALIFIED_NAME_CACHE.computeIfAbsent(
83+
Pair.of(object.getServiceName(), object.getName()), COMPUTE_QUALIFIED_NAME);
84+
85+
span.setResourceName(UTF8BytesString.create(Strings.join(".", objectName, methodName)));
86+
87+
span.setTag(HAZELCAST_SERVICE, object.getServiceName());
88+
span.setTag(HAZELCAST_OPERATION, methodName);
89+
span.setTag(HAZELCAST_NAME, objectName);
90+
91+
return span;
92+
}
93+
94+
/** Annotate the span with the results of the operation. */
95+
public AgentSpan onResult(final AgentSpan span, Object result) {
96+
97+
// Nothing to do here, so return
98+
if (result == null) {
99+
return span;
100+
}
101+
102+
return span;
103+
}
104+
}

0 commit comments

Comments
 (0)