Skip to content

Commit

Permalink
[AzureMonitorExporter] Fix Culture-Specific Formatting of Activity Ta…
Browse files Browse the repository at this point in the history
…gs (#40000)

* Fix 39470

* Update changelog

* PR feedback

* Update changelog link

* Fix french culture string.

* test fix
  • Loading branch information
rajkumar-rangaraj authored Nov 16, 2023
1 parent 3a90a19 commit a22f40e
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 4 deletions.
10 changes: 10 additions & 0 deletions sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@
* In case of duplicate keys within the scopes, only the first entry is
retained, while all subsequent duplicate entries are discarded.

* Resolved an issue where activity tags of various object types, including
double, float, and others, were previously formatted using
`CultureInfo.CurrentCulture`. This behavior caused inconsistencies in tag
value formatting depending on the regional settings of the machine where the
application was running. Such inconsistencies could lead to challenges in data
analysis and cause test failures in environments with differing cultural
settings. The fix ensures uniform and culture-independent formatting of
activity tag values, aligning with consistent data representation.
([#39470](https://github.com/Azure/azure-sdk-for-net/issues/39470))

### Other Changes

* Update OpenTelemetry dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Globalization;
using System.Text;

namespace Azure.Monitor.OpenTelemetry.Exporter.Internals
Expand All @@ -18,6 +19,12 @@ internal static class ArrayExtensions
/// <returns>A comma delimited string of the components of the input array.</returns>
[return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull(nameof(input))]
public static string? ToCommaDelimitedString(this Array? input)
{
return ToCommaDelimitedString(input, CultureInfo.InvariantCulture);
}

// This overload is used for testing purposes only.
internal static string? ToCommaDelimitedString(this Array? input, CultureInfo cultureInfo)
{
if (input == null)
{
Expand All @@ -29,7 +36,7 @@ internal static class ArrayExtensions
{
if (item != null)
{
sb.Append(item);
sb.Append(Convert.ToString(item, cultureInfo));
sb.Append(',');
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
Expand Down Expand Up @@ -85,7 +86,7 @@ internal static void AddPropertiesToTelemetry(IDictionary<string, string> destin
{
// Note: if Key exceeds MaxLength or if Value is null, the entire KVP will be dropped.

destination.Add(tag.Key, tag.Value.ToString().Truncate(SchemaConstants.KVP_MaxValueLength) ?? "null");
destination.Add(tag.Key, Convert.ToString(tag.Value, CultureInfo.InvariantCulture).Truncate(SchemaConstants.KVP_MaxValueLength) ?? "null");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;

using System.Globalization;
using System.Threading;
using Azure.Monitor.OpenTelemetry.Exporter.Internals;

using Xunit;
Expand Down Expand Up @@ -81,7 +82,7 @@ public void TagObjects_UnMapped()
{
var activityTagsProcessor = new ActivityTagsProcessor();

IEnumerable<KeyValuePair<string, object?>> tagObjects = new Dictionary<string, object?> { ["somekey"] = "value" }; ;
IEnumerable<KeyValuePair<string, object?>> tagObjects = new Dictionary<string, object?> { ["somekey"] = "value" };
using var activity = CreateTestActivity(tagObjects);
activityTagsProcessor.CategorizeTags(activity);

Expand Down Expand Up @@ -344,6 +345,42 @@ public void ActivityTagsProcessor_CategorizeTags_ExtractsAuthUserId(ActivityKind
Assert.Equal("TestUser", activityTagsProcessor.EndUserId);
}

[Theory]
[InlineData("fr-FR")] // French culture
[InlineData("de-DE")] // German culture
public void TagObjects_TestCulture(string cultureName)
{
var activityTagsProcessor = new ActivityTagsProcessor();
var originalCulture = Thread.CurrentThread.CurrentCulture;
var cultureInfo = new CultureInfo(cultureName);
Thread.CurrentThread.CurrentCulture = cultureInfo;
IDictionary<string, string> properties = new Dictionary<string, string>();

var doubleArray = new double[] { 1.1, 2.2, 3.3 };
var doubleValue = 123.45;

IEnumerable<KeyValuePair<string, object?>> tagObjects = new Dictionary<string, object?>
{
["doubleArray"] = doubleArray,
["double"] = doubleValue,
};

using var activity = CreateTestActivity(tagObjects);
activityTagsProcessor.CategorizeTags(activity);
TraceHelper.AddPropertiesToTelemetry(properties, ref activityTagsProcessor.UnMappedTags);

// Asserting Culture Behavior
Assert.NotEqual("1.1,2.2,3.3", doubleArray.ToCommaDelimitedString(cultureInfo));
Assert.NotEqual("123.45", doubleValue.ToString(cultureInfo));

// Asserting CultureInvariant Behavior
Assert.Equal("1.1,2.2,3.3", properties["doubleArray"]);
Assert.Equal("123.45", properties["double"]);

// Cleanup: Revert to the original culture
Thread.CurrentThread.CurrentCulture = originalCulture;
}

private static Activity CreateTestActivity(IEnumerable<KeyValuePair<string, object?>>? additionalAttributes = null, ActivityKind activityKind = ActivityKind.Server)
{
var startTimestamp = DateTime.UtcNow;
Expand Down

0 comments on commit a22f40e

Please sign in to comment.