Skip to content

Add process tags as list to remote config payload #8705

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 29, 2025
Merged
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
39 changes: 30 additions & 9 deletions internal-api/src/main/java/datadog/trace/api/ProcessTags.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
import datadog.trace.util.TraceUtils;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -18,6 +21,7 @@ public class ProcessTags {
private static class Lazy {
static final Map<String, String> TAGS = loadTags();
static volatile UTF8BytesString serializedForm;
static volatile List<String> listForm;

private static Map<String, String> loadTags() {
Map<String, String> tags = new LinkedHashMap<>();
Expand Down Expand Up @@ -81,15 +85,17 @@ private static void fillJbossTags(Map<String, String> tags) {
}
}

static synchronized UTF8BytesString calculateSerializedForm() {
if (serializedForm == null && !TAGS.isEmpty()) {
serializedForm =
UTF8BytesString.create(
TAGS.entrySet().stream()
.map(entry -> entry.getKey() + ":" + TraceUtils.normalizeTag(entry.getValue()))
.collect(Collectors.joining(",")));
static void calculate() {
if (listForm != null || TAGS.isEmpty()) {
return;
}
synchronized (Lazy.TAGS) {
final Stream<String> tagStream =
TAGS.entrySet().stream()
.map(entry -> entry.getKey() + ":" + TraceUtils.normalizeTag(entry.getValue()));
listForm = Collections.unmodifiableList(tagStream.collect(Collectors.toList()));
serializedForm = UTF8BytesString.create(String.join(",", listForm));
}
return serializedForm;
}
}

Expand All @@ -100,7 +106,20 @@ public static synchronized void addTag(String key, String value) {
if (enabled) {
Lazy.TAGS.put(key, value);
Lazy.serializedForm = null;
Lazy.listForm = null;
}
}

public static List<String> getTagsAsList() {
if (!enabled) {
return null;
}
final List<String> listForm = Lazy.listForm;
if (listForm != null) {
return listForm;
}
Lazy.calculate();
return Lazy.listForm;
}

public static UTF8BytesString getTagsForSerialization() {
Expand All @@ -111,13 +130,15 @@ public static UTF8BytesString getTagsForSerialization() {
if (serializedForm != null) {
return serializedForm;
}
return Lazy.calculateSerializedForm();
Lazy.calculate();
return Lazy.serializedForm;
}

/** Visible for testing. */
static void empty() {
Lazy.TAGS.clear();
Lazy.serializedForm = null;
Lazy.listForm = null;
}

/** Visible for testing. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class ProcessTagsForkedTest extends DDSpecification {
ProcessTags.addTag("test", "value")
then:
assert ProcessTags.tagsForSerialization == null
assert ProcessTags.tagsAsList == null
}

def 'should lazily recalculate when a tag is added'() {
Expand All @@ -80,12 +81,18 @@ class ProcessTagsForkedTest extends DDSpecification {
ProcessTags.reset()
when:
def processTags = ProcessTags.tagsForSerialization
def tagsAsList = ProcessTags.tagsAsList
then:
assert ProcessTags.enabled
assert processTags != null
assert tagsAsList != null
assert tagsAsList.size() > 0
when:
ProcessTags.addTag("test", "value")
then:
assert ProcessTags.tagsForSerialization.toString() == "$processTags,test:value"
def size = ProcessTags.tagsAsList.size()
assert size == tagsAsList.size() + 1
assert ProcessTags.tagsAsList[size - 1] == "test:value"
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package datadog.remoteconfig.tuf;

import com.squareup.moshi.Json;
import datadog.trace.api.ProcessTags;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
Expand All @@ -26,7 +27,14 @@ public static RemoteConfigRequest newRequest(

ClientInfo.TracerInfo tracerInfo =
new RemoteConfigRequest.ClientInfo.TracerInfo(
runtimeId, tracerVersion, serviceName, extraServices, serviceEnv, serviceVersion, tags);
runtimeId,
tracerVersion,
serviceName,
extraServices,
serviceEnv,
serviceVersion,
tags,
ProcessTags.getTagsAsList());

ClientInfo clientInfo =
new RemoteConfigRequest.ClientInfo(
Expand Down Expand Up @@ -174,21 +182,26 @@ public static class TracerInfo {
@Json(name = "app_version")
private final String serviceVersion;

@Json(name = "process_tags")
private final List<String> processTags;

public TracerInfo(
String runtimeId,
String tracerVersion,
String serviceName,
List<String> extraServices,
String serviceEnv,
String serviceVersion,
List<String> tags) {
List<String> tags,
List<String> processTags) {
this.runtimeId = runtimeId;
this.tracerVersion = tracerVersion;
this.serviceName = serviceName;
this.extraServices = extraServices;
this.serviceEnv = serviceEnv;
this.serviceVersion = serviceVersion;
this.tags = tags;
this.processTags = processTags;
}

public String getServiceName() {
Expand All @@ -210,6 +223,10 @@ public String getServiceVersion() {
public List<String> getTags() {
return tags;
}

public List<String> getProcessTags() {
return processTags;
}
}

private static class AgentInfo {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package datadog.remoteconfig

import com.squareup.moshi.Moshi
import datadog.remoteconfig.tuf.RemoteConfigRequest
import datadog.trace.api.ProcessTags
import datadog.trace.bootstrap.instrumentation.api.Tags
import datadog.trace.test.util.DDSpecification
import datadog.trace.api.Config
import datadog.trace.api.remoteconfig.ServiceNameCollector

import static datadog.trace.api.config.GeneralConfig.EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED

class PollerRequestFactoryTest extends DDSpecification {

static final String TRACER_VERSION = "v1.2.3"
Expand Down Expand Up @@ -51,4 +55,32 @@ class PollerRequestFactoryTest extends DDSpecification {
then:
request.client.tracerInfo.extraServices.contains(extraService)
}

void 'remote config provides process tags when enabled = #enabled'() {
setup:
if (enabled) {
injectSysConfig(EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED, "true")
}
ProcessTags.reset()
PollerRequestFactory factory = new PollerRequestFactory(Config.get(), TRACER_VERSION, CONTAINER_ID, ENTITY_ID, INVALID_REMOTE_CONFIG_URL, null)

when:
def request = factory.buildRemoteConfigRequest( Collections.singletonList("ASM"), null, null, 0, ServiceNameCollector.get())
def json = new Moshi.Builder().build().adapter(RemoteConfigRequest).toJson(request)
then:
def epName = request.client.tracerInfo.processTags.find {it =~ "entrypoint.name:.+"}
def workingDir = request.client.tracerInfo.processTags.find {it =~ "entrypoint.workdir:.+"}

if (enabled) {
assert workingDir != null
assert epName != null
assert json.contains('"process_tags":[')
} else {
assert workingDir == null
assert epName == null
assert !json.contains('"process_tags":[')
}
where:
enabled << [true, false]
}
}