Skip to content

Commit 5c57154

Browse files
committed
Capture more spark relevant attrs
1 parent b6406c2 commit 5c57154

1 file changed

Lines changed: 78 additions & 5 deletions

File tree

  • dd-java-agent/instrumentation/spark/spark-common/src/main/java/datadog/trace/instrumentation/spark

dd-java-agent/instrumentation/spark/spark-common/src/main/java/datadog/trace/instrumentation/spark/SparkLauncherAdvice.java

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import datadog.trace.api.sampling.SamplingMechanism;
66
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
77
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
8+
import java.lang.reflect.Field;
9+
import java.util.Map;
10+
import java.util.regex.Pattern;
811
import net.bytebuddy.asm.Advice;
912
import org.apache.spark.launcher.SparkAppHandle;
1013
import org.slf4j.Logger;
@@ -19,7 +22,70 @@ public class SparkLauncherAdvice {
1922

2023
private static volatile boolean shutdownHookRegistered = false;
2124

22-
public static synchronized void createLauncherSpan(String resource) {
25+
/** Extract SparkLauncher configuration via reflection and set as span tags. */
26+
private static void setLauncherConfigTags(AgentSpan span, Object launcher) {
27+
try {
28+
// SparkLauncher extends AbstractLauncher which has a 'builder' field
29+
Field builderField = launcher.getClass().getSuperclass().getDeclaredField("builder");
30+
builderField.setAccessible(true);
31+
Object builder = builderField.get(launcher);
32+
if (builder == null) {
33+
return;
34+
}
35+
36+
Class<?> builderClass = builder.getClass();
37+
// Fields are on AbstractCommandBuilder (parent of SparkSubmitCommandBuilder)
38+
Class<?> abstractBuilderClass = builderClass.getSuperclass();
39+
40+
setStringFieldAsTag(span, builder, abstractBuilderClass, "master", "master");
41+
setStringFieldAsTag(span, builder, abstractBuilderClass, "deployMode", "deploy_mode");
42+
setStringFieldAsTag(span, builder, abstractBuilderClass, "appName", "application_name");
43+
setStringFieldAsTag(span, builder, abstractBuilderClass, "mainClass", "main_class");
44+
setStringFieldAsTag(span, builder, abstractBuilderClass, "appResource", "app_resource");
45+
46+
// Extract spark conf entries and redact sensitive values
47+
try {
48+
Field confField = abstractBuilderClass.getDeclaredField("conf");
49+
confField.setAccessible(true);
50+
@SuppressWarnings("unchecked")
51+
Map<String, String> conf = (Map<String, String>) confField.get(builder);
52+
if (conf != null) {
53+
Pattern redactionPattern =
54+
Pattern.compile("(?i)secret|password|token|access.key|api.key");
55+
for (Map.Entry<String, String> entry : conf.entrySet()) {
56+
if (SparkConfAllowList.canCaptureJobParameter(entry.getKey())) {
57+
String value = entry.getValue();
58+
if (redactionPattern.matcher(entry.getKey()).find()
59+
|| redactionPattern.matcher(value).find()) {
60+
value = "[redacted]";
61+
}
62+
span.setTag("config." + entry.getKey().replace('.', '_'), value);
63+
}
64+
}
65+
}
66+
} catch (NoSuchFieldException e) {
67+
log.debug("Could not find conf field on builder", e);
68+
}
69+
} catch (Exception e) {
70+
log.debug("Failed to extract SparkLauncher configuration", e);
71+
}
72+
}
73+
74+
private static void setStringFieldAsTag(
75+
AgentSpan span, Object obj, Class<?> clazz, String fieldName, String tagName) {
76+
try {
77+
Field field = clazz.getDeclaredField(fieldName);
78+
field.setAccessible(true);
79+
Object value = field.get(obj);
80+
if (value != null) {
81+
span.setTag(tagName, value.toString());
82+
}
83+
} catch (Exception e) {
84+
log.debug("Could not read field {} from builder", fieldName, e);
85+
}
86+
}
87+
88+
public static synchronized void createLauncherSpan(String resource, Object launcher) {
2389
if (launcherSpan != null) {
2490
return;
2591
}
@@ -32,6 +98,11 @@ public static synchronized void createLauncherSpan(String resource) {
3298
.withResourceName(resource)
3399
.start();
34100
span.setSamplingPriority(PrioritySampling.USER_KEEP, SamplingMechanism.DATA_JOBS);
101+
102+
if (launcher != null) {
103+
setLauncherConfigTags(span, launcher);
104+
}
105+
35106
launcherSpan = span;
36107

37108
if (!shutdownHookRegistered) {
@@ -80,8 +151,10 @@ public static synchronized void finishLauncherSpan(Throwable throwable) {
80151
public static class StartApplicationAdvice {
81152
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
82153
public static void exit(
83-
@Advice.Return SparkAppHandle handle, @Advice.Thrown Throwable throwable) {
84-
createLauncherSpan("SparkLauncher.startApplication");
154+
@Advice.This Object launcher,
155+
@Advice.Return SparkAppHandle handle,
156+
@Advice.Thrown Throwable throwable) {
157+
createLauncherSpan("SparkLauncher.startApplication", launcher);
85158

86159
if (throwable != null) {
87160
AgentSpan span = launcherSpan;
@@ -105,8 +178,8 @@ public static void exit(
105178

106179
public static class LaunchAdvice {
107180
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
108-
public static void exit(@Advice.Thrown Throwable throwable) {
109-
createLauncherSpan("SparkLauncher.launch");
181+
public static void exit(@Advice.This Object launcher, @Advice.Thrown Throwable throwable) {
182+
createLauncherSpan("SparkLauncher.launch", launcher);
110183

111184
if (throwable != null) {
112185
AgentSpan span = launcherSpan;

0 commit comments

Comments
 (0)