diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/CHANGELOG.md b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/CHANGELOG.md
index 30df46c5e3ccb..d4955741de97f 100644
--- a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/CHANGELOG.md
+++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/CHANGELOG.md
@@ -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
diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/ArrayExtensions.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/ArrayExtensions.cs
index 1ec790acb12e3..d8b5c4871afc9 100644
--- a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/ArrayExtensions.cs
+++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/ArrayExtensions.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Globalization;
using System.Text;
namespace Azure.Monitor.OpenTelemetry.Exporter.Internals
@@ -18,6 +19,12 @@ internal static class ArrayExtensions
/// A comma delimited string of the components of the input array.
[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)
{
@@ -29,7 +36,7 @@ internal static class ArrayExtensions
{
if (item != null)
{
- sb.Append(item);
+ sb.Append(Convert.ToString(item, cultureInfo));
sb.Append(',');
}
}
diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/TraceHelper.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/TraceHelper.cs
index 59d3569905c45..47443475c4d3b 100644
--- a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/TraceHelper.cs
+++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/TraceHelper.cs
@@ -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;
@@ -85,7 +86,7 @@ internal static void AddPropertiesToTelemetry(IDictionary 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");
}
}
}
diff --git a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/tests/Azure.Monitor.OpenTelemetry.Exporter.Tests/TagsTests.cs b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/tests/Azure.Monitor.OpenTelemetry.Exporter.Tests/TagsTests.cs
index e997870763ad2..acea80dc5e756 100644
--- a/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/tests/Azure.Monitor.OpenTelemetry.Exporter.Tests/TagsTests.cs
+++ b/sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/tests/Azure.Monitor.OpenTelemetry.Exporter.Tests/TagsTests.cs
@@ -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;
@@ -81,7 +82,7 @@ public void TagObjects_UnMapped()
{
var activityTagsProcessor = new ActivityTagsProcessor();
- IEnumerable> tagObjects = new Dictionary { ["somekey"] = "value" }; ;
+ IEnumerable> tagObjects = new Dictionary { ["somekey"] = "value" };
using var activity = CreateTestActivity(tagObjects);
activityTagsProcessor.CategorizeTags(activity);
@@ -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 properties = new Dictionary();
+
+ var doubleArray = new double[] { 1.1, 2.2, 3.3 };
+ var doubleValue = 123.45;
+
+ IEnumerable> tagObjects = new Dictionary
+ {
+ ["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>? additionalAttributes = null, ActivityKind activityKind = ActivityKind.Server)
{
var startTimestamp = DateTime.UtcNow;