diff --git a/AGENTS.md b/AGENTS.md index 693b48a8d971..dbf89a033dff 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -213,6 +213,7 @@ tracer/src/Datadog.Trace - Add missing `using` directives instead of fully-qualified type names - Use modern C# syntax, but avoid features requiring types unavailable in older runtimes (e.g., no `ValueTuple` syntax for .NET Framework 4.6.1) - Prefer modern collection expressions (`[]`) +- Use `StringUtil.IsNullOrEmpty()` instead of `string.IsNullOrEmpty()` for compatibility across all supported runtimes - StyleCop: see `tracer/stylecop.json`; address warnings before pushing **C/C++ style:** diff --git a/tracer/src/Datadog.Trace/Logging/DirectSubmission/Formatting/LogFormatter.cs b/tracer/src/Datadog.Trace/Logging/DirectSubmission/Formatting/LogFormatter.cs index 11dc23e826b3..d44d43ff3e6e 100644 --- a/tracer/src/Datadog.Trace/Logging/DirectSubmission/Formatting/LogFormatter.cs +++ b/tracer/src/Datadog.Trace/Logging/DirectSubmission/Formatting/LogFormatter.cs @@ -12,7 +12,6 @@ using Datadog.Trace.Ci.Tags; using Datadog.Trace.ClrProfiler.AutoInstrumentation.Logging; using Datadog.Trace.Configuration; -using Datadog.Trace.ExtensionMethods; using Datadog.Trace.Util; using Datadog.Trace.Vendors.Newtonsoft.Json; @@ -20,8 +19,8 @@ namespace Datadog.Trace.Logging.DirectSubmission.Formatting { internal class LogFormatter { - private const string KeyValueTagSeparator = ":"; - private const string TagSeparator = ","; + private const char KeyValueTagSeparator = ':'; + private const char TagSeparator = ','; private const string SourcePropertyName = "ddsource"; private const string ServicePropertyName = "service"; private const string HostPropertyName = "host"; @@ -76,9 +75,9 @@ private static string StringifyGlobalTags(IReadOnlyDictionary gl foreach (var tagPair in globalTags) { sb.Append(tagPair.Key) - .Append(':') + .Append(KeyValueTagSeparator) .Append(tagPair.Value) - .Append(','); + .Append(TagSeparator); } // remove final joiner @@ -86,16 +85,63 @@ private static string StringifyGlobalTags(IReadOnlyDictionary gl return StringBuilderCache.GetStringAndRelease(sb); } - private string? EnrichTagsWithAasMetadata(string globalTags, ImmutableAzureAppServiceSettings? aasSettings) + private static string RemoveScheme(string url) + { + if (url.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) + { + return url.Substring("https://".Length); + } + + if (url.StartsWith("http://", StringComparison.OrdinalIgnoreCase)) + { + return url.Substring("http://".Length); + } + + return url; + } + + private static string EnrichTagsWithAasMetadata(string globalTags, ImmutableAzureAppServiceSettings? aasSettings) { if (aasSettings is null) { - return string.IsNullOrEmpty(globalTags) ? null : globalTags; + return globalTags; + } + + var hasResourceId = !string.IsNullOrEmpty(aasSettings.ResourceId); + var hasSiteKind = !string.IsNullOrEmpty(aasSettings.SiteKind); + + if (!hasResourceId && !hasSiteKind) + { + return globalTags; + } + + var sb = StringBuilderCache.Acquire(); + + if (hasResourceId) + { + sb.Append(Trace.Tags.AzureAppServicesResourceId) + .Append(KeyValueTagSeparator) + .Append(aasSettings.ResourceId) + .Append(TagSeparator); } - var aasTags = $"{Trace.Tags.AzureAppServicesResourceId}{KeyValueTagSeparator}{aasSettings.ResourceId}"; + if (hasSiteKind) + { + sb.Append(Trace.Tags.AzureAppServicesSiteKind) + .Append(KeyValueTagSeparator) + .Append(aasSettings.SiteKind) + .Append(TagSeparator); + } - return string.IsNullOrEmpty(globalTags) ? aasTags : aasTags + TagSeparator + globalTags; + // remove final joiner + sb.Remove(sb.Length - 1, length: 1); + if (!string.IsNullOrEmpty(globalTags)) + { + sb.Append(TagSeparator) + .Append(globalTags); + } + + return StringBuilderCache.GetStringAndRelease(sb); } private void EnrichTagsStringWithGitMetadata() @@ -120,16 +166,6 @@ private void EnrichTagsStringWithGitMetadata() _gitMetadataAdded = true; } - private string? RemoveScheme(string url) - { - return url switch - { - { } when url.StartsWith("https://", StringComparison.OrdinalIgnoreCase) => url.Substring("https://".Length), - { } when url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) => url.Substring("http://".Length), - _ => url - }; - } - internal static bool IsSourceProperty(string? propertyName) => string.Equals(propertyName, SourcePropertyName, StringComparison.OrdinalIgnoreCase); @@ -317,7 +353,7 @@ internal void FormatLog( EnrichTagsStringWithGitMetadata(); - if (Tags is not null && !renderingDetails.HasRenderedTags) + if (!StringUtil.IsNullOrEmpty(Tags) && !renderingDetails.HasRenderedTags) { writer.WritePropertyName(TagsPropertyName, escape: false); writer.WriteValue(Tags); diff --git a/tracer/test/Datadog.Trace.Tests/Logging/DirectSubmission/Formatting/LogFormatterTests.cs b/tracer/test/Datadog.Trace.Tests/Logging/DirectSubmission/Formatting/LogFormatterTests.cs index d7a2871bd64b..5aeb241e1309 100644 --- a/tracer/test/Datadog.Trace.Tests/Logging/DirectSubmission/Formatting/LogFormatterTests.cs +++ b/tracer/test/Datadog.Trace.Tests/Logging/DirectSubmission/Formatting/LogFormatterTests.cs @@ -182,7 +182,7 @@ public void WritesLogFormatCorrectly( (string Key, string Value)[] expectedTags = (isInAas, hasRenderedTags) switch { (_, true) => null, // if the log itself adds this, we don't add the global tags currently - (true, false) => [("aas.resource.id", aasSettings!.ResourceId), ("Key1", "Value1"), ("Key2", "Value2")], + (true, false) => [("aas.resource.id", aasSettings!.ResourceId), ("aas.site.kind", aasSettings.SiteKind), ("Key1", "Value1"), ("Key2", "Value2")], (false, false) => [("Key1", "Value1"), ("Key2", "Value2")], };