11package datadog .trace .api ;
22
3+ import java .io .File ;
4+ import java .io .FileNotFoundException ;
5+ import java .io .FileReader ;
6+ import java .io .IOException ;
37import java .net .InetAddress ;
48import java .net .UnknownHostException ;
59import java .util .Arrays ;
1923import lombok .extern .slf4j .Slf4j ;
2024
2125/**
22- * Config gives priority to system properties and falls back to environment variables. It also
23- * includes default values to ensure a valid config.
26+ * Config reads values with the following priority: 1) system properties, 2) environment variables,
27+ * 3) optional configuration file. It also includes default values to ensure a valid config.
2428 *
2529 * <p>
2630 *
@@ -35,6 +39,7 @@ public class Config {
3539
3640 private static final Pattern ENV_REPLACEMENT = Pattern .compile ("[^a-zA-Z0-9_]" );
3741
42+ public static final String CONFIGURATION_FILE = "trace.config" ;
3843 public static final String SERVICE_NAME = "service.name" ;
3944 public static final String SERVICE = "service" ;
4045 public static final String TRACE_ENABLED = "trace.enabled" ;
@@ -190,9 +195,14 @@ public enum PropagationStyle {
190195
191196 @ Getter private final boolean traceAnalyticsEnabled ;
192197
193- // Read order: System Properties -> Env Variables, [-> default value]
198+ // Values from an optionally provided properties file
199+ private static Properties propertiesFromConfigFile ;
200+
201+ // Read order: System Properties -> Env Variables, [-> properties file], [-> default value]
194202 // Visible for testing
195203 Config () {
204+ propertiesFromConfigFile = loadConfigurationFile ();
205+
196206 runtimeId = UUID .randomUUID ().toString ();
197207
198208 serviceName = getSettingFromEnvironment (SERVICE_NAME , DEFAULT_SERVICE_NAME );
@@ -566,25 +576,43 @@ public static boolean traceAnalyticsIntegrationEnabled(
566576 /**
567577 * Helper method that takes the name, adds a "dd." prefix then checks for System Properties of
568578 * that name. If none found, the name is converted to an Environment Variable and used to check
569- * the env. If setting not configured in either location, defaultValue is returned.
579+ * the env. If none of the above returns a value, then an optional properties file if checked. If
580+ * setting is not configured in either location, <code>defaultValue</code> is returned.
570581 *
571582 * @param name
572583 * @param defaultValue
573584 * @return
574585 * @deprecated This method should only be used internally. Use the explicit getter instead.
575586 */
576587 public static String getSettingFromEnvironment (final String name , final String defaultValue ) {
577- final String completeName = PREFIX + name ;
578- final String value =
579- System .getProperties ()
580- .getProperty (completeName , System .getenv (propertyToEnvironmentName (completeName )));
581- return value == null ? defaultValue : value ;
588+ String value ;
589+
590+ // System properties and properties provided from command line have the highest precedence
591+ value = System .getProperties ().getProperty (propertyNameToSystemPropertyName (name ));
592+ if (null != value ) {
593+ return value ;
594+ }
595+
596+ // If value not provided from system properties, looking at env variables
597+ value = System .getenv (propertyNameToEnvironmentVariableName (name ));
598+ if (null != value ) {
599+ return value ;
600+ }
601+
602+ // If value is not defined yet, we look at properties optionally defined in a properties file
603+ value = propertiesFromConfigFile .getProperty (propertyNameToSystemPropertyName (name ));
604+ if (null != value ) {
605+ return value ;
606+ }
607+
608+ return defaultValue ;
582609 }
583610
584611 /** @deprecated This method should only be used internally. Use the explicit getter instead. */
585612 private static Map <String , String > getMapSettingFromEnvironment (
586613 final String name , final String defaultValue ) {
587- return parseMap (getSettingFromEnvironment (name , defaultValue ), PREFIX + name );
614+ return parseMap (
615+ getSettingFromEnvironment (name , defaultValue ), propertyNameToSystemPropertyName (name ));
588616 }
589617
590618 /**
@@ -674,8 +702,28 @@ private Set<Integer> getIntegerRangeSettingFromEnvironment(
674702 }
675703 }
676704
677- private static String propertyToEnvironmentName (final String name ) {
678- return ENV_REPLACEMENT .matcher (name .toUpperCase ()).replaceAll ("_" );
705+ /**
706+ * Converts the property name, e.g. 'service.name' into a public environment variable name, e.g.
707+ * `DD_SERVICE_NAME`.
708+ *
709+ * @param setting The setting name, e.g. `service.name`
710+ * @return The public facing environment variable name
711+ */
712+ private static String propertyNameToEnvironmentVariableName (final String setting ) {
713+ return ENV_REPLACEMENT
714+ .matcher (propertyNameToSystemPropertyName (setting ).toUpperCase ())
715+ .replaceAll ("_" );
716+ }
717+
718+ /**
719+ * Converts the property name, e.g. 'service.name' into a public system property name, e.g.
720+ * `dd.service.name`.
721+ *
722+ * @param setting The setting name, e.g. `service.name`
723+ * @return The public facing system property name
724+ */
725+ private static String propertyNameToSystemPropertyName (String setting ) {
726+ return PREFIX + setting ;
679727 }
680728
681729 private static Map <String , String > getPropertyMapValue (
@@ -830,6 +878,50 @@ private static <V extends Enum<V>> Set<V> convertStringSetToEnumSet(
830878 return Collections .unmodifiableSet (result );
831879 }
832880
881+ /**
882+ * Loads the optional configuration properties file into the global {@link Properties} object.
883+ *
884+ * @return The {@link Properties} object. the returned instance might be empty of file does not
885+ * exist or if it is in a wrong format.
886+ */
887+ private static Properties loadConfigurationFile () {
888+ Properties properties = new Properties ();
889+
890+ // Reading from system property first and from env after
891+ String configurationFilePath =
892+ System .getProperty (propertyNameToSystemPropertyName (CONFIGURATION_FILE ));
893+ if (null == configurationFilePath ) {
894+ configurationFilePath =
895+ System .getenv (propertyNameToEnvironmentVariableName (CONFIGURATION_FILE ));
896+ }
897+ if (null == configurationFilePath ) {
898+ return properties ;
899+ }
900+
901+ // Normalizing tilde (~) paths for unix systems
902+ configurationFilePath =
903+ configurationFilePath .replaceFirst ("^~" , System .getProperty ("user.home" ));
904+
905+ // Configuration properties file is optional
906+ File configurationFile = new File (configurationFilePath );
907+ if (!configurationFile .exists ()) {
908+ log .error ("Configuration file '{}' not found." , configurationFilePath );
909+ return properties ;
910+ }
911+
912+ try {
913+ FileReader fileReader = new FileReader (configurationFile );
914+ properties .load (fileReader );
915+ } catch (FileNotFoundException fnf ) {
916+ log .error ("Configuration file '{}' not found." , configurationFilePath );
917+ } catch (IOException ioe ) {
918+ log .error (
919+ "Configuration file '{}' cannot be accessed or correctly parsed." , configurationFilePath );
920+ }
921+
922+ return properties ;
923+ }
924+
833925 /**
834926 * Returns the detected hostname. This operation is time consuming so if the usage changes and
835927 * this method will be called several times then we should implement some sort of caching.
0 commit comments