From 28947ba7b875eb95f72ae40fcc8b42359c89c692 Mon Sep 17 00:00:00 2001 From: Vikentiy Fesunov Date: Thu, 23 Jul 2026 14:44:44 +0200 Subject: [PATCH 1/2] Isolate tests from system environment --- src/main/java/com/timgroup/statsd/EnvMap.java | 19 +++++++ .../statsd/NonBlockingStatsDClient.java | 18 +++---- .../NonBlockingStatsDClientBuilder.java | 37 ++++++++++---- .../com/timgroup/statsd/AggregationTest.java | 2 + .../timgroup/statsd/BuilderAddressTest.java | 34 +++++++++---- .../statsd/CardinalityOverrideTest.java | 13 +++-- .../com/timgroup/statsd/NamedPipeTest.java | 2 + .../NonBlockingDirectStatsDClientTest.java | 8 +-- .../NonBlockingStatsDClientBuilderTest.java | 46 +++++++++++------ .../statsd/NonBlockingStatsDClientTest.java | 50 +++++++++++++++---- .../com/timgroup/statsd/TelemetryTest.java | 12 +++-- .../com/timgroup/statsd/UnixSocketTest.java | 6 +++ .../timgroup/statsd/UnixStreamSocketTest.java | 7 +++ 13 files changed, 187 insertions(+), 67 deletions(-) create mode 100644 src/main/java/com/timgroup/statsd/EnvMap.java diff --git a/src/main/java/com/timgroup/statsd/EnvMap.java b/src/main/java/com/timgroup/statsd/EnvMap.java new file mode 100644 index 00000000..3c3e5a27 --- /dev/null +++ b/src/main/java/com/timgroup/statsd/EnvMap.java @@ -0,0 +1,19 @@ +package com.timgroup.statsd; + +import java.util.Map; + +class EnvMap { + private final Map env; + + EnvMap() { + env = null; + } + + EnvMap(Map provided) { + env = provided; + } + + String get(String name) { + return env != null ? env.get(name) : System.getenv(name); + } +} diff --git a/src/main/java/com/timgroup/statsd/NonBlockingStatsDClient.java b/src/main/java/com/timgroup/statsd/NonBlockingStatsDClient.java index 4ea7591c..2cb8a004 100644 --- a/src/main/java/com/timgroup/statsd/NonBlockingStatsDClient.java +++ b/src/main/java/com/timgroup/statsd/NonBlockingStatsDClient.java @@ -76,10 +76,6 @@ String envName() { return (PREFIX + "_" + toString()).toUpperCase(); } - String envVal() { - return System.getenv(envName()); - } - String tag() { return toString().toLowerCase(); } @@ -182,6 +178,7 @@ protected static String format(ThreadLocal formatter, Number value private final String containerID; private final String externalEnv; final TagsCardinality clientTagsCardinality; + final EnvMap env; /** * Create a new StatsD client communicating with a StatsD instance on the host and port @@ -209,6 +206,7 @@ public NonBlockingStatsDClient(final NonBlockingStatsDClientBuilder builder) blocking = builder.blocking; maxPacketSizeBytes = builder.maxPacketSizeBytes; clientTagsCardinality = builder.tagsCardinality; + env = builder.env; { List constantPreTags = new ArrayList<>(); @@ -220,7 +218,7 @@ public NonBlockingStatsDClient(final NonBlockingStatsDClientBuilder builder) // Support "dd.internal.entity_id" internal tag. updateTagsWithEntityID(constantPreTags, builder.entityID); for (final Literal literal : Literal.values()) { - final String envVal = literal.envVal(); + final String envVal = env.get(literal.envName()); if (envVal != null && !envVal.trim().isEmpty()) { constantPreTags.add(literal.tag() + ":" + envVal); } @@ -240,7 +238,7 @@ public NonBlockingStatsDClient(final NonBlockingStatsDClientBuilder builder) boolean originDetectionEnabled = isOriginDetectionEnabled(builder.originDetectionEnabled); containerID = getContainerID(builder.containerID, originDetectionEnabled); - externalEnv = originDetectionEnabled ? Utf8.sanitize(System.getenv("DD_EXTERNAL_ENV")) : ""; + externalEnv = originDetectionEnabled ? Utf8.sanitize(env.get("DD_EXTERNAL_ENV")) : ""; try { clientChannel = @@ -1521,11 +1519,11 @@ public boolean writeTo(StringBuilder sb, int capacity) { * @param entityID the entityID string provided by argument * @return true if tags was modified */ - private static boolean updateTagsWithEntityID(final List tags, String entityID) { + private boolean updateTagsWithEntityID(final List tags, String entityID) { // Support "dd.internal.entity_id" internal tag. if (entityID == null || entityID.trim().isEmpty()) { // if the entityID parameter is null, default to the environment variable - entityID = System.getenv(DD_ENTITY_ID_ENV_VAR); + entityID = env.get(DD_ENTITY_ID_ENV_VAR); } if (entityID != null && !entityID.trim().isEmpty()) { final String entityTag = ENTITY_ID_TAG_NAME + ":" + entityID; @@ -1591,14 +1589,14 @@ protected boolean isInvalidSample(double sampleRate) { return sampleRate != 1 && ThreadLocalRandom.current().nextDouble() > sampleRate; } - static boolean isOriginDetectionEnabled(boolean originDetectionEnabled) { + boolean isOriginDetectionEnabled(boolean originDetectionEnabled) { if (!originDetectionEnabled) { // origin detection is explicitly disabled // or a user-defined container ID was provided return false; } - String value = System.getenv(ORIGIN_DETECTION_ENABLED_ENV_VAR); + String value = env.get(ORIGIN_DETECTION_ENABLED_ENV_VAR); value = value != null ? value.trim() : null; if (value != null && !value.isEmpty()) { return !Arrays.asList("no", "false", "0", "n", "off").contains(value.toLowerCase()); diff --git a/src/main/java/com/timgroup/statsd/NonBlockingStatsDClientBuilder.java b/src/main/java/com/timgroup/statsd/NonBlockingStatsDClientBuilder.java index 289b16c2..89f30326 100644 --- a/src/main/java/com/timgroup/statsd/NonBlockingStatsDClientBuilder.java +++ b/src/main/java/com/timgroup/statsd/NonBlockingStatsDClientBuilder.java @@ -5,6 +5,7 @@ import java.net.SocketAddress; import java.net.URI; import java.net.UnknownHostException; +import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ThreadFactory; import jnr.unixsocket.UnixSocketAddress; @@ -122,6 +123,8 @@ public class NonBlockingStatsDClientBuilder implements Cloneable { public ThreadFactory threadFactory; public TagsCardinality tagsCardinality = null; + EnvMap env = new EnvMap(); + public NonBlockingStatsDClientBuilder() {} public NonBlockingStatsDClientBuilder port(int val) { @@ -334,6 +337,22 @@ public NonBlockingStatsDClientBuilder tagsCardinality(TagsCardinality cardinalit return this; } + /** + * Overrides the environment variables the client reads configuration from. + * + *

By default the client reads from the process environment via {@link + * System#env.get(String)}. Supplying a map makes the client read configuration (e.g. {@code + * DD_ENV}, {@code DD_SERVICE}, {@code DD_VERSION}, {@code DD_ENTITY_ID}) exclusively from that + * map, so it is unaffected by the ambient process environment. + * + * @param env the map to read environment variables from, or null to use the process environment + * @return this builder + */ + public NonBlockingStatsDClientBuilder withEnvironmentVariables(Map env) { + this.env = new EnvMap(env); + return this; + } + /** * NonBlockingStatsDClient factory method. * @@ -386,11 +405,10 @@ protected NonBlockingStatsDClientBuilder resolve() { resolved.tagsCardinality = this.tagsCardinality; if (resolved.tagsCardinality == null) { - resolved.tagsCardinality = TagsCardinality.fromString(System.getenv("DD_CARDINALITY")); + resolved.tagsCardinality = TagsCardinality.fromString(env.get("DD_CARDINALITY")); } if (resolved.tagsCardinality == null) { - resolved.tagsCardinality = - TagsCardinality.fromString(System.getenv("DATADOG_CARDINALITY")); + resolved.tagsCardinality = TagsCardinality.fromString(env.get("DATADOG_CARDINALITY")); } if (resolved.tagsCardinality == null) { resolved.tagsCardinality = TagsCardinality.DEFAULT; @@ -414,12 +432,12 @@ private Callable getAddressLookup() { } // Next, try various environment variables. - String url = System.getenv(NonBlockingStatsDClient.DD_DOGSTATSD_URL_ENV_VAR); + String url = env.get(NonBlockingStatsDClient.DD_DOGSTATSD_URL_ENV_VAR); if (url != null) { return getAddressLookupFromUrl(url); } - String namedPipeFromEnv = System.getenv(NonBlockingStatsDClient.DD_NAMED_PIPE_ENV_VAR); + String namedPipeFromEnv = env.get(NonBlockingStatsDClient.DD_NAMED_PIPE_ENV_VAR); if (namedPipeFromEnv != null) { return staticNamedPipeResolution(namedPipeFromEnv); } @@ -543,8 +561,8 @@ private static Callable staticAddress(final String hostname, fina * @return host name from the environment variable "DD_AGENT_HOST" * @throws StatsDClientException if the environment variable is not set */ - private static String getHostnameFromEnvVar() { - final String hostname = System.getenv(NonBlockingStatsDClient.DD_AGENT_HOST_ENV_VAR); + private String getHostnameFromEnvVar() { + final String hostname = env.get(NonBlockingStatsDClient.DD_AGENT_HOST_ENV_VAR); if (hostname == null) { throw new StatsDClientException( "Failed to retrieve agent hostname from environment variable", null); @@ -558,9 +576,8 @@ private static String getHostnameFromEnvVar() { * @return dogstatsd port from the environment variable "DD_DOGSTATSD_PORT" * @throws StatsDClientException if the environment variable is an integer */ - private static int getPortFromEnvVar(final int defaultPort) { - final String statsDPortString = - System.getenv(NonBlockingStatsDClient.DD_DOGSTATSD_PORT_ENV_VAR); + private int getPortFromEnvVar(final int defaultPort) { + final String statsDPortString = env.get(NonBlockingStatsDClient.DD_DOGSTATSD_PORT_ENV_VAR); if (statsDPortString == null) { return defaultPort; } else { diff --git a/src/test/java/com/timgroup/statsd/AggregationTest.java b/src/test/java/com/timgroup/statsd/AggregationTest.java index c9ab755f..637098cd 100644 --- a/src/test/java/com/timgroup/statsd/AggregationTest.java +++ b/src/test/java/com/timgroup/statsd/AggregationTest.java @@ -6,6 +6,7 @@ import static org.hamcrest.Matchers.startsWith; import java.io.IOException; +import java.util.HashMap; import java.util.List; import org.junit.After; import org.junit.Before; @@ -20,6 +21,7 @@ public void start() throws IOException { server = new UDPDummyStatsDServer(0); testClient = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(new HashMap()) .prefix("my.prefix") .hostname("localhost") .port(server.getPort()) diff --git a/src/test/java/com/timgroup/statsd/BuilderAddressTest.java b/src/test/java/com/timgroup/statsd/BuilderAddressTest.java index d37703d5..ea344be5 100644 --- a/src/test/java/com/timgroup/statsd/BuilderAddressTest.java +++ b/src/test/java/com/timgroup/statsd/BuilderAddressTest.java @@ -7,18 +7,20 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; +import java.util.Map; import jnr.unixsocket.UnixSocketAddress; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.junit.contrib.java.lang.system.EnvironmentVariables; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class BuilderAddressTest { - @Rule public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); + + // Controlled environment so address resolution only sees the variables this test sets. + Map env; final String url; final String host; @@ -187,6 +189,7 @@ public String getPath() { @Before public void set() { + env = new HashMap<>(); set(NonBlockingStatsDClient.DD_DOGSTATSD_URL_ENV_VAR, url); set(NonBlockingStatsDClient.DD_AGENT_HOST_ENV_VAR, host); set(NonBlockingStatsDClient.DD_DOGSTATSD_PORT_ENV_VAR, port); @@ -195,9 +198,7 @@ public void set() { void set(String name, String val) { if (val != null) { - environmentVariables.set(name, val); - } else { - environmentVariables.clear(name); + env.put(name, val); } } @@ -206,7 +207,7 @@ public void address_resolution() throws Exception { NonBlockingStatsDClientBuilder b; // Default configuration matches env vars - b = new NonBlockingStatsDClientBuilder().resolve(); + b = new NonBlockingStatsDClientBuilder().withEnvironmentVariables(env).resolve(); SocketAddress actual = b.addressLookup.call(); // Make it possible to run this code even if we don't have jnr-unixsocket. @@ -222,13 +223,26 @@ public void address_resolution() throws Exception { } // Explicit configuration is used regardless of environment variables. - b = new NonBlockingStatsDClientBuilder().hostname("2.2.2.2").resolve(); + b = + new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) + .hostname("2.2.2.2") + .resolve(); assertEquals(new InetSocketAddress("2.2.2.2", defaultPort), b.addressLookup.call()); - b = new NonBlockingStatsDClientBuilder().hostname("2.2.2.2").port(2222).resolve(); + b = + new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) + .hostname("2.2.2.2") + .port(2222) + .resolve(); assertEquals(new InetSocketAddress("2.2.2.2", 2222), b.addressLookup.call()); - b = new NonBlockingStatsDClientBuilder().namedPipe("ook").resolve(); + b = + new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) + .namedPipe("ook") + .resolve(); assertEquals(new NamedPipeSocketAddress("ook"), b.addressLookup.call()); } } diff --git a/src/test/java/com/timgroup/statsd/CardinalityOverrideTest.java b/src/test/java/com/timgroup/statsd/CardinalityOverrideTest.java index 751e5406..0485550c 100644 --- a/src/test/java/com/timgroup/statsd/CardinalityOverrideTest.java +++ b/src/test/java/com/timgroup/statsd/CardinalityOverrideTest.java @@ -5,12 +5,12 @@ import static org.hamcrest.Matchers.hasItem; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import java.util.logging.Logger; import org.junit.After; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.junit.contrib.java.lang.system.EnvironmentVariables; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @@ -45,13 +45,14 @@ public static Object[][] parameters() { }); } - @Rule public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); - String clientTagsCardinality; Case msgTagsCardinality; UDPDummyStatsDServer server; NonBlockingStatsDClient client; + // Controlled environment so the client is unaffected by ambient DD_* variables. + Map clientEnv; + public CardinalityOverrideTest(String clientTagsCardinality, Case msgTagsCardinality) { log.info(String.format("%s %s", clientTagsCardinality, msgTagsCardinality.value)); this.clientTagsCardinality = clientTagsCardinality; @@ -75,12 +76,14 @@ private void assertPayload(final String payloadHead) { @Before public void start() throws IOException { + clientEnv = new HashMap<>(); if (clientTagsCardinality != null) { - environmentVariables.set("DD_CARDINALITY", clientTagsCardinality); + clientEnv.put("DD_CARDINALITY", clientTagsCardinality); } server = new UDPDummyStatsDServer(0); client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("my.prefix") .hostname("localhost") .port(server.getPort()) diff --git a/src/test/java/com/timgroup/statsd/NamedPipeTest.java b/src/test/java/com/timgroup/statsd/NamedPipeTest.java index 35e47ba5..65b0e1de 100644 --- a/src/test/java/com/timgroup/statsd/NamedPipeTest.java +++ b/src/test/java/com/timgroup/statsd/NamedPipeTest.java @@ -5,6 +5,7 @@ import static org.hamcrest.Matchers.nullValue; import java.io.IOException; +import java.util.HashMap; import java.util.Random; import java.util.logging.Logger; import org.junit.After; @@ -42,6 +43,7 @@ public void start() { server = new NamedPipeDummyStatsDServer(pipeName); client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(new HashMap()) .prefix("my.prefix") .namedPipe(pipeName) .queueSize(1) diff --git a/src/test/java/com/timgroup/statsd/NonBlockingDirectStatsDClientTest.java b/src/test/java/com/timgroup/statsd/NonBlockingDirectStatsDClientTest.java index d8e1f3c4..47bbbe55 100644 --- a/src/test/java/com/timgroup/statsd/NonBlockingDirectStatsDClientTest.java +++ b/src/test/java/com/timgroup/statsd/NonBlockingDirectStatsDClientTest.java @@ -5,13 +5,13 @@ import static org.hamcrest.Matchers.hasItem; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.FixMethodOrder; -import org.junit.Rule; import org.junit.Test; -import org.junit.contrib.java.lang.system.EnvironmentVariables; import org.junit.runners.MethodSorters; @FixMethodOrder(MethodSorters.NAME_ASCENDING) @@ -22,13 +22,15 @@ public class NonBlockingDirectStatsDClientTest { private static DirectStatsDClient client; private static DummyStatsDServer server; - @Rule public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); + // Controlled (empty) environment so the client is unaffected by ambient DD_* variables. + private static final Map CLIENT_ENV = new HashMap<>(); @BeforeClass public static void start() throws IOException { server = new UDPDummyStatsDServer(STATSD_SERVER_PORT); client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(CLIENT_ENV) .prefix("my.prefix") .hostname("localhost") .port(STATSD_SERVER_PORT) diff --git a/src/test/java/com/timgroup/statsd/NonBlockingStatsDClientBuilderTest.java b/src/test/java/com/timgroup/statsd/NonBlockingStatsDClientBuilderTest.java index 8388df92..bc349081 100644 --- a/src/test/java/com/timgroup/statsd/NonBlockingStatsDClientBuilderTest.java +++ b/src/test/java/com/timgroup/statsd/NonBlockingStatsDClientBuilderTest.java @@ -5,20 +5,21 @@ import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; -import org.junit.Rule; +import java.util.HashMap; +import java.util.Map; import org.junit.Test; -import org.junit.contrib.java.lang.system.EnvironmentVariables; import org.junit.function.ThrowingRunnable; public class NonBlockingStatsDClientBuilderTest { - @Rule public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); @Test(timeout = 5000L) public void origin_detection_env_false() throws Exception { - environmentVariables.set(NonBlockingStatsDClient.ORIGIN_DETECTION_ENABLED_ENV_VAR, "false"); + final Map env = new HashMap<>(); + env.put(NonBlockingStatsDClient.ORIGIN_DETECTION_ENABLED_ENV_VAR, "false"); final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) .prefix("my.prefix") .hostname("localhost") .port(8125) @@ -30,17 +31,18 @@ public void origin_detection_env_false() throws Exception { assertFalse( client.isOriginDetectionEnabled( NonBlockingStatsDClient.DEFAULT_ENABLE_ORIGIN_DETECTION)); - environmentVariables.clear(NonBlockingStatsDClient.ORIGIN_DETECTION_ENABLED_ENV_VAR); } @Test(timeout = 5000L) public void origin_detection_env_unknown() throws Exception { - environmentVariables.set( + final Map env = new HashMap<>(); + env.put( NonBlockingStatsDClient.ORIGIN_DETECTION_ENABLED_ENV_VAR, "unknown"); // default to true final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) .prefix("my.prefix") .hostname("localhost") .port(8125) @@ -53,13 +55,14 @@ public void origin_detection_env_unknown() throws Exception { assertTrue( client.isOriginDetectionEnabled( NonBlockingStatsDClient.DEFAULT_ENABLE_ORIGIN_DETECTION)); - environmentVariables.clear(NonBlockingStatsDClient.ORIGIN_DETECTION_ENABLED_ENV_VAR); } @Test(timeout = 5000L) public void origin_detection_env_unset() throws Exception { + final Map env = new HashMap<>(); final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) .prefix("my.prefix") .hostname("localhost") .port(8125) @@ -76,8 +79,10 @@ public void origin_detection_env_unset() throws Exception { @Test(timeout = 5000L) public void origin_detection_arg_false() throws Exception { + final Map env = new HashMap<>(); final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) .prefix("my.prefix") .hostname("localhost") .port(8125) @@ -96,32 +101,45 @@ public void address_resolution_empty() throws Exception { new ThrowingRunnable() { @Override public void run() { - new NonBlockingStatsDClientBuilder().resolve(); + new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(new HashMap()) + .resolve(); } }); } @Test public void tags_cardinality() throws Exception { - environmentVariables.set("DD_DOGSTATSD_URL", "localhost:8125"); + final Map env = new HashMap<>(); + env.put("DD_DOGSTATSD_URL", "localhost:8125"); // default value assertEquals( TagsCardinality.DEFAULT, - new NonBlockingStatsDClientBuilder().resolve().tagsCardinality); + new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) + .resolve() + .tagsCardinality); // one env variable works - environmentVariables.set("DATADOG_CARDINALITY", "low"); + env.put("DATADOG_CARDINALITY", "low"); assertEquals( TagsCardinality.LOW, - new NonBlockingStatsDClientBuilder().resolve().tagsCardinality); + new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) + .resolve() + .tagsCardinality); // the other variable takes precedence - environmentVariables.set("DD_CARDINALITY", "high"); + env.put("DD_CARDINALITY", "high"); assertEquals( TagsCardinality.HIGH, - new NonBlockingStatsDClientBuilder().resolve().tagsCardinality); + new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) + .resolve() + .tagsCardinality); // explicit user input takes precedence even if they request default assertEquals( TagsCardinality.DEFAULT, new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) .tagsCardinality(TagsCardinality.DEFAULT) .resolve() .tagsCardinality); diff --git a/src/test/java/com/timgroup/statsd/NonBlockingStatsDClientTest.java b/src/test/java/com/timgroup/statsd/NonBlockingStatsDClientTest.java index 8ed7a030..9a299ced 100644 --- a/src/test/java/com/timgroup/statsd/NonBlockingStatsDClientTest.java +++ b/src/test/java/com/timgroup/statsd/NonBlockingStatsDClientTest.java @@ -16,8 +16,10 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Random; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; @@ -53,6 +55,11 @@ public class NonBlockingStatsDClientTest { private String tagsCardinality; private String payloadTail; + // Controlled environment injected into every client built by this test, so the client only + // sees the variables a test explicitly sets and is unaffected by the ambient process + // environment (e.g. DD_ENV/DD_SERVICE/DD_ENTITY_ID set by a CI runner). + private Map clientEnv; + @Parameters public static Object[][] parameters() { return TestHelpers.permutations( @@ -94,16 +101,18 @@ public NonBlockingStatsDClientTest( @Before public void start() throws IOException { + clientEnv = new HashMap<>(); if (externalEnv != null) { - environmentVariables.set("DD_EXTERNAL_ENV", externalEnv); + clientEnv.put("DD_EXTERNAL_ENV", externalEnv); } if (tagsCardinality != null) { - environmentVariables.set("DD_CARDINALITY", tagsCardinality); + clientEnv.put("DD_CARDINALITY", tagsCardinality); } server = new UDPDummyStatsDServer(0); client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("my.prefix") .hostname("localhost") .port(server.getPort()) @@ -114,6 +123,7 @@ public void start() throws IOException { .build(); clientUnaggregated = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("my.prefix") .hostname("localhost") .port(server.getPort()) @@ -307,6 +317,7 @@ public void sends_gauge_without_truncation() throws Exception { final RecordingErrorHandler errorHandler = new RecordingErrorHandler(); final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("") .hostname("localhost") .port(server.getPort()) @@ -614,6 +625,7 @@ public void sends_gauge_mixed_tags() throws Exception { final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("my.prefix") .hostname("localhost") .port(server.getPort()) @@ -638,6 +650,7 @@ public void sends_gauge_mixed_tags_with_sample_rate() throws Exception { final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("my.prefix") .hostname("localhost") .port(server.getPort()) @@ -662,6 +675,7 @@ public void sends_gauge_constant_tags_only() throws Exception { final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("my.prefix") .hostname("localhost") .port(server.getPort()) @@ -684,9 +698,10 @@ public void sends_gauge_constant_tags_only() throws Exception { @Test(timeout = 5000L) public void sends_gauge_entityID_from_env() throws Exception { final String entity_value = "foo-entity"; - environmentVariables.set(NonBlockingStatsDClient.DD_ENTITY_ID_ENV_VAR, entity_value); + clientEnv.put(NonBlockingStatsDClient.DD_ENTITY_ID_ENV_VAR, entity_value); final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("my.prefix") .hostname("localhost") .port(server.getPort()) @@ -708,10 +723,11 @@ public void sends_gauge_entityID_from_env() throws Exception { @Test(timeout = 5000L) public void sends_gauge_entityID_from_env_and_constant_tags() throws Exception { final String entity_value = "foo-entity"; - environmentVariables.set(NonBlockingStatsDClient.DD_ENTITY_ID_ENV_VAR, entity_value); + clientEnv.put(NonBlockingStatsDClient.DD_ENTITY_ID_ENV_VAR, entity_value); final String constantTags = "arbitraryTag:arbitraryValue"; final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("my.prefix") .hostname("localhost") .port(server.getPort()) @@ -733,9 +749,10 @@ public void sends_gauge_entityID_from_env_and_constant_tags() throws Exception { @Test(timeout = 5000L) public void sends_gauge_entityID_from_args() throws Exception { final String entity_value = "foo-entity"; - environmentVariables.set(NonBlockingStatsDClient.DD_ENTITY_ID_ENV_VAR, entity_value); + clientEnv.put(NonBlockingStatsDClient.DD_ENTITY_ID_ENV_VAR, entity_value); final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("my.prefix") .hostname("localhost") .port(server.getPort()) @@ -758,12 +775,13 @@ public void sends_gauge_entityID_from_args() throws Exception { @Test(timeout = 5000L) public void init_client_from_env_vars() throws Exception { final String entity_value = "foo-entity"; - environmentVariables.set( + clientEnv.put( NonBlockingStatsDClient.DD_DOGSTATSD_PORT_ENV_VAR, Integer.toString(server.getPort())); - environmentVariables.set(NonBlockingStatsDClient.DD_AGENT_HOST_ENV_VAR, "localhost"); + clientEnv.put(NonBlockingStatsDClient.DD_AGENT_HOST_ENV_VAR, "localhost"); final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("my.prefix") .enableAggregation(false) .originDetectionEnabled(originDetectionEnabled) @@ -786,9 +804,10 @@ public void checkEnvVars() throws Exception { NonBlockingStatsDClient.Literal.values()) { final String envVarName = literal.envName(); final String randomString = envVarName + "_val_" + r.nextDouble(); - environmentVariables.set(envVarName, randomString); + clientEnv.put(envVarName, randomString); final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("checkEnvVars") .hostname("localhost") .port(server.getPort()) @@ -808,7 +827,7 @@ public void checkEnvVars() throws Exception { + randomString); server.clear(); - environmentVariables.clear(envVarName); + clientEnv.remove(envVarName); log.info("passed for '" + literal + "'; env cleaned."); client.stop(); } @@ -819,6 +838,7 @@ public void sends_gauge_empty_prefix() throws Exception { final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("") .hostname("localhost") .port(server.getPort()) @@ -841,6 +861,7 @@ public void sends_gauge_null_prefix() throws Exception { final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix(null) .hostname("localhost") .port(server.getPort()) @@ -863,6 +884,7 @@ public void sends_gauge_no_prefix() throws Exception { final NonBlockingStatsDClient no_prefix_client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .hostname("localhost") .port(server.getPort()) .enableAggregation(false) @@ -963,6 +985,7 @@ public void sends_event_empty_prefix() throws Exception { final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("") .hostname("localhost") .port(server.getPort()) @@ -1068,6 +1091,7 @@ public void sends_too_large_message() throws Exception { try (final NonBlockingStatsDClient testClient = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("my.prefix") .hostname("localhost") .port(server.getPort()) @@ -1115,6 +1139,7 @@ public void sends_telemetry_elsewhere() throws Exception { final UDPDummyStatsDServer telemetryServer = new UDPDummyStatsDServer(0); final NonBlockingStatsDClient testClient = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("my.prefix") .hostname("localhost") .port(server.getPort()) @@ -1247,6 +1272,7 @@ public NonsamplingClient build() throws StatsDClientException { public void nonsampling_client_test() throws Exception { final NonBlockingStatsDClientBuilder builder = new NonsamplingClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("") .hostname("localhost") .enableAggregation(false) @@ -1308,7 +1334,8 @@ public int write(ByteBuffer data) throws IOException { } }; NonBlockingStatsDClient client = - builder.hostname("localhost") + builder.withEnvironmentVariables(clientEnv) + .hostname("localhost") .port(port) .blocking(true) .originDetectionEnabled(originDetectionEnabled) @@ -1355,7 +1382,8 @@ public int write(ByteBuffer data) throws IOException { final BlockingQueue errors = new LinkedBlockingQueue(); NonBlockingStatsDClient client = - builder.hostname("localhost") + builder.withEnvironmentVariables(clientEnv) + .hostname("localhost") .blocking(true) .errorHandler( new StatsDClientErrorHandler() { diff --git a/src/test/java/com/timgroup/statsd/TelemetryTest.java b/src/test/java/com/timgroup/statsd/TelemetryTest.java index 80ef04f4..783fe4ca 100644 --- a/src/test/java/com/timgroup/statsd/TelemetryTest.java +++ b/src/test/java/com/timgroup/statsd/TelemetryTest.java @@ -6,15 +6,15 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.logging.Logger; import org.junit.After; import org.junit.Assume; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.junit.contrib.java.lang.system.EnvironmentVariables; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @@ -130,7 +130,8 @@ public static Object[][] parameters() { }); } - @Rule public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); + // Controlled environment so the client is unaffected by ambient DD_* variables. + private Map clientEnv; public TelemetryTest(String containerID, String tagsCardinality) { this.containerID = containerID; @@ -152,10 +153,12 @@ public void start() throws IOException, Exception { server = new UDPDummyStatsDServer(0); fakeProcessor = new FakeProcessor(LOGGING_HANDLER); - environmentVariables.set("DD_CARDINALITY", tagsCardinality); + clientEnv = new HashMap<>(); + clientEnv.put("DD_CARDINALITY", tagsCardinality); NonBlockingStatsDClientBuilder builder = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("my.prefix") .hostname("localhost") .constantTags("test") @@ -540,6 +543,7 @@ public void telemetry_droppedData() throws Exception { // fails to send any data on the network, producing packets dropped NonBlockingStatsDClient clientError = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(clientEnv) .prefix("my.prefix") .hostname("localhost") .constantTags("test") diff --git a/src/test/java/com/timgroup/statsd/UnixSocketTest.java b/src/test/java/com/timgroup/statsd/UnixSocketTest.java index ff4213b8..e9e3b247 100644 --- a/src/test/java/com/timgroup/statsd/UnixSocketTest.java +++ b/src/test/java/com/timgroup/statsd/UnixSocketTest.java @@ -10,6 +10,8 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; import java.util.logging.Logger; import org.junit.After; import org.junit.Assume; @@ -28,6 +30,8 @@ public class UnixSocketTest implements StatsDClientErrorHandler { private static Logger log = Logger.getLogger(StatsDClientErrorHandler.class.getName()); + static final Map env = new HashMap<>(); + public synchronized void handle(Exception exception) { log.info("Got exception: " + exception); lastException = exception; @@ -49,6 +53,7 @@ public void start() throws IOException { client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) .prefix("my.prefix") .hostname(socketFile.toString()) .port(0) @@ -63,6 +68,7 @@ public void start() throws IOException { clientAggregate = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) .prefix("my.prefix") .hostname(socketFile.toString()) .port(0) diff --git a/src/test/java/com/timgroup/statsd/UnixStreamSocketTest.java b/src/test/java/com/timgroup/statsd/UnixStreamSocketTest.java index d7e64e4d..4f88074b 100644 --- a/src/test/java/com/timgroup/statsd/UnixStreamSocketTest.java +++ b/src/test/java/com/timgroup/statsd/UnixStreamSocketTest.java @@ -11,6 +11,8 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; import java.util.logging.Logger; import org.junit.After; import org.junit.Assume; @@ -24,6 +26,7 @@ public class UnixStreamSocketTest implements StatsDClientErrorHandler { private static NonBlockingStatsDClient clientAggregate; private static DummyStatsDServer server; private static File socketFile; + private final Map env = new HashMap<>(); private volatile Exception lastException = new Exception(); @@ -50,6 +53,7 @@ public void start() throws IOException { client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) .prefix("my.prefix") .address("unixstream://" + socketFile.getPath()) .port(0) @@ -65,6 +69,7 @@ public void start() throws IOException { clientAggregate = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) .prefix("my.prefix") .address("unixstream://" + socketFile.getPath()) .port(0) @@ -186,6 +191,7 @@ public void resist_dsd_timeout() throws Exception { public void stream_uds_has_uds_buffer_size() throws Exception { final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) .prefix("my.prefix") .address("unixstream:///foo") .containerID("fake-container-id") @@ -200,6 +206,7 @@ public void stream_uds_has_uds_buffer_size() throws Exception { public void max_packet_size_override() throws Exception { final NonBlockingStatsDClient client = new NonBlockingStatsDClientBuilder() + .withEnvironmentVariables(env) .prefix("my.prefix") .address("unixstream:///foo") .containerID("fake-container-id") From 361d7980db8f11fd71275a18b876ee263547919c Mon Sep 17 00:00:00 2001 From: Vikentiy Fesunov Date: Thu, 23 Jul 2026 14:54:52 +0200 Subject: [PATCH 2/2] Fix javadoc --- .../com/timgroup/statsd/NonBlockingStatsDClientBuilder.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/timgroup/statsd/NonBlockingStatsDClientBuilder.java b/src/main/java/com/timgroup/statsd/NonBlockingStatsDClientBuilder.java index 89f30326..cfef457d 100644 --- a/src/main/java/com/timgroup/statsd/NonBlockingStatsDClientBuilder.java +++ b/src/main/java/com/timgroup/statsd/NonBlockingStatsDClientBuilder.java @@ -341,9 +341,9 @@ public NonBlockingStatsDClientBuilder tagsCardinality(TagsCardinality cardinalit * Overrides the environment variables the client reads configuration from. * *

By default the client reads from the process environment via {@link - * System#env.get(String)}. Supplying a map makes the client read configuration (e.g. {@code - * DD_ENV}, {@code DD_SERVICE}, {@code DD_VERSION}, {@code DD_ENTITY_ID}) exclusively from that - * map, so it is unaffected by the ambient process environment. + * System#getenv(String)}. Supplying a map makes the client read configuration (for example, + * {@code DD_ENV}, {@code DD_SERVICE}, {@code DD_VERSION}, {@code DD_ENTITY_ID}) exclusively + * from that map, so it is unaffected by the ambient process environment. * * @param env the map to read environment variables from, or null to use the process environment * @return this builder