Skip to content

Commit 72cad28

Browse files
authored
Merge pull request #862 from DataDog/labbati/config-file
Provide a way to configure the tracer using properties file
2 parents e8246f6 + d865a1a commit 72cad28

3 files changed

Lines changed: 167 additions & 12 deletions

File tree

dd-trace-api/src/main/java/datadog/trace/api/Config.java

Lines changed: 104 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package datadog.trace.api;
22

3+
import java.io.File;
4+
import java.io.FileNotFoundException;
5+
import java.io.FileReader;
6+
import java.io.IOException;
37
import java.net.InetAddress;
48
import java.net.UnknownHostException;
59
import java.util.Arrays;
@@ -19,8 +23,8 @@
1923
import 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.

dd-trace-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import spock.lang.Specification
88
import static datadog.trace.api.Config.AGENT_HOST
99
import static datadog.trace.api.Config.AGENT_PORT_LEGACY
1010
import static datadog.trace.api.Config.AGENT_UNIX_DOMAIN_SOCKET
11+
import static datadog.trace.api.Config.CONFIGURATION_FILE
1112
import static datadog.trace.api.Config.DB_CLIENT_HOST_SPLIT_BY_INSTANCE
1213
import static datadog.trace.api.Config.DEFAULT_JMX_FETCH_STATSD_PORT
1314
import static datadog.trace.api.Config.GLOBAL_TAGS
@@ -725,4 +726,65 @@ class ConfigTest extends Specification {
725726
then:
726727
config.localRootSpanTags.get('_dd.hostname') == InetAddress.localHost.hostName
727728
}
729+
730+
def "verify fallback to properties file"() {
731+
setup:
732+
System.setProperty(PREFIX + CONFIGURATION_FILE, "src/test/resources/dd-java-tracer.properties")
733+
734+
when:
735+
def config = new Config()
736+
737+
then:
738+
config.serviceName == "set-in-properties"
739+
740+
cleanup:
741+
System.clearProperty(PREFIX + CONFIGURATION_FILE)
742+
}
743+
744+
def "verify fallback to properties file has lower priority than system property"() {
745+
setup:
746+
System.setProperty(PREFIX + CONFIGURATION_FILE, "src/test/resources/dd-java-tracer.properties")
747+
System.setProperty(PREFIX + SERVICE_NAME, "set-in-system")
748+
749+
when:
750+
def config = new Config()
751+
752+
then:
753+
config.serviceName == "set-in-system"
754+
755+
cleanup:
756+
System.clearProperty(PREFIX + CONFIGURATION_FILE)
757+
System.clearProperty(PREFIX + SERVICE_NAME)
758+
}
759+
760+
def "verify fallback to properties file has lower priority than env var"() {
761+
setup:
762+
System.setProperty(PREFIX + CONFIGURATION_FILE, "src/test/resources/dd-java-tracer.properties")
763+
environmentVariables.set("DD_SERVICE_NAME", "set-in-env")
764+
765+
when:
766+
def config = new Config()
767+
768+
then:
769+
config.serviceName == "set-in-env"
770+
771+
cleanup:
772+
System.clearProperty(PREFIX + CONFIGURATION_FILE)
773+
System.clearProperty(PREFIX + SERVICE_NAME)
774+
environmentVariables.clear("DD_SERVICE_NAME")
775+
}
776+
777+
def "verify fallback to properties file that does not exist does not crash app"() {
778+
setup:
779+
System.setProperty(PREFIX + CONFIGURATION_FILE, "src/test/resources/do-not-exist.properties")
780+
781+
when:
782+
def config = new Config()
783+
784+
then:
785+
config.serviceName == 'unnamed-java-app'
786+
787+
cleanup:
788+
System.clearProperty(PREFIX + CONFIGURATION_FILE)
789+
}
728790
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dd.service.name=set-in-properties

0 commit comments

Comments
 (0)