Skip to content

Commit

Permalink
[AzureMonitorExporter] Fix how arrays stored in Activity Tags are ser…
Browse files Browse the repository at this point in the history
…ialized. (#34086)

* fix array tostring

* cleanup
  • Loading branch information
TimothyMothra authored Feb 11, 2023
1 parent 94dd3d8 commit 5db3f5b
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Text;

namespace Azure.Monitor.OpenTelemetry.Exporter.Internals
{
internal static class ArrayExtensions
{
/// <summary>
/// Builds a comma delimited string of the components of an array.
/// </summary>
/// <remarks>
/// For example: new int[] { 1, 2, 3 } would be returned as "1,2,3".
/// </remarks>
/// <param name="input">An array to be evaluated.</param>
/// <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)
{
if (input == null)
{
return null;
}

StringBuilder sb = new(input.Length);
foreach (var item in input)
{
// TODO: Consider changing it to JSon array.
if (item != null)
{
sb.Append(item);
sb.Append(',');
}
}

// remove trailing comma
if (sb.Length > 0)
{
sb.Length--;
}

return sb.ToString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,23 +80,7 @@ public void ForEach(IEnumerable<KeyValuePair<string, object>> activityTags)

if (activityTag.Value is Array array)
{
StringBuilder sw = new StringBuilder();
foreach (var item in array)
{
// TODO: Consider changing it to JSon array.
if (item != null)
{
sw.Append(item);
sw.Append(',');
}
}

if (sw.Length > 0)
{
sw.Length--;
}

AzMonList.Add(ref UnMappedTags, new KeyValuePair<string, object>(activityTag.Key, sw.ToString()));
AzMonList.Add(ref UnMappedTags, new KeyValuePair<string, object>(activityTag.Key, array.ToCommaDelimitedString()));
continue;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,14 @@ private static MonitorBase GetTraceTelemetryData(ActivityEvent activityEvent)

foreach (var tag in activityEvent.Tags)
{
messageData.Properties.Add(tag.Key, tag.Value.ToString());
if (tag.Value is Array arrayValue)
{
messageData.Properties.Add(tag.Key, arrayValue.ToCommaDelimitedString());
}
else
{
messageData.Properties.Add(tag.Key, tag.Value.ToString());
}
}

return new MonitorBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ namespace Azure.Monitor.OpenTelemetry.Exporter.Tests.CommonTestFramework
{
internal static class TelemetryItemValidationHelper
{
public static void AssertLog_As_MessageTelemetry(
public static void AssertMessageTelemetry(
TelemetryItem telemetryItem,
string expectedSeverityLevel,
string expectedMessage,
IDictionary<string, string> expectedMeessageProperties,
IDictionary<string, string> expectedMessageProperties,
string expectedSpanId,
string expectedTraceId)
{
Expand All @@ -43,10 +43,19 @@ public static void AssertLog_As_MessageTelemetry(
Assert.Contains("ai.internal.sdkVersion", telemetryItem.Tags.Keys);

var messageData = (MessageData)telemetryItem.Data.BaseData;
Assert.Equal(expectedSeverityLevel, messageData.SeverityLevel);

if (expectedSeverityLevel == null)
{
Assert.Null(messageData.SeverityLevel);
}
else
{
Assert.Equal(expectedSeverityLevel, messageData.SeverityLevel);
}

Assert.Equal(expectedMessage, messageData.Message);

foreach (var prop in expectedMeessageProperties)
foreach (var prop in expectedMessageProperties)
{
Assert.Equal(prop.Value, messageData.Properties[prop.Key]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ public void VerifyLog(LogLevel logLevel, string expectedSeverityLevel)
this.telemetryOutput.Write(telemetryItems);
var telemetryItem = telemetryItems.Single();

TelemetryItemValidationHelper.AssertLog_As_MessageTelemetry(
TelemetryItemValidationHelper.AssertMessageTelemetry(
telemetryItem: telemetryItem,
expectedSeverityLevel: expectedSeverityLevel,
expectedMessage: "Hello {name}.",
expectedMeessageProperties: new Dictionary<string, string> { { "name", "World" }},
expectedMessageProperties: new Dictionary<string, string> { { "name", "World" }},
expectedSpanId: null,
expectedTraceId: null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,11 +256,63 @@ public void VerifyLogWithinActivity(LogLevel logLevel, string expectedSeverityLe
this.telemetryOutput.Write(logTelemetryItems);
var logTelemetryItem = logTelemetryItems.Single();

TelemetryItemValidationHelper.AssertLog_As_MessageTelemetry(
TelemetryItemValidationHelper.AssertMessageTelemetry(
telemetryItem: logTelemetryItem,
expectedSeverityLevel: expectedSeverityLevel,
expectedMessage: "Hello {name}.",
expectedMeessageProperties: new Dictionary<string, string> { { "name", "World" } },
expectedMessageProperties: new Dictionary<string, string> { { "name", "World" } },
expectedSpanId: spanId,
expectedTraceId: traceId);
}

[Fact]
public void TestActivityEvents()
{
// SETUP
var uniqueTestId = Guid.NewGuid();

var activitySourceName = $"activitySourceName{uniqueTestId}";
using var activitySource = new ActivitySource(activitySourceName);

var tracerProvider = Sdk.CreateTracerProviderBuilder()
.AddSource(activitySourceName)
.AddAzureMonitorTraceExporterForTest(out ConcurrentBag<TelemetryItem> telemetryItems)
.Build();

// ACT
string spanId = null, traceId = null;

using (var activity = activitySource.StartActivity(name: "ActivityWithException"))
{
traceId = activity.TraceId.ToHexString();
spanId = activity.SpanId.ToHexString();

var eventTags = new Dictionary<string, object>
{
{ "integer", 1 },
{ "string", "Hello, World!" },
{ "intArray", new int[] { 1, 2, 3 } }
};
activity?.AddEvent(new("Gonna try it!", DateTimeOffset.Now, new(eventTags)));
}

// CLEANUP
tracerProvider.Dispose();

// ASSERT
Assert.True(telemetryItems.Any(), "Unit test failed to collect telemetry.");
this.telemetryOutput.Write(telemetryItems);

var messageTelemetryItem = telemetryItems.First(x => x.Name == "Message");

TelemetryItemValidationHelper.AssertMessageTelemetry(
telemetryItem: messageTelemetryItem,
expectedSeverityLevel: null,
expectedMessage: "Gonna try it!",
expectedMessageProperties: new Dictionary<string, string> {
{ "integer", "1" },
{ "string", "Hello, World!" },
{ "intArray", "1,2,3" } },
expectedSpanId: spanId,
expectedTraceId: traceId);
}
Expand Down

0 comments on commit 5db3f5b

Please sign in to comment.