Skip to content

Commit 61bbb70

Browse files
committed
Collect process tags for tracing
1 parent b00c24e commit 61bbb70

File tree

15 files changed

+339
-32
lines changed

15 files changed

+339
-32
lines changed

dd-java-agent/instrumentation/graal/native-image/src/main/java/datadog/trace/instrumentation/graal/nativeimage/NativeImageGeneratorRunnerInstrumentation.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public static void onEnter(@Advice.Argument(value = 0, readOnly = false) String[
7676
+ "datadog.trace.api.Platform:rerun,"
7777
+ "datadog.trace.api.Platform$Captured:build_time,"
7878
+ "datadog.trace.api.env.CapturedEnvironment:build_time,"
79+
+ "datadog.trace.api.env.CapturedEnvironment$ProcessInfo:build_time,"
7980
+ "datadog.trace.api.ConfigCollector:rerun,"
8081
+ "datadog.trace.api.ConfigDefaults:build_time,"
8182
+ "datadog.trace.api.ConfigOrigin:build_time,"

dd-java-agent/instrumentation/spring-boot/src/main/java/datadog/trace/instrumentation/springboot/SpringApplicationInstrumentation.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
import datadog.trace.agent.tooling.Instrumenter;
88
import datadog.trace.agent.tooling.InstrumenterModule;
99
import datadog.trace.api.Config;
10+
import datadog.trace.api.ProcessTags;
1011
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
12+
import java.util.Arrays;
1113
import net.bytebuddy.asm.Advice;
1214
import org.springframework.core.env.ConfigurableEnvironment;
1315

@@ -60,6 +62,17 @@ public static void afterEnvironmentPostProcessed(
6062
final String applicationName = environment.getProperty("spring.application.name");
6163
if (applicationName != null && !applicationName.isEmpty()) {
6264
AgentTracer.get().updatePreferredServiceName(applicationName);
65+
ProcessTags.addTag("springboot.application", applicationName);
66+
}
67+
final String[] profiles = environment.getActiveProfiles();
68+
System.err.println(Arrays.toString(profiles));
69+
if (profiles != null && profiles.length > 0) {
70+
ProcessTags.addTag("springboot.profile", profiles[0]);
71+
} else {
72+
final String[] defaultProfiles = environment.getDefaultProfiles();
73+
if (defaultProfiles != null && defaultProfiles.length > 0) {
74+
ProcessTags.addTag("springboot.profile", defaultProfiles[0]);
75+
}
6376
}
6477
}
6578
}

dd-java-agent/instrumentation/spring-boot/src/test/groovy/SpringBootApplicationTest.groovy

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import datadog.trace.api.ProcessTags
2+
13
import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace
24

35
import datadog.trace.agent.test.AgentTestRunner
@@ -10,6 +12,7 @@ class SpringBootApplicationTest extends AgentTestRunner {
1012
protected void configurePreAgent() {
1113
super.configurePreAgent()
1214
}
15+
1316
static class BeanWhoTraces implements InitializingBean {
1417

1518
@Override
@@ -32,15 +35,19 @@ class SpringBootApplicationTest extends AgentTestRunner {
3235
}
3336
}
3437
})
38+
and:
39+
def processTags = ProcessTags.getTagsForSerialization()
40+
assert processTags.toString() =~ ".+,springboot.application:$expectedService,springboot.profile:$expectedProfile"
3541
context != null
3642
cleanup:
3743
context?.stop()
3844
where:
39-
expectedService | args
40-
"application-name-from-args" | new String[]{
41-
"--spring.application.name=application-name-from-args"
45+
expectedService | expectedProfile | args
46+
"application-name-from-args" | "prod" | new String[]{
47+
"--spring.application.name=application-name-from-args",
48+
"--spring.profiles.active=prod,common",
4249
}
43-
"application-name-from-properties" | new String[0] // will load from properties
50+
"application-name-from-properties" | "default" | new String[0] // will load from properties
4451
}
4552
}
4653

dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/AgentTestRunner.groovy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import datadog.trace.agent.tooling.bytebuddy.matcher.GlobalIgnores
2727
import datadog.trace.api.Config
2828
import datadog.trace.api.DDSpanId
2929
import datadog.trace.api.IdGenerationStrategy
30+
import datadog.trace.api.ProcessTags
3031
import datadog.trace.api.StatsDClient
3132
import datadog.trace.api.TraceConfig
3233
import datadog.trace.api.WellKnownTags
@@ -509,6 +510,7 @@ abstract class AgentTestRunner extends DDSpecification implements AgentBuilder.L
509510
ActiveSubsystems.APPSEC_ACTIVE = true
510511
}
511512
InstrumentationErrors.resetErrorCount()
513+
ProcessTags.reset()
512514
}
513515

514516
@Override

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,8 @@ public final class ConfigDefaults {
241241
static final int DEFAULT_TELEMETRY_DEPENDENCY_RESOLUTION_QUEUE_SIZE = 100000;
242242

243243
static final Set<String> DEFAULT_TRACE_EXPERIMENTAL_FEATURES_ENABLED =
244-
new HashSet<>(asList("DD_TAGS", "DD_LOGS_INJECTION"));
244+
new HashSet<>(
245+
asList("DD_TAGS", "DD_LOGS_INJECTION", "EXPERIMENTAL_COLLECT_PROCESS_TAGS_ENABLED"));
245246

246247
static final boolean DEFAULT_TRACE_128_BIT_TRACEID_GENERATION_ENABLED = true;
247248
static final boolean DEFAULT_TRACE_128_BIT_TRACEID_LOGGING_ENABLED = true;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,5 @@ public class DDTags {
9898
public static final String DECISION_MAKER_INHERITED = "_dd.dm.inherited";
9999
public static final String DECISION_MAKER_SERVICE = "_dd.dm.service";
100100
public static final String DECISION_MAKER_RESOURCE = "_dd.dm.resource";
101+
public static final String PROCESS_TAGS = "_dd.process";
101102
}

dd-trace-api/src/main/java/datadog/trace/api/config/GeneralConfig.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ public final class GeneralConfig {
2929
@Deprecated // Use dd.tags instead
3030
public static final String GLOBAL_TAGS = "trace.global.tags";
3131

32+
public static final String EXPERIMENTAL_COLLECT_PROCESS_TAGS_ENABLED =
33+
"experimental.collect.tags.enabled";
34+
3235
public static final String LOG_LEVEL = "log.level";
3336
public static final String TRACE_DEBUG = "trace.debug";
3437
public static final String TRACE_TRIAGE = "trace.triage";
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package datadog.trace.core.tagprocessor;
2+
3+
import static datadog.trace.api.DDTags.PROCESS_TAGS;
4+
5+
import datadog.trace.api.ProcessTags;
6+
import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink;
7+
import datadog.trace.core.DDSpanContext;
8+
import java.util.List;
9+
import java.util.Map;
10+
11+
public class ProcessTagsAdder implements TagsPostProcessor {
12+
@Override
13+
public Map<String, Object> processTags(
14+
Map<String, Object> unsafeTags, DDSpanContext spanContext, List<AgentSpanLink> spanLinks) {
15+
final CharSequence processTags = ProcessTags.getTagsForSerialization();
16+
if (processTags != null) {
17+
unsafeTags.put(PROCESS_TAGS, processTags);
18+
}
19+
return unsafeTags;
20+
}
21+
}

dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/TagsPostProcessorFactory.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public final class TagsPostProcessorFactory {
1010

1111
private static class Lazy {
1212
private static TagsPostProcessor create() {
13-
final List<TagsPostProcessor> processors = new ArrayList<>(4);
13+
final List<TagsPostProcessor> processors = new ArrayList<>(7);
1414
processors.add(new PeerServiceCalculator());
1515
if (addBaseService) {
1616
processors.add(new BaseServiceAdder(Config.get().getServiceName()));
@@ -32,6 +32,9 @@ private static TagsPostProcessor create() {
3232
if (Config.get().isAddSpanPointers("aws")) {
3333
processors.add(new SpanPointersProcessor());
3434
}
35+
if (Config.get().isExperimentalCollectProcessTagsEnabled()) {
36+
processors.add(new ProcessTagsAdder());
37+
}
3538
return new PostProcessorChain(
3639
processors.toArray(processors.toArray(new TagsPostProcessor[0])));
3740
}
@@ -52,6 +55,7 @@ public static void withAddBaseService(boolean enabled) {
5255
addBaseService = enabled;
5356
Lazy.instance = Lazy.create();
5457
}
58+
5559
/**
5660
* Mostly used for test purposes.
5761
*
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package datadog.trace.core.tagprocessor
2+
3+
import datadog.trace.api.ProcessTags
4+
import datadog.trace.core.DDSpanContext
5+
import datadog.trace.test.util.DDSpecification
6+
7+
class ProcessTagsAdderTest extends DDSpecification {
8+
def "should add _dd.process"() {
9+
setup:
10+
def adder = new ProcessTagsAdder()
11+
def spanContext = Mock(DDSpanContext)
12+
13+
when:
14+
def enrichedTags = adder.processTags([:], spanContext, [])
15+
16+
then:
17+
def firstValue = enrichedTags.get("_dd.process")
18+
assert !firstValue.toString().isEmpty()
19+
20+
when:
21+
ProcessTags.addTag("test", "value")
22+
enrichedTags = adder.processTags([:], spanContext, [])
23+
24+
then:
25+
assert enrichedTags.get("_dd.process").toString() == "$firstValue,test:value"
26+
}
27+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ public static String getHostName() {
150150
private final boolean peerServiceDefaultsEnabled;
151151
private final Map<String, String> peerServiceComponentOverrides;
152152
private final boolean removeIntegrationServiceNamesEnabled;
153+
private final boolean experimentalCollectProcessTagsEnabled;
153154
private final Map<String, String> peerServiceMapping;
154155
private final Map<String, String> serviceMapping;
155156
private final Map<String, String> tags;
@@ -846,6 +847,8 @@ private Config(final ConfigProvider configProvider, final InstrumenterConfig ins
846847
// feature flag to remove fake services in v0
847848
removeIntegrationServiceNamesEnabled =
848849
configProvider.getBoolean(TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED, false);
850+
experimentalCollectProcessTagsEnabled =
851+
configProvider.getBoolean(EXPERIMENTAL_COLLECT_PROCESS_TAGS_ENABLED, false);
849852

850853
peerServiceMapping = configProvider.getMergedMap(TRACE_PEER_SERVICE_MAPPING);
851854

@@ -2091,6 +2094,10 @@ public Set<String> getExperimentalFeaturesEnabled() {
20912094
return experimentalFeaturesEnabled;
20922095
}
20932096

2097+
public boolean isExperimentalCollectProcessTagsEnabled() {
2098+
return experimentalCollectProcessTagsEnabled;
2099+
}
2100+
20942101
public boolean isTraceEnabled() {
20952102
return instrumenterConfig.isTraceEnabled();
20962103
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package datadog.trace.api;
2+
3+
import datadog.trace.api.env.CapturedEnvironment;
4+
import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
5+
import datadog.trace.util.TraceUtils;
6+
import java.nio.file.Path;
7+
import java.nio.file.Paths;
8+
import java.util.LinkedHashMap;
9+
import java.util.Map;
10+
import java.util.stream.Collectors;
11+
import org.slf4j.Logger;
12+
import org.slf4j.LoggerFactory;
13+
14+
public class ProcessTags {
15+
private static final Logger LOGGER = LoggerFactory.getLogger(ProcessTags.class);
16+
17+
private static class Lazy {
18+
static final Map<String, String> TAGS = loadTags();
19+
static volatile UTF8BytesString serializedForm;
20+
21+
private static Map<String, String> loadTags() {
22+
Map<String, String> tags = new LinkedHashMap<>();
23+
try {
24+
fillBaseTags(tags);
25+
fillJbossTags(tags);
26+
} catch (Throwable t) {
27+
LOGGER.debug("Unable to calculate default process tags", t);
28+
}
29+
return tags;
30+
}
31+
32+
private static void insertSysPropIfPresent(
33+
Map<String, String> tags, String propKey, String tagKey) {
34+
String value = System.getProperty(propKey);
35+
if (value != null) {
36+
tags.put(tagKey, value);
37+
}
38+
}
39+
40+
private static boolean insertLastPathSegmentIfPresent(
41+
Map<String, String> tags, String path, String tagKey) {
42+
if (path == null || path.isEmpty()) {
43+
return false;
44+
}
45+
try {
46+
final Path p = Paths.get(path).getFileName();
47+
if (p != null) {
48+
tags.put(tagKey, p.toString());
49+
return true;
50+
}
51+
} catch (Throwable ignored) {
52+
}
53+
return false;
54+
}
55+
56+
private static void fillBaseTags(Map<String, String> tags) {
57+
final CapturedEnvironment.ProcessInfo processInfo =
58+
CapturedEnvironment.get().getProcessInfo();
59+
if (processInfo.mainClass != null) {
60+
tags.put("entrypoint.name", processInfo.mainClass);
61+
}
62+
if (processInfo.jarFile != null) {
63+
final String jarName = processInfo.jarFile.getName();
64+
tags.put("entrypoint.name", jarName.substring(0, jarName.length() - 4)); // strip .jar
65+
insertLastPathSegmentIfPresent(tags, processInfo.jarFile.getParent(), "entrypoint.basedir");
66+
}
67+
68+
insertLastPathSegmentIfPresent(tags, System.getProperty("user.dir"), "entrypoint.workdir");
69+
}
70+
71+
private static void fillJbossTags(Map<String, String> tags) {
72+
if (insertLastPathSegmentIfPresent(
73+
tags, System.getProperty("jboss.home.dir"), "jboss.home")) {
74+
insertSysPropIfPresent(tags, "jboss.server.name", "server.name");
75+
tags.put(
76+
"jboss.mode",
77+
System.getProperties().containsKey("[Standalone]") ? "standalone" : "domain");
78+
}
79+
}
80+
81+
static synchronized UTF8BytesString calculateSerializedForm() {
82+
if (serializedForm == null && !TAGS.isEmpty()) {
83+
serializedForm =
84+
UTF8BytesString.create(
85+
TAGS.entrySet().stream()
86+
.map(entry -> entry.getKey() + ":" + TraceUtils.normalizeTag(entry.getValue()))
87+
.collect(Collectors.joining(",")));
88+
}
89+
return serializedForm;
90+
}
91+
}
92+
93+
private ProcessTags() {}
94+
95+
// need to be synchronized on writing. As optimization, it does not need to be sync on read.
96+
public static synchronized void addTag(String key, String value) {
97+
Lazy.TAGS.put(key, value);
98+
Lazy.serializedForm = null;
99+
}
100+
101+
public static UTF8BytesString getTagsForSerialization() {
102+
final UTF8BytesString serializedForm = Lazy.serializedForm;
103+
if (serializedForm != null) {
104+
return serializedForm;
105+
}
106+
return Lazy.calculateSerializedForm();
107+
}
108+
109+
/** Visible for testing. */
110+
static void empty() {
111+
Lazy.TAGS.clear();
112+
Lazy.serializedForm = null;
113+
}
114+
115+
/** Visible for testing. */
116+
static void reset() {
117+
empty();
118+
Lazy.TAGS.putAll(Lazy.loadTags());
119+
}
120+
}

0 commit comments

Comments
 (0)