Skip to content

Commit

Permalink
Adding SetParentAsDependent Activity Extension (#671)
Browse files Browse the repository at this point in the history
* Adding SetParentAsDependent Activity Extension

* Updating to use ActivityMetricsSender

* Addressing comments

* Removing redundant null check

* Chaning name to SetParentNameAsDimensionEnabled

---------

Co-authored-by: Isaac Walker <iswalke@microsoft.com>
  • Loading branch information
IsaacWalker and Isaac Walker authored Apr 24, 2024
1 parent 197fee6 commit 0221ca2
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 9 deletions.
19 changes: 18 additions & 1 deletion src/Activities/Internal/ActivityMetricsSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Diagnostics;
using System.Diagnostics.Metrics;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.Omex.Extensions.Abstractions.Activities;
using Microsoft.Omex.Extensions.Abstractions.ExecutionContext;

Expand All @@ -20,8 +21,14 @@ internal sealed class ActivityMetricsSender : IActivitiesEventSender, IDisposabl
private readonly IHostEnvironment m_hostEnvironment;
private readonly HashSet<string> m_customBaggageDimension;
private readonly HashSet<string> m_customTagObjectsDimension;
private readonly bool m_isSetParentNameAsDimensionEnabled;

public ActivityMetricsSender(IExecutionContext executionContext, IHostEnvironment hostEnvironment, ICustomBaggageDimensions customBaggageDimensions, ICustomTagObjectsDimensions customTagObjectsDimensions)
public ActivityMetricsSender(
IExecutionContext executionContext,
IHostEnvironment hostEnvironment,
ICustomBaggageDimensions customBaggageDimensions,
ICustomTagObjectsDimensions customTagObjectsDimensions,
IOptions<ActivityOption> activityOptions)
{
m_context = executionContext;
m_hostEnvironment = hostEnvironment;
Expand All @@ -30,6 +37,7 @@ public ActivityMetricsSender(IExecutionContext executionContext, IHostEnvironmen
m_healthCheckActivityHistogram = m_meter.CreateHistogram<long>("HealthCheckActivities");
m_customBaggageDimension = customBaggageDimensions.CustomDimensions;
m_customTagObjectsDimension = customTagObjectsDimensions.CustomDimensions;
m_isSetParentNameAsDimensionEnabled = activityOptions.Value.SetParentNameAsDimensionEnabled;
}

public void SendActivityMetric(Activity activity)
Expand Down Expand Up @@ -73,6 +81,15 @@ public void SendActivityMetric(Activity activity)
}
}

if (m_isSetParentNameAsDimensionEnabled)
{
Activity? parent = activity.Parent;
if (!string.IsNullOrEmpty(parent?.OperationName))
{
tagList.Add("ParentName", parent.OperationName);
}
}

histogram.Record(durationMs, tagList);
}

Expand Down
5 changes: 5 additions & 0 deletions src/Activities/Option/ActivityOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,10 @@ internal class ActivityOption
/// Disable ActivityEventSender so Activity metric is only sent via ActivityMetricsSender
/// </summary>
public bool ActivityEventSenderEnabled { get; set; } = true;

/// <summary>
/// Sets each activities parent name as a dimension value.
/// </summary>
public bool SetParentNameAsDimensionEnabled { get; set; } = false;
}
}
73 changes: 65 additions & 8 deletions tests/Activities.UnitTests/Internal/ActivityMetricsSenderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Diagnostics.Metrics;
using System.Linq;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.Omex.Extensions.Abstractions.Activities;
using Microsoft.Omex.Extensions.Abstractions.Activities.Processing;
using Microsoft.Omex.Extensions.Abstractions.ExecutionContext;
Expand All @@ -25,13 +26,14 @@ public class ActivityMetricsSenderTests
public void SendActivityMetric_NoCustomDimensions_ProduceMetricPointSuccessfully(bool isHealthCheck)
{
// 1. Arrange
(IExecutionContext context, IHostEnvironment environment) = PrepareEnvironment();
(IExecutionContext context, IHostEnvironment environment, IOptions<ActivityOption> options) = PrepareEnvironment();

ActivityMetricsSender sender = new(
context,
environment,
isHealthCheck ? new CustomBaggageDimensions(new HashSet<string> { "HealthCheckMarker" }) : new CustomBaggageDimensions(),
new CustomTagObjectsDimensions());
new CustomTagObjectsDimensions(),
options);
Listener listener = new();

Activity activity = new(nameof(activity));
Expand Down Expand Up @@ -59,7 +61,7 @@ public void SendActivityMetric_NoCustomDimensions_ProduceMetricPointSuccessfully
public void SendActivityMetric_CustomDimensionsAreRegistered_ProduceMetricPointWithRegisteredDimensions(bool isHealthCheck)
{
// 1. Arrange
(IExecutionContext context, IHostEnvironment environment) = PrepareEnvironment();
(IExecutionContext context, IHostEnvironment environment, IOptions<ActivityOption> options) = PrepareEnvironment();

const string testBaggage1 = "TestBaggage1";
const string testBaggage2 = "TestBaggage2";
Expand All @@ -76,7 +78,8 @@ public void SendActivityMetric_CustomDimensionsAreRegistered_ProduceMetricPointW
{
ActivityTagKeys.Result, ActivityTagKeys.Metadata, ActivityTagKeys.SubType,
testTag1, testTag2
}));
}),
options);

Listener listener = new();

Expand Down Expand Up @@ -109,7 +112,7 @@ public void SendActivityMetric_CustomDimensionsAreRegistered_ProduceMetricPointW
public void SendActivityMetric_CustomDimensionsAreNotRegistered_FailToProduceMetricPoint()
{
// 1. Arrange
(IExecutionContext context, IHostEnvironment environment) = PrepareEnvironment();
(IExecutionContext context, IHostEnvironment environment, IOptions<ActivityOption> options) = PrepareEnvironment();

const string testBaggage1 = "TestBaggage1";
const string testBaggage2 = "TestBaggage2";
Expand All @@ -120,7 +123,8 @@ public void SendActivityMetric_CustomDimensionsAreNotRegistered_FailToProduceMet
context,
environment,
new CustomBaggageDimensions(new HashSet<string>()), // Override by empty set
new CustomTagObjectsDimensions(new HashSet<string>())); // Override by empty set
new CustomTagObjectsDimensions(new HashSet<string>()),
options); // Override by empty set

Listener listener = new();

Expand Down Expand Up @@ -153,6 +157,52 @@ public void SendActivityMetric_CustomDimensionsAreNotRegistered_FailToProduceMet
}
}

[TestMethod]
[DataRow(true, true, true, DisplayName = "Parent Name emmitted when parent is present and has a name")]
[DataRow(true, false, false, DisplayName = "Parent Name not emmitted when parent is present but has no name")]
[DataRow(false, false, false, DisplayName = "Parent Name not emmitted when parent is not present")]
public void SendActivityMetric_WithSendParentName_ProducesMetricPointWithParentName(bool hasParentActivity, bool hasParentName, bool expectParentNameToBeEmitted)
{
// 1. Arrange
ActivityOption activityOptions = new()
{
SetParentNameAsDimensionEnabled = true
};
(IExecutionContext context, IHostEnvironment environment, IOptions<ActivityOption> options) = PrepareEnvironment(activityOptions);

ActivityMetricsSender sender = new(
context,
environment,
new CustomBaggageDimensions([]),
new CustomTagObjectsDimensions([]),
options);
Listener listener = new();

string? parentName = hasParentName ? nameof(parentName) : null;
if (hasParentActivity)
{
Activity parent = new(parentName!);
parent.Start();
}

Activity activity = new(nameof(activity));
activity.Start().Stop();

sender.SendActivityMetric(activity);

// 3. Assert
MeasurementResult result = listener.Results.First(m => environment.EnvironmentName.Equals(m.Tags[s_environmentTagName]));

if(expectParentNameToBeEmitted)
{
AssertTag(result, "ParentName", nameof(parentName));
}
else
{
AssertTagNotExist(result, "ParentName");
}
}

private void VerifyTagsExist(Listener listener, Activity activity, IExecutionContext context, IHostEnvironment environment, string instrumentationName)
{
MeasurementResult result = listener.Results.First(m => environment.EnvironmentName.Equals(m.Tags[s_environmentTagName]));
Expand Down Expand Up @@ -185,8 +235,10 @@ private void VerifyTagsExist(Listener listener, Activity activity, IExecutionCon

private static void AssertTag(MeasurementResult result, string key, object? expectedValue) => Assert.AreEqual(expectedValue, result.Tags[key]);

private static void AssertTagNotExist(MeasurementResult result, string key) => CollectionAssert.DoesNotContain(result.Tags.Keys, key);

private (IExecutionContext, IHostEnvironment) PrepareEnvironment()

private (IExecutionContext, IHostEnvironment, IOptions<ActivityOption>) PrepareEnvironment(ActivityOption? options = null)
{
Activity.DefaultIdFormat = ActivityIdFormat.W3C;
Activity.ForceDefaultIdFormat = true;
Expand All @@ -207,7 +259,12 @@ private void VerifyTagsExist(Listener listener, Activity activity, IExecutionCon
Mock<IHostEnvironment> environmentMock = new();
environmentMock.Setup(e => e.EnvironmentName).Returns("TestEnv");
IHostEnvironment environment = environmentMock.Object;
return (context, environment);

Mock<IOptions<ActivityOption>> optionsMock = new();
optionsMock.Setup(mock => mock.Value).Returns(options ?? new ActivityOption());
IOptions<ActivityOption> activityOptions = optionsMock.Object;

return (context, environment, activityOptions);
}

private static readonly string s_environmentTagName = "Environment";
Expand Down

0 comments on commit 0221ca2

Please sign in to comment.