Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
70 changes: 37 additions & 33 deletions tracer/src/Datadog.Trace/ProcessTags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,52 +20,56 @@ internal static class ProcessTags
public const string EntrypointBasedir = "entrypoint.basedir";
public const string EntrypointWorkdir = "entrypoint.workdir";

public static readonly string SerializedTags = GetSerializedTags();
// two views on the same data
public static readonly List<string> TagsList = GetTagsList();
public static readonly string SerializedTags = GetSerializedTagsFromList(TagsList);

/// <summary>
/// From the full path of a directory, get the name of the leaf directory.
/// </summary>
private static string GetLastPathSegment(string directoryPath)
private static List<string> GetTagsList()
{
// Path.GetFileName returns an empty string if the path ends with a '/'.
// We could use Path.TrimEndingDirectorySeparator instead of the trim here, but it's not available on .NET Framework
return Path.GetFileName(directoryPath.TrimEnd('\\').TrimEnd('/'));
// ⚠️ make sure entries are added in alphabetical order of keys
List<string> tags = [];
tags.AddNormalizedTag(EntrypointBasedir, GetLastPathSegment(AppContext.BaseDirectory));
tags.AddNormalizedTag(EntrypointName, GetEntryPointName());
// workdir can be changed by the code, but we consider that capturing the value when this is called is good enough
tags.AddNormalizedTag(EntrypointWorkdir, GetLastPathSegment(Environment.CurrentDirectory));

return tags;
}

private static string GetSerializedTags()
/// <summary>
/// normalizes the tag value (keys are hardcoded so they don't need that)
/// and adds it to the list iff not null or empty
/// </summary>
private static void AddNormalizedTag(this List<string> tags, string key, string? value)
{
// ⚠️ make sure entries are added in alphabetical order of keys
List<KeyValuePair<string, string?>> tags =
[
new(EntrypointBasedir, GetLastPathSegment(AppContext.BaseDirectory)),
new(EntrypointName, GetEntryPointName()),
// workdir can be changed by the code, but we consider that capturing the value when this is called is good enough
new(EntrypointWorkdir, GetLastPathSegment(Environment.CurrentDirectory))
];

// then normalize values and put all tags in a string
var serializedTags = StringBuilderCache.Acquire();
foreach (var kvp in tags)
if (string.IsNullOrEmpty(value))
{
if (!string.IsNullOrEmpty(kvp.Value))
{
serializedTags.Append($"{kvp.Key}:{NormalizeTagValue(kvp.Value!)},");
}
return;
}

serializedTags.Remove(serializedTags.Length - 1, length: 1); // remove last comma
return StringBuilderCache.GetStringAndRelease(serializedTags);
// TraceUtil.NormalizeTag does almost exactly what we want, except it allows ':',
// which we don't want because we use it as a key/value separator.
var normalizedValue = TraceUtil.NormalizeTag(value).Replace(oldChar: ':', newChar: '_');
tags.Add($"{key}:{normalizedValue}");
}

private static string? GetEntryPointName()
private static string GetSerializedTagsFromList(List<string> tags)
{
return EntryAssemblyLocator.GetEntryAssembly()?.EntryPoint?.DeclaringType?.FullName;
return string.Join(",", tags);
}

private static string NormalizeTagValue(string tagValue)
/// <summary>
/// From the full path of a directory, get the name of the leaf directory.
/// </summary>
private static string GetLastPathSegment(string directoryPath)
{
// TraceUtil.NormalizeTag does almost exactly what we want, except it allows ':',
// which we don't want because we use it as a key/value separator.
return TraceUtil.NormalizeTag(tagValue).Replace(oldChar: ':', newChar: '_');
// Path.GetFileName returns an empty string if the path ends with a '/'.
// We could use Path.TrimEndingDirectorySeparator instead of the trim here, but it's not available on .NET Framework
return Path.GetFileName(directoryPath.TrimEnd('\\').TrimEnd('/'));
}

private static string? GetEntryPointName()
{
return EntryAssemblyLocator.GetEntryAssembly()?.EntryPoint?.DeclaringType?.FullName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ namespace Datadog.Trace.RemoteConfigurationManagement.Protocol
{
internal class RcmClientTracer
{
public RcmClientTracer(string runtimeId, string tracerVersion, string service, string env, string? appVersion, List<string> tags)
public RcmClientTracer(string runtimeId, string tracerVersion, string service, string env, string? appVersion, List<string> tags, List<string> processTags)
{
RuntimeId = runtimeId;
Language = TracerConstants.Language;
TracerVersion = tracerVersion;
Service = service;
ProcessTags = processTags;
Env = env;
AppVersion = appVersion;
Tags = tags;
Expand All @@ -35,6 +36,9 @@ public RcmClientTracer(string runtimeId, string tracerVersion, string service, s
[JsonProperty("service")]
public string Service { get; }

[JsonProperty("process_tags")]
public List<string> ProcessTags { get; }

[JsonProperty("extra_services")]
public string[]? ExtraServices { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public static RemoteConfigurationManager Create(
return new RemoteConfigurationManager(
discoveryService,
remoteConfigurationApi,
rcmTracer: new RcmClientTracer(settings.RuntimeId, settings.TracerVersion, serviceName, TraceUtil.NormalizeTag(tracerSettings.Environment), tracerSettings.ServiceVersion, tags),
new RcmClientTracer(settings.RuntimeId, settings.TracerVersion, serviceName, TraceUtil.NormalizeTag(tracerSettings.Environment), tracerSettings.ServiceVersion, tags, tracerSettings.PropagateProcessTags ? ProcessTags.TagsList : []),
pollInterval: settings.PollInterval,
gitMetadataTagsProvider,
subscriptionManager);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ private static GetRcmRequest GetRequest(string backendClientStage = null)
service: nameof(RemoteConfigurationApiTests),
env: "RCM Test",
appVersion: "1.0.0",
tags: []);
tags: [],
processTags: ["a.b:c", "x.y:z"]);

var state = new RcmClientState(
rootVersion: 1,
Expand Down
Loading