55import datadog .trace .api .sampling .SamplingMechanism ;
66import datadog .trace .bootstrap .instrumentation .api .AgentSpan ;
77import datadog .trace .bootstrap .instrumentation .api .AgentTracer ;
8+ import java .lang .reflect .Field ;
9+ import java .util .Map ;
10+ import java .util .regex .Pattern ;
811import net .bytebuddy .asm .Advice ;
912import org .apache .spark .launcher .SparkAppHandle ;
1013import 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