55import datadog .environment .SystemProperties ;
66import datadog .trace .api .ConfigCollector ;
77import datadog .trace .api .ConfigOrigin ;
8+ import datadog .trace .api .config .AppSecConfig ;
89import de .thetaphi .forbiddenapis .SuppressForbidden ;
910import java .io .File ;
1011import java .io .FileNotFoundException ;
1112import java .io .FileReader ;
1213import java .io .IOException ;
1314import java .util .Arrays ;
1415import java .util .BitSet ;
16+ import java .util .Collections ;
1517import java .util .HashMap ;
1618import java .util .HashSet ;
1719import java .util .LinkedHashMap ;
@@ -42,6 +44,32 @@ private ConfigProvider(boolean collectConfig, ConfigProvider.Source... sources)
4244 this .sources = sources ;
4345 }
4446
47+ /**
48+ * Creates a ConfigProvider with sources ordered from lowest to highest precedence. Internally
49+ * reverses the array to support the new approach of iterating from lowest to highest precedence,
50+ * enabling reporting of all configured sources to telemetry (not just the highest-precedence
51+ * match).
52+ *
53+ * @param sources the configuration sources, in order from lowest to highest precedence
54+ * @return a ConfigProvider with sources in precedence order (highest first)
55+ */
56+ public static ConfigProvider createWithPrecedenceOrder (Source ... sources ) {
57+ Source [] reversed = Arrays .copyOf (sources , sources .length );
58+ Collections .reverse (Arrays .asList (reversed ));
59+ return new ConfigProvider (reversed );
60+ }
61+
62+ /**
63+ * Same as {@link #createWithPrecedenceOrder(Source...)} but allows specifying the collectConfig
64+ * flag.
65+ */
66+ public static ConfigProvider createWithPrecedenceOrder (boolean collectConfig , Source ... sources ) {
67+ Source [] reversed = Arrays .copyOf (sources , sources .length );
68+ Collections .reverse (Arrays .asList (reversed ));
69+ return new ConfigProvider (collectConfig , reversed );
70+ }
71+
72+ // TODO: Handle this special case
4573 public String getConfigFileStatus () {
4674 for (ConfigProvider .Source source : sources ) {
4775 if (source instanceof PropertiesConfigSource ) {
@@ -75,63 +103,79 @@ public <T extends Enum<T>> T getEnum(String key, Class<T> enumType, T defaultVal
75103 }
76104
77105 public String getString (String key , String defaultValue , String ... aliases ) {
106+ if (collectConfig ) {
107+ ConfigCollector .get ().put (key , defaultValue , ConfigOrigin .DEFAULT );
108+ }
109+ String value = null ;
78110 for (ConfigProvider .Source source : sources ) {
79- String value = source .get (key , aliases );
80- if (value != null ) {
111+ String tmp = source .get (key , aliases );
112+ if (tmp != null ) {
113+ value = tmp ;
81114 if (collectConfig ) {
82115 ConfigCollector .get ().put (key , value , source .origin ());
83116 }
84- return value ;
85117 }
86118 }
87- if (collectConfig ) {
88- ConfigCollector .get ().put (key , defaultValue , ConfigOrigin .DEFAULT );
89- }
90- return defaultValue ;
119+ return value != null ? value : defaultValue ;
91120 }
92121
93122 /**
94123 * Like {@link #getString(String, String, String...)} but falls back to next source if a value is
95124 * an empty or blank string.
96125 */
97126 public String getStringNotEmpty (String key , String defaultValue , String ... aliases ) {
127+ if (collectConfig ) {
128+ ConfigCollector .get ().put (key , defaultValue , ConfigOrigin .DEFAULT );
129+ }
130+ String value = null ;
98131 for (ConfigProvider .Source source : sources ) {
99- String value = source .get (key , aliases );
100- if (value != null && !value .trim ().isEmpty ()) {
132+ String tmp = source .get (key , aliases );
133+ if (key .equals (AppSecConfig .APPSEC_AUTOMATED_USER_EVENTS_TRACKING )) {
134+ System .out .println ("MTOFF - source: " + source .getClass ().getSimpleName () + " tmp: " + tmp );
135+ }
136+ if (tmp != null && !tmp .trim ().isEmpty ()) {
137+ value = tmp ;
138+ if (key .equals (AppSecConfig .APPSEC_AUTOMATED_USER_EVENTS_TRACKING )) {
139+ System .out .println (
140+ "MTOFF - source: " + source .getClass ().getSimpleName () + " value: " + value );
141+ }
101142 if (collectConfig ) {
102143 ConfigCollector .get ().put (key , value , source .origin ());
103144 }
104- return value ;
105145 }
106146 }
107147 if (collectConfig ) {
108148 ConfigCollector .get ().put (key , defaultValue , ConfigOrigin .DEFAULT );
109149 }
110- return defaultValue ;
150+ return value != null ? value : defaultValue ;
111151 }
112152
113153 public String getStringExcludingSource (
114154 String key ,
115155 String defaultValue ,
116156 Class <? extends ConfigProvider .Source > excludedSource ,
117157 String ... aliases ) {
158+ if (collectConfig ) {
159+ ConfigCollector .get ().put (key , defaultValue , ConfigOrigin .DEFAULT );
160+ }
161+ String value = null ;
118162 for (ConfigProvider .Source source : sources ) {
119163 if (excludedSource .isAssignableFrom (source .getClass ())) {
120164 continue ;
121165 }
122166
123- String value = source .get (key , aliases );
124- if (value != null ) {
167+ String tmp = source .get (key , aliases );
168+ if (tmp != null ) {
169+ value = tmp ;
125170 if (collectConfig ) {
126171 ConfigCollector .get ().put (key , value , source .origin ());
127172 }
128- return value ;
129173 }
130174 }
131175 if (collectConfig ) {
132176 ConfigCollector .get ().put (key , defaultValue , ConfigOrigin .DEFAULT );
133177 }
134- return defaultValue ;
178+ return value != null ? value : defaultValue ;
135179 }
136180
137181 public boolean isSet (String key ) {
@@ -192,15 +236,19 @@ public double getDouble(String key, double defaultValue) {
192236 }
193237
194238 private <T > T get (String key , T defaultValue , Class <T > type , String ... aliases ) {
239+ if (collectConfig ) {
240+ ConfigCollector .get ().put (key , defaultValue , ConfigOrigin .DEFAULT );
241+ }
242+ T value = null ;
195243 for (ConfigProvider .Source source : sources ) {
196244 try {
197245 String sourceValue = source .get (key , aliases );
198- T value = ConfigConverter .valueOf (sourceValue , type );
199- if (value != null ) {
246+ T tmp = ConfigConverter .valueOf (sourceValue , type );
247+ if (tmp != null ) {
248+ value = tmp ;
200249 if (collectConfig ) {
201250 ConfigCollector .get ().put (key , sourceValue , source .origin ());
202251 }
203- return value ;
204252 }
205253 } catch (NumberFormatException ex ) {
206254 // continue
@@ -209,31 +257,31 @@ private <T> T get(String key, T defaultValue, Class<T> type, String... aliases)
209257 if (collectConfig ) {
210258 ConfigCollector .get ().put (key , defaultValue , ConfigOrigin .DEFAULT );
211259 }
212- return defaultValue ;
260+ return value != null ? value : defaultValue ;
213261 }
214262
215263 public List <String > getList (String key ) {
216264 return ConfigConverter .parseList (getString (key ));
217265 }
218266
219267 public List <String > getList (String key , List <String > defaultValue ) {
268+ if (collectConfig ) {
269+ ConfigCollector .get ().put (key , defaultValue , ConfigOrigin .DEFAULT );
270+ }
220271 String list = getString (key );
221272 if (null == list ) {
222- if (collectConfig ) {
223- ConfigCollector .get ().put (key , defaultValue , ConfigOrigin .DEFAULT );
224- }
225273 return defaultValue ;
226274 } else {
227275 return ConfigConverter .parseList (list );
228276 }
229277 }
230278
231279 public Set <String > getSet (String key , Set <String > defaultValue ) {
280+ if (collectConfig ) {
281+ ConfigCollector .get ().put (key , defaultValue , ConfigOrigin .DEFAULT );
282+ }
232283 String list = getString (key );
233284 if (null == list ) {
234- if (collectConfig ) {
235- ConfigCollector .get ().put (key , defaultValue , ConfigOrigin .DEFAULT );
236- }
237285 return defaultValue ;
238286 } else {
239287 return new HashSet (ConfigConverter .parseList (list ));
@@ -250,16 +298,20 @@ public Map<String, String> getMergedMap(String key, String... aliases) {
250298 // System properties take precedence over env
251299 // prior art:
252300 // https://docs.spring.io/spring-boot/docs/1.5.6.RELEASE/reference/html/boot-features-external-config.html
253- // We reverse iterate to allow overrides
254- for (int i = sources . length - 1 ; 0 <= i ; i -- ) {
255- String value = sources [ i ] .get (key , aliases );
301+ // We iterate in order so higher precedence sources overwrite lower precedence
302+ for (Source source : sources ) {
303+ String value = source .get (key , aliases );
256304 Map <String , String > parsedMap = ConfigConverter .parseMap (value , key );
257305 if (!parsedMap .isEmpty ()) {
258- origin = sources [i ].origin ();
306+ origin = source .origin ();
307+ if (collectConfig ) {
308+ ConfigCollector .get ().put (key , parsedMap , origin );
309+ }
259310 }
260311 merged .putAll (parsedMap );
261312 }
262313 if (collectConfig ) {
314+ // TO DISCUSS: But if multiple sources have been set, origin isn't exactly accurate here...?
263315 ConfigCollector .get ().put (key , merged , origin );
264316 }
265317 return merged ;
@@ -271,13 +323,16 @@ public Map<String, String> getMergedTagsMap(String key, String... aliases) {
271323 // System properties take precedence over env
272324 // prior art:
273325 // https://docs.spring.io/spring-boot/docs/1.5.6.RELEASE/reference/html/boot-features-external-config.html
274- // We reverse iterate to allow overrides
275- for (int i = sources . length - 1 ; 0 <= i ; i -- ) {
276- String value = sources [ i ] .get (key , aliases );
326+ // We iterate in order so higher precedence sources overwrite lower precedence
327+ for (Source source : sources ) {
328+ String value = source .get (key , aliases );
277329 Map <String , String > parsedMap =
278330 ConfigConverter .parseTraceTagsMap (value , ':' , Arrays .asList (',' , ' ' ));
279331 if (!parsedMap .isEmpty ()) {
280- origin = sources [i ].origin ();
332+ origin = source .origin ();
333+ if (collectConfig ) {
334+ ConfigCollector .get ().put (key , parsedMap , origin );
335+ }
281336 }
282337 merged .putAll (parsedMap );
283338 }
@@ -293,12 +348,15 @@ public Map<String, String> getOrderedMap(String key) {
293348 // System properties take precedence over env
294349 // prior art:
295350 // https://docs.spring.io/spring-boot/docs/1.5.6.RELEASE/reference/html/boot-features-external-config.html
296- // We reverse iterate to allow overrides
297- for (int i = sources . length - 1 ; 0 <= i ; i -- ) {
298- String value = sources [ i ] .get (key );
351+ // We iterate in order so higher precedence sources overwrite lower precedence
352+ for (Source source : sources ) {
353+ String value = source .get (key );
299354 Map <String , String > parsedMap = ConfigConverter .parseOrderedMap (value , key );
300355 if (!parsedMap .isEmpty ()) {
301- origin = sources [i ].origin ();
356+ origin = source .origin ();
357+ if (collectConfig ) {
358+ ConfigCollector .get ().put (key , parsedMap , origin );
359+ }
302360 }
303361 merged .putAll (parsedMap );
304362 }
@@ -315,14 +373,17 @@ public Map<String, String> getMergedMapWithOptionalMappings(
315373 // System properties take precedence over env
316374 // prior art:
317375 // https://docs.spring.io/spring-boot/docs/1.5.6.RELEASE/reference/html/boot-features-external-config.html
318- // We reverse iterate to allow overrides
376+ // We iterate in order so higher precedence sources overwrite lower precedence
319377 for (String key : keys ) {
320- for (int i = sources . length - 1 ; 0 <= i ; i -- ) {
321- String value = sources [ i ] .get (key );
378+ for (Source source : sources ) {
379+ String value = source .get (key );
322380 Map <String , String > parsedMap =
323381 ConfigConverter .parseMapWithOptionalMappings (value , key , defaultPrefix , lowercaseKeys );
324382 if (!parsedMap .isEmpty ()) {
325- origin = sources [i ].origin ();
383+ origin = source .origin ();
384+ if (collectConfig ) {
385+ ConfigCollector .get ().put (key , parsedMap , origin );
386+ }
326387 }
327388 merged .putAll (parsedMap );
328389 }
@@ -378,15 +439,15 @@ public static ConfigProvider createDefault() {
378439 loadConfigurationFile (
379440 new ConfigProvider (new SystemPropertiesConfigSource (), new EnvironmentConfigSource ()));
380441 if (configProperties .isEmpty ()) {
381- return new ConfigProvider (
442+ return createWithPrecedenceOrder (
382443 new SystemPropertiesConfigSource (),
383444 StableConfigSource .FLEET ,
384445 new EnvironmentConfigSource (),
385446 new OtelEnvironmentConfigSource (),
386447 StableConfigSource .LOCAL ,
387448 new CapturedEnvironmentConfigSource ());
388449 } else {
389- return new ConfigProvider (
450+ return createWithPrecedenceOrder (
390451 new SystemPropertiesConfigSource (),
391452 StableConfigSource .FLEET ,
392453 new EnvironmentConfigSource (),
@@ -400,10 +461,10 @@ public static ConfigProvider createDefault() {
400461 public static ConfigProvider withoutCollector () {
401462 Properties configProperties =
402463 loadConfigurationFile (
403- new ConfigProvider (
464+ createWithPrecedenceOrder (
404465 false , new SystemPropertiesConfigSource (), new EnvironmentConfigSource ()));
405466 if (configProperties .isEmpty ()) {
406- return new ConfigProvider (
467+ return createWithPrecedenceOrder (
407468 false ,
408469 new SystemPropertiesConfigSource (),
409470 StableConfigSource .FLEET ,
@@ -412,7 +473,7 @@ public static ConfigProvider withoutCollector() {
412473 StableConfigSource .LOCAL ,
413474 new CapturedEnvironmentConfigSource ());
414475 } else {
415- return new ConfigProvider (
476+ return createWithPrecedenceOrder (
416477 false ,
417478 new SystemPropertiesConfigSource (),
418479 StableConfigSource .FLEET ,
@@ -428,12 +489,12 @@ public static ConfigProvider withPropertiesOverride(Properties properties) {
428489 PropertiesConfigSource providedConfigSource = new PropertiesConfigSource (properties , false );
429490 Properties configProperties =
430491 loadConfigurationFile (
431- new ConfigProvider (
492+ createWithPrecedenceOrder (
432493 new SystemPropertiesConfigSource (),
433494 new EnvironmentConfigSource (),
434495 providedConfigSource ));
435496 if (configProperties .isEmpty ()) {
436- return new ConfigProvider (
497+ return createWithPrecedenceOrder (
437498 new SystemPropertiesConfigSource (),
438499 StableConfigSource .FLEET ,
439500 new EnvironmentConfigSource (),
@@ -442,7 +503,7 @@ public static ConfigProvider withPropertiesOverride(Properties properties) {
442503 StableConfigSource .LOCAL ,
443504 new CapturedEnvironmentConfigSource ());
444505 } else {
445- return new ConfigProvider (
506+ return createWithPrecedenceOrder (
446507 providedConfigSource ,
447508 new SystemPropertiesConfigSource (),
448509 StableConfigSource .FLEET ,
0 commit comments