Skip to content
This repository was archived by the owner on Aug 3, 2024. It is now read-only.

Commit abbf45b

Browse files
Add DiagnosticsTelemetryModule.AddOrSetHeartbeatProperty extension method (#326)
1 parent b107552 commit abbf45b

6 files changed

Lines changed: 166 additions & 2 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing;
6+
7+
namespace NuGet.Services.Logging
8+
{
9+
public static class DiagnosticsTelemetryModuleExtensions
10+
{
11+
/// <summary>
12+
/// Tries to add or set a heartbeat property.
13+
/// </summary>
14+
/// <param name="module">The <see cref="DiagnosticsTelemetryModule"/>.</param>
15+
/// <param name="propertyName">Name of the heartbeat value to add.</param>
16+
/// <param name="propertyValue">Current value of the heartbeat value to add.</param>
17+
/// <param name="isHealthy">Flag indicating whether or not the property represents a healthy value.</param>
18+
/// <returns><c>True</c> if the property was set; otherwise <c>False</c>.</returns>
19+
public static bool AddOrSetHeartbeatProperty(
20+
this DiagnosticsTelemetryModule module,
21+
string propertyName,
22+
string propertyValue,
23+
bool isHealthy)
24+
{
25+
if (module == null)
26+
{
27+
throw new ArgumentNullException(nameof(module));
28+
}
29+
30+
var propertySet = module.AddHeartbeatProperty(propertyName, propertyValue, isHealthy);
31+
if (!propertySet)
32+
{
33+
return module.SetHeartbeatProperty(propertyName, propertyValue, isHealthy);
34+
}
35+
36+
return propertySet;
37+
}
38+
}
39+
}

src/NuGet.Services.Logging/TelemetryClientExtensions.cs renamed to src/NuGet.Services.Logging/Extensions/TelemetryClientExtensions.cs

File renamed without changes.

src/NuGet.Services.Logging/NuGet.Services.Logging.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,12 @@
5353
<Compile Include="DeploymentLabelEnricher.cs" />
5454
<Compile Include="DurationMetric.cs" />
5555
<Compile Include="ExceptionTelemetryProcessor.cs" />
56+
<Compile Include="Extensions\DiagnosticsTelemetryModuleExtensions.cs" />
5657
<Compile Include="LoggingSetup.cs" />
5758
<Compile Include="Properties\AssemblyInfo.cs" />
5859
<Compile Include="Properties\AssemblyInfo.*.cs" />
5960
<Compile Include="RequestTelemetryProcessor.cs" />
60-
<Compile Include="TelemetryClientExtensions.cs" />
61+
<Compile Include="Extensions\TelemetryClientExtensions.cs" />
6162
<Compile Include="TelemetryClientWrapper.cs" />
6263
<Compile Include="TelemetryContextInitializer.cs" />
6364
</ItemGroup>
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Reflection;
7+
using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing;
8+
using Xunit;
9+
10+
namespace NuGet.Services.Logging.Tests
11+
{
12+
public class DiagnosticsTelemetryModuleExtensionsTests
13+
{
14+
public class TheAddOrSetHeartbeatPropertyMethod
15+
{
16+
[Fact]
17+
public void ThrowsForNullModule()
18+
{
19+
Assert.Throws<ArgumentNullException>(
20+
"module",
21+
() => DiagnosticsTelemetryModuleExtensions.AddOrSetHeartbeatProperty(null, "name", "value", true));
22+
}
23+
24+
[Fact]
25+
public void ReturnsFalseForNullPropertyName()
26+
{
27+
// Arrange
28+
var module = new DiagnosticsTelemetryModule();
29+
30+
// Act
31+
var result = DiagnosticsTelemetryModuleExtensions.AddOrSetHeartbeatProperty(module, null, "value", true);
32+
33+
// Assert
34+
Assert.False(result);
35+
}
36+
37+
[Theory]
38+
[InlineData("propertyName", "propertyValue", true)]
39+
[InlineData("propertyName", "propertyValue", false)]
40+
public void ReturnsTrueAndAddsPayloadWhenNotAddedYet(string propertyName, string propertyValue, bool isHealthy)
41+
{
42+
// Arrange
43+
var module = new DiagnosticsTelemetryModule();
44+
45+
// Act
46+
var result = DiagnosticsTelemetryModuleExtensions.AddOrSetHeartbeatProperty(
47+
module,
48+
propertyName,
49+
propertyValue,
50+
isHealthy);
51+
52+
// Assert
53+
Assert.True(result);
54+
55+
VerifyHeartbeatPropertyPayload(module, propertyName, propertyValue, isHealthy);
56+
}
57+
58+
[Theory]
59+
[InlineData("propertyName", "propertyValue", true)]
60+
[InlineData("propertyName", "propertyValue", false)]
61+
public void ReturnsTrueAndSetsPayloadWhenAlreadyAdded(string propertyName, string propertyValue, bool isHealthy)
62+
{
63+
// Arrange
64+
var module = new DiagnosticsTelemetryModule();
65+
module.AddHeartbeatProperty(propertyName, propertyValue, isHealthy);
66+
67+
// Act
68+
var result = DiagnosticsTelemetryModuleExtensions.AddOrSetHeartbeatProperty(
69+
module,
70+
propertyName,
71+
propertyValue,
72+
isHealthy);
73+
74+
// Assert
75+
Assert.True(result);
76+
77+
VerifyHeartbeatPropertyPayload(module, propertyName, propertyValue, isHealthy);
78+
}
79+
80+
private void VerifyHeartbeatPropertyPayload(
81+
DiagnosticsTelemetryModule module,
82+
string propertyName,
83+
string propertyValue,
84+
bool isHealthy)
85+
{
86+
// Verify payload settings using reflection
87+
var heartbeatPropertyManager = typeof(DiagnosticsTelemetryModule)
88+
.GetField("HeartbeatProvider", BindingFlags.NonPublic | BindingFlags.Instance)
89+
.GetValue(module) as IHeartbeatPropertyManager;
90+
91+
var heartbeatProperties = heartbeatPropertyManager
92+
.GetType()
93+
.GetField("heartbeatProperties", BindingFlags.NonPublic | BindingFlags.Instance)
94+
.GetValue(heartbeatPropertyManager); // returns ConcurrentDictionary<string, HeartbeatPropertyPayload>
95+
96+
var propertyNames = heartbeatProperties
97+
.GetType()
98+
.GetProperty("Keys", BindingFlags.Public | BindingFlags.Instance)
99+
.GetValue(heartbeatProperties) as ICollection<string>;
100+
101+
Assert.Contains(propertyNames, i => i.Equals(propertyName));
102+
103+
var propertyPayload = heartbeatProperties
104+
.GetType()
105+
.GetProperty("Item", BindingFlags.Public | BindingFlags.Instance)
106+
.GetValue(heartbeatProperties, new object[] { propertyName }); // returns internal type HeartbeatPropertyPayload
107+
108+
var payloadValue = propertyPayload
109+
.GetType()
110+
.GetProperty("PayloadValue", BindingFlags.Public | BindingFlags.Instance)
111+
.GetValue(propertyPayload) as string;
112+
113+
var payloadIsHealthy = (bool)propertyPayload
114+
.GetType()
115+
.GetProperty("IsHealthy", BindingFlags.Public | BindingFlags.Instance)
116+
.GetValue(propertyPayload);
117+
118+
Assert.Equal(propertyValue, payloadValue);
119+
Assert.Equal(isHealthy, payloadIsHealthy);
120+
}
121+
}
122+
}
123+
}

tests/NuGet.Services.Logging.Tests/TelemetryClientExtensionsTests.cs renamed to tests/NuGet.Services.Logging.Tests/Extensions/TelemetryClientExtensionsTests.cs

File renamed without changes.

tests/NuGet.Services.Logging.Tests/NuGet.Services.Logging.Tests.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@
5555
<ItemGroup>
5656
<Compile Include="ApplicationInsightsTests.cs" />
5757
<Compile Include="DeploymentLabelEnricherTests.cs" />
58+
<Compile Include="Extensions\DiagnosticsTelemetryModuleExtensionsTests.cs" />
5859
<Compile Include="ExceptionTelemetryProcessorTests.cs" />
5960
<Compile Include="LoggingSetupTests.cs" />
6061
<Compile Include="Properties\AssemblyInfo.cs" />
6162
<Compile Include="RequestTelemetryProcessorTests.cs" />
62-
<Compile Include="TelemetryClientExtensionsTests.cs" />
63+
<Compile Include="Extensions\TelemetryClientExtensionsTests.cs" />
6364
<Compile Include="TelemetryProcessorTest.cs" />
6465
</ItemGroup>
6566
<ItemGroup>

0 commit comments

Comments
 (0)