Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,9 @@ public static void shutdown(final boolean sync) {
if (flareEnabled) {
stopFlarePoller();
}
if (featureFlaggingEnabled) {
shutdownFeatureFlagging(AGENT_CLASSLOADER);
}

if (agentlessLogSubmissionEnabled) {
shutdownLogsIntake();
Expand Down Expand Up @@ -1179,6 +1182,20 @@ private static void maybeStartFeatureFlagging(final Class<?> scoClass, final Obj
}
}

static void shutdownFeatureFlagging(final ClassLoader agentClassLoader) {
if (agentClassLoader == null) {
return;
}
try {
final Class<?> ffSysClass =
agentClassLoader.loadClass("com.datadog.featureflag.FeatureFlaggingSystem");
final Method stopMethod = ffSysClass.getMethod("stop");
stopMethod.invoke(null);
} catch (final Throwable e) {
log.warn("Unable to stop Feature Flagging subsystem", e);
}
}

private static void maybeInstallLogsIntake(Class<?> scoClass, Object sco) {
if (agentlessLogSubmissionEnabled || appLogsCollectionEnabled) {
StaticEventLogger.begin("Logs Intake");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package datadog.trace.bootstrap;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class AgentFeatureFlaggingLifecycleTest {

@BeforeEach
void reset() {
FakeFeatureFlaggingSystem.stopCalls.set(0);
}

@Test
void shutdownInvokesFeatureFlaggingSystemStopThroughAgentClassLoader() {
final ClassLoader classLoader =
new ClassLoader(null) {
@Override
public Class<?> loadClass(final String name) throws ClassNotFoundException {
if ("com.datadog.featureflag.FeatureFlaggingSystem".equals(name)) {
return FakeFeatureFlaggingSystem.class;
}
return super.loadClass(name);
}
};

Agent.shutdownFeatureFlagging(classLoader);

assertEquals(1, FakeFeatureFlaggingSystem.stopCalls.get());
}

@Test
void shutdownIsNoopBeforeAgentClassLoaderExists() {
Agent.shutdownFeatureFlagging(null);

assertEquals(0, FakeFeatureFlaggingSystem.stopCalls.get());
}

public static final class FakeFeatureFlaggingSystem {
private static final AtomicInteger stopCalls = new AtomicInteger();

public static void stop() {
stopCalls.incrementAndGet();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class OpenFeatureProviderSmokeTest extends AbstractServerSmokeTest {
command.addAll(['-jar', springBootShadowJar, "--server.port=${httpPort}".toString()])
final builder = new ProcessBuilder(command).directory(new File(buildDirectory))
builder.environment().put('DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED', 'true')
builder.environment().put('DD_FEATURE_FLAGS_CONFIGURATION_SOURCE', 'remote_config')
return builder
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ public final class ConfigDefaults {

static final boolean DEFAULT_INJECT_DATADOG_ATTRIBUTE = true;
static final String DEFAULT_SITE = "datadoghq.com";
static final String DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE = "agentless";
static final int DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_POLL_INTERVAL_SECONDS = 30;
static final int DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_REQUEST_TIMEOUT_SECONDS = 2;

static final boolean DEFAULT_CODE_ORIGIN_FOR_SPANS_INTERFACE_SUPPORT = false;
static final int DEFAULT_CODE_ORIGIN_MAX_USER_FRAMES = 8;
Expand Down
1 change: 1 addition & 0 deletions internal-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ dependencies {
api(project(":components:context"))
api(project(":components:environment"))
api(project(":components:json"))
api(project(":products:feature-flagging:feature-flagging-config"))
api(project(":utils:config-utils"))
api(project(":utils:time-utils"))

Expand Down
78 changes: 78 additions & 0 deletions internal-api/src/main/java/datadog/trace/api/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@
import static datadog.trace.api.ConfigDefaults.DEFAULT_ELASTICSEARCH_BODY_ENABLED;
import static datadog.trace.api.ConfigDefaults.DEFAULT_ELASTICSEARCH_PARAMS_ENABLED;
import static datadog.trace.api.ConfigDefaults.DEFAULT_EXPERIMENTATAL_JEE_SPLIT_BY_DEPLOYMENT;
import static datadog.trace.api.ConfigDefaults.DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE;
import static datadog.trace.api.ConfigDefaults.DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_POLL_INTERVAL_SECONDS;
import static datadog.trace.api.ConfigDefaults.DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_REQUEST_TIMEOUT_SECONDS;
import static datadog.trace.api.ConfigDefaults.DEFAULT_GRPC_CLIENT_ERROR_STATUSES;
import static datadog.trace.api.ConfigDefaults.DEFAULT_GRPC_SERVER_ERROR_STATUSES;
import static datadog.trace.api.ConfigDefaults.DEFAULT_HEALTH_METRICS_ENABLED;
Expand Down Expand Up @@ -723,6 +726,10 @@
import static datadog.trace.api.config.TracerConfig.WRITER_BAGGAGE_INJECT;
import static datadog.trace.api.config.TracerConfig.WRITER_LINKS_INJECT;
import static datadog.trace.api.config.TracerConfig.WRITER_TYPE;
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE;
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL;
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS;
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS;
import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY;
import static datadog.trace.bootstrap.instrumentation.api.WriterConstants.OTLP_WRITER_TYPE;
import static datadog.trace.util.CollectionUtils.tryMakeImmutableList;
Expand Down Expand Up @@ -1214,6 +1221,11 @@ public static String getHostName() {

private final int remoteConfigMaxExtraServices;

private final String featureFlaggingConfigurationSource;
private final String featureFlaggingConfigurationSourceAgentlessBaseUrl;
private final int featureFlaggingConfigurationSourcePollIntervalSeconds;
private final int featureFlaggingConfigurationSourceRequestTimeoutSeconds;

private final boolean dbmInjectSqlBaseHash;
private final String dbmPropagationMode;
private final boolean dbmTracePreparedStatements;
Expand Down Expand Up @@ -2839,6 +2851,40 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment())
configProvider.getInteger(
REMOTE_CONFIG_MAX_EXTRA_SERVICES, DEFAULT_REMOTE_CONFIG_MAX_EXTRA_SERVICES);

featureFlaggingConfigurationSource =
normalizeFeatureFlaggingConfigurationSource(
configProvider.getString(
FEATURE_FLAGS_CONFIGURATION_SOURCE, DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE));
featureFlaggingConfigurationSourceAgentlessBaseUrl =
configProvider.getStringNotEmpty(
FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL, null);
int configuredFeatureFlaggingPollIntervalSeconds =
configProvider.getInteger(
FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS,
DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_POLL_INTERVAL_SECONDS);
if (configuredFeatureFlaggingPollIntervalSeconds <= 0) {
log.warn(
"Invalid Feature Flagging agentless poll interval: {}. The value must be positive",
configuredFeatureFlaggingPollIntervalSeconds);
configuredFeatureFlaggingPollIntervalSeconds =
DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_POLL_INTERVAL_SECONDS;
}
featureFlaggingConfigurationSourcePollIntervalSeconds =
configuredFeatureFlaggingPollIntervalSeconds;
int configuredFeatureFlaggingRequestTimeoutSeconds =
configProvider.getInteger(
FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS,
DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_REQUEST_TIMEOUT_SECONDS);
if (configuredFeatureFlaggingRequestTimeoutSeconds <= 0) {
log.warn(
"Invalid Feature Flagging agentless request timeout: {}. The value must be positive",
configuredFeatureFlaggingRequestTimeoutSeconds);
configuredFeatureFlaggingRequestTimeoutSeconds =
DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_REQUEST_TIMEOUT_SECONDS;
}
featureFlaggingConfigurationSourceRequestTimeoutSeconds =
configuredFeatureFlaggingRequestTimeoutSeconds;

dynamicInstrumentationEnabled =
configProvider.getBoolean(
DYNAMIC_INSTRUMENTATION_ENABLED, DEFAULT_DYNAMIC_INSTRUMENTATION_ENABLED);
Expand Down Expand Up @@ -3751,6 +3797,14 @@ public boolean isInferredProxyPropagationEnabled() {
return traceInferredProxyEnabled;
}

private static String normalizeFeatureFlaggingConfigurationSource(final String source) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe worth adding a test to cover this normalization?

if (source == null) {
return DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE;
}
final String normalized = source.trim().toLowerCase(Locale.ROOT);
return normalized.isEmpty() ? DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE : normalized;
}

public boolean isBaggageExtract() {
return tracePropagationStylesToExtract.contains(TracePropagationStyle.BAGGAGE);
}
Expand Down Expand Up @@ -4668,6 +4722,22 @@ public int getRemoteConfigMaxExtraServices() {
return remoteConfigMaxExtraServices;
}

public String getFeatureFlaggingConfigurationSource() {
return featureFlaggingConfigurationSource;
}

public String getFeatureFlaggingConfigurationSourceAgentlessBaseUrl() {
return featureFlaggingConfigurationSourceAgentlessBaseUrl;
}

public int getFeatureFlaggingConfigurationSourcePollIntervalSeconds() {
return featureFlaggingConfigurationSourcePollIntervalSeconds;
}

public int getFeatureFlaggingConfigurationSourceRequestTimeoutSeconds() {
return featureFlaggingConfigurationSourceRequestTimeoutSeconds;
}

public boolean isDynamicInstrumentationEnabled() {
return dynamicInstrumentationEnabled;
}
Expand Down Expand Up @@ -6500,6 +6570,14 @@ public String toString() {
+ remoteConfigMaxPayloadSize
+ ", remoteConfigIntegrityCheckEnabled="
+ remoteConfigIntegrityCheckEnabled
+ ", featureFlaggingConfigurationSource="
+ featureFlaggingConfigurationSource
+ ", featureFlaggingConfigurationSourceAgentlessBaseUrl="
+ featureFlaggingConfigurationSourceAgentlessBaseUrl
+ ", featureFlaggingConfigurationSourcePollIntervalSeconds="
+ featureFlaggingConfigurationSourcePollIntervalSeconds
+ ", featureFlaggingConfigurationSourceRequestTimeoutSeconds="
+ featureFlaggingConfigurationSourceRequestTimeoutSeconds
+ ", debuggerEnabled="
+ dynamicInstrumentationEnabled
+ ", debuggerUploadTimeout="
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ public enum AgentThread {

LLMOBS_EVALS_PROCESSOR("dd-llmobs-evals-processor"),

FEATURE_FLAG_EXPOSURE_PROCESSOR("dd-ffe-exposure-processor");
FEATURE_FLAG_EXPOSURE_PROCESSOR("dd-ffe-exposure-processor"),
FEATURE_FLAG_CONFIGURATION_POLLER("dd-feature-flagging-http-poller");

public final String threadName;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package datadog.trace.api

import static datadog.trace.api.ConfigDefaults.DEFAULT_HTTP_CLIENT_ERROR_STATUSES
import static datadog.trace.api.ConfigDefaults.DEFAULT_HTTP_SERVER_ERROR_STATUSES
import static datadog.trace.api.ConfigDefaults.DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_POLL_INTERVAL_SECONDS
import static datadog.trace.api.ConfigDefaults.DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_REQUEST_TIMEOUT_SECONDS
import static datadog.trace.api.ConfigDefaults.DEFAULT_PARTIAL_FLUSH_MIN_SPANS
import static datadog.trace.api.ConfigDefaults.DEFAULT_SERVICE_NAME
import static datadog.trace.api.ConfigDefaults.DEFAULT_TRACE_LONG_RUNNING_FLUSH_INTERVAL
Expand Down Expand Up @@ -55,6 +57,9 @@ import static datadog.trace.api.config.GeneralConfig.TAGS
import static datadog.trace.api.config.GeneralConfig.TRACER_METRICS_IGNORED_RESOURCES
import static datadog.trace.api.config.GeneralConfig.TRACE_OTEL_SEMANTICS_ENABLED
import static datadog.trace.api.config.GeneralConfig.VERSION
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS
import static datadog.trace.api.featureflag.config.FeatureFlaggingConfig.FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS
import static datadog.trace.api.config.JmxFetchConfig.JMX_FETCH_CHECK_PERIOD
import static datadog.trace.api.config.JmxFetchConfig.JMX_FETCH_ENABLED
import static datadog.trace.api.config.JmxFetchConfig.JMX_FETCH_METRICS_CONFIGS
Expand Down Expand Up @@ -3478,4 +3483,53 @@ class ConfigTest extends DDSpecification {
"1" | true
"0" | false
}

def "agentless feature flag timing uses positive configured values"() {
setup:
Properties properties = new Properties()
properties.setProperty(FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS, "60")
properties.setProperty(FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS, "4")

when:
def config = new Config(ConfigProvider.withPropertiesOverride(properties))

then:
config.featureFlaggingConfigurationSourcePollIntervalSeconds == 60
config.featureFlaggingConfigurationSourceRequestTimeoutSeconds == 4
}

def "feature flag configuration source normalizes #value to #expected"() {
setup:
Properties properties = new Properties()
if (value != null) {
properties.setProperty(FEATURE_FLAGS_CONFIGURATION_SOURCE, value)
}

when:
def config = new Config(ConfigProvider.withPropertiesOverride(properties))

then:
config.featureFlaggingConfigurationSource == expected

where:
value | expected
null | "agentless"
"" | "agentless"
" " | "agentless"
" ReMoTe_ConFiG " | "remote_config"
}

def "agentless feature flag timing falls back for non-positive values"() {
setup:
Properties properties = new Properties()
properties.setProperty(FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS, "0")
properties.setProperty(FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS, "-1")

when:
def config = new Config(ConfigProvider.withPropertiesOverride(properties))

then:
config.featureFlaggingConfigurationSourcePollIntervalSeconds == DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_POLL_INTERVAL_SECONDS
config.featureFlaggingConfigurationSourceRequestTimeoutSeconds == DEFAULT_FEATURE_FLAGGING_CONFIGURATION_SOURCE_REQUEST_TIMEOUT_SECONDS
}
}
32 changes: 32 additions & 0 deletions metadata/supported-configurations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1497,6 +1497,38 @@
"aliases": []
}
],
"DD_FEATURE_FLAGS_CONFIGURATION_SOURCE": [
{
"version": "A",
"type": "string",
"default": "agentless",
"aliases": []
}
],
"DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_BASE_URL": [
{
"version": "A",
"type": "string",
"default": null,
"aliases": []
}
],
"DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_POLL_INTERVAL_SECONDS": [
{
"version": "A",
"type": "int",
"default": "30",
"aliases": []
}
],
"DD_FEATURE_FLAGS_CONFIGURATION_SOURCE_AGENTLESS_REQUEST_TIMEOUT_SECONDS": [
{
"version": "A",
"type": "int",
"default": "2",
"aliases": []
}
],
"DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED": [
{
"version": "A",
Expand Down
Loading
Loading