Config and builder
MODIFY
dd-trace-api/src/main/java/datadog/trace/api/Config.java
dd-trace-api/src/main/java/datadog/trace/api/Config.java
@@ -63,6 +63,10 @@ public class Config {
public static final String TRACE_EXECUTORS = "trace.executors";
public static final String TRACE_METHODS = "trace.methods";
public static final String TRACE_CLASSES_EXCLUDE = "trace.classes.exclude";
+ public static final String TRACE_SAMPLING_SERVICE_RULES = "trace.sampling.service.rules";
+ public static final String TRACE_SAMPLING_OPERATION_RULES = "trace.sampling.operation.rules";
+ public static final String TRACE_SAMPLING_DEFAULT_RATE = "trace.sampling.default.rate";
+ public static final String TRACE_SAMPLING_RATE_LIMIT = "trace.sampling.rate.limit";
public static final String TRACE_REPORT_HOSTNAME = "trace.report-hostname";
public static final String HEADER_TAGS = "trace.header.tags";
public static final String HTTP_SERVER_ERROR_STATUSES = "http.server.error.statuses";
@@ -145,6 +149,7 @@ public class Config {
private static final String DEFAULT_TRACE_METHODS = null;
public static final boolean DEFAULT_TRACE_ANALYTICS_ENABLED = false;
public static final float DEFAULT_ANALYTICS_SAMPLE_RATE = 1.0f;
+ public static final double DEFAULT_TRACE_SAMPLING_RATE_LIMIT = 100;
public enum PropagationStyle {
DATADOG,
@@ -215,6 +220,11 @@ public class Config {
@Getter private final boolean traceAnalyticsEnabled;
+ @Getter private final Map<String, String> traceSamplingServiceRules;
+ @Getter private final Map<String, String> traceSamplingOperationRules;
+ @Getter private final Double traceSamplingDefaultRate;
+ @Getter private final Double traceSamplingRateLimit;
+
// Values from an optionally provided properties file
private static Properties propertiesFromConfigFile;
@@ -336,6 +346,14 @@ public class Config {
traceAnalyticsEnabled =
getBooleanSettingFromEnvironment(TRACE_ANALYTICS_ENABLED, DEFAULT_TRACE_ANALYTICS_ENABLED);
+ traceSamplingServiceRules = getMapSettingFromEnvironment(TRACE_SAMPLING_SERVICE_RULES, null);
+ traceSamplingOperationRules =
+ getMapSettingFromEnvironment(TRACE_SAMPLING_OPERATION_RULES, null);
+ traceSamplingDefaultRate = getDoubleSettingFromEnvironment(TRACE_SAMPLING_DEFAULT_RATE, null);
+ traceSamplingRateLimit =
+ getDoubleSettingFromEnvironment(
+ TRACE_SAMPLING_RATE_LIMIT, DEFAULT_TRACE_SAMPLING_RATE_LIMIT);
+
log.debug("New instance: {}", this);
}
@@ -460,6 +478,19 @@ public class Config {
traceAnalyticsEnabled =
getPropertyBooleanValue(properties, TRACE_ANALYTICS_ENABLED, parent.traceAnalyticsEnabled);
+ traceSamplingServiceRules =
+ getPropertyMapValue(
+ properties, TRACE_SAMPLING_SERVICE_RULES, parent.traceSamplingServiceRules);
+ traceSamplingOperationRules =
+ getPropertyMapValue(
+ properties, TRACE_SAMPLING_OPERATION_RULES, parent.traceSamplingOperationRules);
+ traceSamplingDefaultRate =
+ getPropertyDoubleValue(
+ properties, TRACE_SAMPLING_DEFAULT_RATE, parent.traceSamplingDefaultRate);
+ traceSamplingRateLimit =
+ getPropertyDoubleValue(
+ properties, TRACE_SAMPLING_RATE_LIMIT, parent.traceSamplingRateLimit);
+
log.debug("New instance: {}", this);
}
@@ -697,6 +728,22 @@ public class Config {
}
}
+ /**
+ * Calls {@link #getSettingFromEnvironment(String, String)} and converts the result to a Double.
+ *
+ * @deprecated This method should only be used internally. Use the explicit getter instead.
+ */
+ public static Double getDoubleSettingFromEnvironment(
+ final String name, final Double defaultValue) {
+ final String value = getSettingFromEnvironment(name, null);
+ try {
+ return value == null ? defaultValue : Double.valueOf(value);
+ } catch (final NumberFormatException e) {
+ log.warn("Invalid configuration for " + name, e);
+ return defaultValue;
+ }
+ }
+
/**
* Calls {@link #getSettingFromEnvironment(String, String)} and converts the result to a Integer.
*/
@@ -795,6 +842,18 @@ public class Config {
return value == null || value.trim().isEmpty() ? defaultValue : Integer.valueOf(value);
}
+ private static Float getPropertyFloatValue(
+ final Properties properties, final String name, final Float defaultValue) {
+ final String value = properties.getProperty(name);
+ return value == null || value.trim().isEmpty() ? defaultValue : Float.valueOf(value);
+ }
+
+ private static Double getPropertyDoubleValue(
+ final Properties properties, final String name, final Double defaultValue) {
+ final String value = properties.getProperty(name);
+ return value == null || value.trim().isEmpty() ? defaultValue : Double.valueOf(value);
+ }
+
private static <T extends Enum<T>> Set<T> getPropertySetValue(
final Properties properties, final String name, final Class<T> clazz) {
final String value = properties.getProperty(name);

MODIFY
dd-trace-ot/src/main/java/datadog/trace/common/sampling/RuleBasedSampler.java
dd-trace-ot/src/main/java/datadog/trace/common/sampling/RuleBasedSampler.java
@@ -32,6 +32,47 @@ public class RuleBasedSampler implements Sampler, PrioritySampler {
this.rateLimit = rateLimit;
}
+ public static RuleBasedSampler build(
+ final Map<String, String> serviceRules,
+ final Map<String, String> operationRules,
+ final Double defaultRate,
+ final double rateLimit) {
+
+ final List<SamplingRule> samplingRules = new ArrayList<>();
+
+ if (serviceRules != null) {
+ for (final Entry<String, String> entry : serviceRules.entrySet()) {
+ try {
+ final double rateForEntry = Double.parseDouble(entry.getValue());
+ final SamplingRule samplingRule =
+ new ServiceSamplingRule(entry.getKey(), new KnuthSampler(rateForEntry));
+ samplingRules.add(samplingRule);
+ } catch (final NumberFormatException e) {
+ log.error("Unable to parse rate for service: {}", entry, e);
+ }
+ }
+ }
+
+ if (operationRules != null) {
+ for (final Entry<String, String> entry : operationRules.entrySet()) {
+ try {
+ final double rateForEntry = Double.parseDouble(entry.getValue());
+ final SamplingRule samplingRule =
+ new OperationSamplingRule(entry.getKey(), new KnuthSampler(rateForEntry));
+ samplingRules.add(samplingRule);
+ } catch (final NumberFormatException e) {
+ log.error("Unable to parse rate for operation: {}", entry, e);
+ }
+ }
+ }
+
+ if (defaultRate != null) {
+ final SamplingRule samplingRule = new AlwaysMatchesSamplingRule(new KnuthSampler(defaultRate));
+ samplingRules.add(samplingRule);
+ }
+
+ return new RuleBasedSampler(samplingRules, rateLimit, new RateByServiceSampler());
+ }
@Override
public boolean sample(final DDSpan span) {

MODIFY
dd-trace-ot/src/main/java/datadog/trace/common/sampling/Sampler.java
dd-trace-ot/src/main/java/datadog/trace/common/sampling/Sampler.java
@@ -2,7 +2,9 @@ package datadog.trace.common.sampling;
import datadog.opentracing.DDSpan;
import datadog.trace.api.Config;
+import java.util.Map;
import java.util.Properties;
+import lombok.extern.slf4j.Slf4j;
/** Main interface to sample a collection of traces. */
public interface Sampler {
@@ -15,11 +17,30 @@ public interface Sampler {
*/
boolean sample(DDSpan span);
+ @Slf4j
final class Builder {
public static Sampler forConfig(final Config config) {
- final Sampler sampler;
+ Sampler sampler;
if (config != null) {
- if (config.isPrioritySamplingEnabled()) {
+ final Map<String, String> serviceRules = config.getTraceSamplingServiceRules();
+ final Map<String, String> operationRules = config.getTraceSamplingOperationRules();
+
+ if ((serviceRules != null && !serviceRules.isEmpty())
+ || (operationRules != null && !operationRules.isEmpty())
+ || config.getTraceSamplingDefaultRate() != null) {
+
+ try {
+ sampler =
+ RuleBasedSampler.build(
+ serviceRules,
+ operationRules,
+ config.getTraceSamplingDefaultRate(),
+ config.getTraceSamplingRateLimit());
+ } catch (final IllegalArgumentException e) {
+ log.error("Invalid sampler configuration. Using AllSampler", e);
+ sampler = new AllSampler();
+ }
+ } else if (config.isPrioritySamplingEnabled()) {
sampler = new RateByServiceSampler();
} else {
sampler = new AllSampler();

Config and builder