Skip to content

Commit d6b43cb

Browse files
authored
Adding Config Inversion Telemetry component (#9244)
* Adding ConfigInversion Telemetry component * adding test coverage * adding test coverage * update test name
1 parent 6c38611 commit d6b43cb

File tree

7 files changed

+180
-0
lines changed

7 files changed

+180
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
kotlin version: 2.0.21
2+
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
3+
1. Kotlin compile daemon is ready
4+
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package datadog.trace.api.telemetry;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collection;
5+
import java.util.Collections;
6+
import java.util.List;
7+
import java.util.concurrent.ArrayBlockingQueue;
8+
import java.util.concurrent.BlockingQueue;
9+
import org.slf4j.Logger;
10+
import org.slf4j.LoggerFactory;
11+
12+
public class ConfigInversionMetricCollector
13+
implements MetricCollector<ConfigInversionMetricCollector.ConfigInversionMetric> {
14+
private static final Logger log = LoggerFactory.getLogger(ConfigInversionMetricCollector.class);
15+
private static final String CONFIG_INVERSION_KEY_TAG = "config_name:";
16+
private static final String CONFIG_INVERSION_METRIC_NAME = "untracked.config.detected";
17+
private static final String NAMESPACE = "tracers";
18+
private static final ConfigInversionMetricCollector INSTANCE =
19+
new ConfigInversionMetricCollector();
20+
21+
private final BlockingQueue<ConfigInversionMetricCollector.ConfigInversionMetric> metricsQueue;
22+
23+
private ConfigInversionMetricCollector() {
24+
this.metricsQueue = new ArrayBlockingQueue<>(RAW_QUEUE_SIZE);
25+
}
26+
27+
public static ConfigInversionMetricCollector getInstance() {
28+
return INSTANCE;
29+
}
30+
31+
public void setUndocumentedEnvVarMetric(String configName) {
32+
setMetricConfigInversionMetric(CONFIG_INVERSION_KEY_TAG + configName);
33+
}
34+
35+
private void setMetricConfigInversionMetric(final String... tags) {
36+
if (!metricsQueue.offer(
37+
new ConfigInversionMetricCollector.ConfigInversionMetric(
38+
NAMESPACE, true, CONFIG_INVERSION_METRIC_NAME, "count", 1, tags))) {
39+
log.debug("Unable to add telemetry metric {} for {}", CONFIG_INVERSION_METRIC_NAME, tags[0]);
40+
}
41+
}
42+
43+
@Override
44+
public void prepareMetrics() {
45+
// Nothing to do here
46+
}
47+
48+
@Override
49+
public Collection<ConfigInversionMetricCollector.ConfigInversionMetric> drain() {
50+
if (this.metricsQueue.isEmpty()) {
51+
return Collections.emptyList();
52+
}
53+
List<ConfigInversionMetricCollector.ConfigInversionMetric> drained =
54+
new ArrayList<>(this.metricsQueue.size());
55+
this.metricsQueue.drainTo(drained);
56+
return drained;
57+
}
58+
59+
public static class ConfigInversionMetric extends MetricCollector.Metric {
60+
public ConfigInversionMetric(
61+
String namespace,
62+
boolean common,
63+
String metricName,
64+
String type,
65+
Number value,
66+
final String... tags) {
67+
super(namespace, common, metricName, type, value, tags);
68+
}
69+
}
70+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package datadog.trace.api.telemetry
2+
3+
import datadog.trace.test.util.DDSpecification
4+
5+
import static datadog.trace.api.telemetry.ConfigInversionMetricCollector.CONFIG_INVERSION_METRIC_NAME
6+
7+
class ConfigInversionMetricCollectorTest extends DDSpecification {
8+
9+
def "should emit metric when unsupported env var is used"() {
10+
setup:
11+
def collector = ConfigInversionMetricCollector.getInstance()
12+
13+
when:
14+
ConfigInversionMetricCollectorTestHelper.checkAndEmitUnsupported("DD_UNKNOWN_FEATURE")
15+
collector.prepareMetrics()
16+
def metrics = collector.drain()
17+
18+
then:
19+
metrics.size() == 1
20+
def metric = metrics[0]
21+
metric.type == 'count'
22+
metric.value == 1
23+
metric.namespace == 'tracers'
24+
metric.metricName == CONFIG_INVERSION_METRIC_NAME
25+
metric.tags.size() == 1
26+
metric.tags[0] == 'config_name:DD_UNKNOWN_FEATURE'
27+
}
28+
29+
def "should not emit metric when supported env var is used"() {
30+
setup:
31+
def collector = ConfigInversionMetricCollector.getInstance()
32+
33+
when:
34+
ConfigInversionMetricCollectorTestHelper.checkAndEmitUnsupported("DD_ENV")
35+
collector.prepareMetrics()
36+
def metrics = collector.drain()
37+
38+
then:
39+
metrics.isEmpty()
40+
}
41+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package datadog.trace.api.telemetry;
2+
3+
import java.util.Arrays;
4+
import java.util.HashSet;
5+
import java.util.Set;
6+
7+
// Lightweight helper class to simulate Config Inversion ConfigHelper scenario where telemetry
8+
// metrics are emitted for "unknown" environment variables.
9+
public class ConfigInversionMetricCollectorTestHelper {
10+
private static final Set<String> SUPPORTED_ENV_VARS =
11+
new HashSet<>(Arrays.asList("DD_ENV", "DD_SERVICE"));
12+
13+
private static final ConfigInversionMetricCollector configInversionMetricCollector =
14+
ConfigInversionMetricCollector.getInstance();
15+
16+
public static void checkAndEmitUnsupported(String envVarName) {
17+
if (!SUPPORTED_ENV_VARS.contains(envVarName)) {
18+
configInversionMetricCollector.setUndocumentedEnvVarMetric(envVarName);
19+
}
20+
}
21+
}

telemetry/src/main/java/datadog/telemetry/TelemetrySystem.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import datadog.telemetry.integration.IntegrationPeriodicAction;
1111
import datadog.telemetry.log.LogPeriodicAction;
1212
import datadog.telemetry.metric.CiVisibilityMetricPeriodicAction;
13+
import datadog.telemetry.metric.ConfigInversionMetricPeriodicAction;
1314
import datadog.telemetry.metric.CoreMetricsPeriodicAction;
1415
import datadog.telemetry.metric.IastMetricPeriodicAction;
1516
import datadog.telemetry.metric.OtelEnvMetricPeriodicAction;
@@ -51,6 +52,7 @@ static Thread createTelemetryRunnable(
5152
if (telemetryMetricsEnabled) {
5253
actions.add(new CoreMetricsPeriodicAction());
5354
actions.add(new OtelEnvMetricPeriodicAction());
55+
actions.add(new ConfigInversionMetricPeriodicAction());
5456
actions.add(new IntegrationPeriodicAction());
5557
actions.add(new WafMetricPeriodicAction());
5658
if (Verbosity.OFF != Config.get().getIastTelemetryVerbosity()) {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package datadog.telemetry.metric;
2+
3+
import datadog.trace.api.telemetry.ConfigInversionMetricCollector;
4+
import datadog.trace.api.telemetry.MetricCollector;
5+
import edu.umd.cs.findbugs.annotations.NonNull;
6+
7+
public class ConfigInversionMetricPeriodicAction extends MetricPeriodicAction {
8+
@Override
9+
@NonNull
10+
public MetricCollector collector() {
11+
return ConfigInversionMetricCollector.getInstance();
12+
}
13+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package datadog.telemetry.metric
2+
3+
import datadog.telemetry.TelemetryService
4+
import datadog.telemetry.api.Metric
5+
import spock.lang.Specification
6+
7+
class ConfigInversionMetricPeriodicActionTest extends Specification{
8+
9+
void 'test undocumented env var metric'() {
10+
setup:
11+
final telemetryService = Mock(TelemetryService)
12+
final action = new ConfigInversionMetricPeriodicAction()
13+
14+
when:
15+
action.collector().setUndocumentedEnvVarMetric("DD_ENV_VAR")
16+
action.collector().prepareMetrics()
17+
action.doIteration(telemetryService)
18+
19+
then:
20+
1 * telemetryService.addMetric({ Metric metric ->
21+
metric.namespace == 'tracers' &&
22+
metric.metric == 'untracked.config.detected' &&
23+
metric.points[0][1] == 1 &&
24+
metric.tags == ['config_name:DD_ENV_VAR'] &&
25+
metric.type == Metric.TypeEnum.COUNT
26+
})
27+
0 * _._
28+
}
29+
}

0 commit comments

Comments
 (0)