|
11 | 11 | import org.slf4j.Logger; |
12 | 12 | import org.slf4j.LoggerFactory; |
13 | 13 |
|
| 14 | +/** |
| 15 | + * Listener for SparkLauncher spans. Tracks the lifecycle of a Spark application submitted via |
| 16 | + * SparkLauncher.startApplication(). Only a single launcher span can be active at a time. Subsequent |
| 17 | + * calls to startApplication() from the same or different launcher instances will not create spans; |
| 18 | + * only the first launch in the JVM is traced |
| 19 | + */ |
14 | 20 | public class SparkLauncherListener implements SparkAppHandle.Listener { |
15 | 21 |
|
16 | 22 | private static final Logger log = LoggerFactory.getLogger(SparkLauncherListener.class); |
@@ -81,46 +87,51 @@ public static synchronized void finishSpanWithThrowable(Throwable throwable) { |
81 | 87 |
|
82 | 88 | @Override |
83 | 89 | public void stateChanged(SparkAppHandle handle) { |
84 | | - SparkAppHandle.State state = handle.getState(); |
85 | | - AgentSpan span = launcherSpan; |
86 | | - if (span != null) { |
87 | | - span.setTag("spark.launcher.app_state", state.toString()); |
88 | | - |
89 | | - String appId = handle.getAppId(); |
90 | | - if (appId != null) { |
91 | | - span.setTag("spark.app_id", appId); |
92 | | - span.setTag("app_id", appId); |
93 | | - } |
| 90 | + synchronized (SparkLauncherListener.class) { |
| 91 | + SparkAppHandle.State state = handle.getState(); |
| 92 | + AgentSpan span = launcherSpan; |
| 93 | + if (span != null) { |
| 94 | + span.setTag("spark.launcher.app_state", state.toString()); |
| 95 | + |
| 96 | + String appId = handle.getAppId(); |
| 97 | + if (appId != null) { |
| 98 | + span.setTag("spark.app_id", appId); |
| 99 | + span.setTag("app_id", appId); |
| 100 | + } |
94 | 101 |
|
95 | | - if (state.isFinal()) { |
96 | | - if (state == SparkAppHandle.State.FAILED |
97 | | - || state == SparkAppHandle.State.KILLED |
98 | | - || state == SparkAppHandle.State.LOST) { |
99 | | - // Set error tags but don't finish yet — RunMainAdvice may add the throwable |
100 | | - // with the full stack trace. The span will be finished by RunMainAdvice or |
101 | | - // the shutdown hook. |
102 | | - span.setError(true); |
103 | | - span.setTag(DDTags.ERROR_TYPE, "Spark Launcher Failed"); |
104 | | - span.setTag(DDTags.ERROR_MSG, "Application " + state); |
105 | | - } else { |
106 | | - finishSpan(false, null); |
| 102 | + if (state.isFinal()) { |
| 103 | + if (state == SparkAppHandle.State.FAILED |
| 104 | + || state == SparkAppHandle.State.KILLED |
| 105 | + || state == SparkAppHandle.State.LOST) { |
| 106 | + finishSpan(true, "Application " + state); |
| 107 | + } else { |
| 108 | + finishSpan(false, null); |
| 109 | + } |
107 | 110 | } |
108 | 111 | } |
109 | 112 | } |
110 | 113 | } |
111 | 114 |
|
112 | 115 | @Override |
113 | 116 | public void infoChanged(SparkAppHandle handle) { |
114 | | - AgentSpan span = launcherSpan; |
115 | | - if (span != null) { |
116 | | - String appId = handle.getAppId(); |
117 | | - if (appId != null) { |
118 | | - span.setTag("spark.app_id", appId); |
119 | | - span.setTag("app_id", appId); |
| 117 | + synchronized (SparkLauncherListener.class) { |
| 118 | + AgentSpan span = launcherSpan; |
| 119 | + if (span != null) { |
| 120 | + String appId = handle.getAppId(); |
| 121 | + if (appId != null) { |
| 122 | + span.setTag("spark.app_id", appId); |
| 123 | + span.setTag("app_id", appId); |
| 124 | + } |
120 | 125 | } |
121 | 126 | } |
122 | 127 | } |
123 | 128 |
|
| 129 | + /** |
| 130 | + * Extract launcher configuration via reflection and set as span tags. Secret redaction uses the |
| 131 | + * default pattern only (not spark.redaction.regex) because the SparkLauncher conf map is a plain |
| 132 | + * Map, not a SparkConf, so there is no way to read the user's custom redaction regex at this |
| 133 | + * point. |
| 134 | + */ |
124 | 135 | private static void setLauncherConfigTags(AgentSpan span, Object launcher) { |
125 | 136 | try { |
126 | 137 | Field builderField = launcher.getClass().getSuperclass().getDeclaredField("builder"); |
|
0 commit comments