Skip to content

Commit 1143722

Browse files
authored
Merge pull request #455 from microsoft/rossgrambo-activity-based-telemetry
Telemetry Publishing via Activity & Activity Event
2 parents d90fa76 + 4579aef commit 1143722

16 files changed

+399
-353
lines changed

examples/EvaluationDataToApplicationInsights/Program.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT license.
33
//
4-
using Microsoft.FeatureManagement.Telemetry;
54
using Microsoft.FeatureManagement;
65
using EvaluationDataToApplicationInsights;
76
using Microsoft.ApplicationInsights.Extensibility;
@@ -31,7 +30,7 @@
3130
// Wire up evaluation event emission
3231
builder.Services.AddFeatureManagement()
3332
.WithTargeting<HttpContextTargetingContextAccessor>()
34-
.AddTelemetryPublisher<ApplicationInsightsTelemetryPublisher>();
33+
.AddApplicationInsightsTelemetryPublisher();
3534

3635
//
3736
// Default code from .NET template below
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using Microsoft.ApplicationInsights;
2+
using System.Diagnostics;
3+
4+
namespace Microsoft.FeatureManagement.Telemetry.ApplicationInsights
5+
{
6+
/// <summary>
7+
/// Listens to <see cref="Activity"/> events from feature management and sends them to Application Insights.
8+
/// </summary>
9+
internal sealed class ApplicationInsightsEventPublisher : IDisposable
10+
{
11+
private readonly TelemetryClient _telemetryClient;
12+
private readonly ActivityListener _activityListener;
13+
14+
/// <summary>
15+
/// Initializes a new instance of the <see cref="ApplicationInsightsEventPublisher"/> class.
16+
/// </summary>
17+
/// <param name="telemetryClient">The Application Insights telemetry client.</param>
18+
public ApplicationInsightsEventPublisher(TelemetryClient telemetryClient)
19+
{
20+
_telemetryClient = telemetryClient ?? throw new ArgumentNullException(nameof(telemetryClient));
21+
22+
_activityListener = new ActivityListener
23+
{
24+
ShouldListenTo = (activitySource) => activitySource.Name == "Microsoft.FeatureManagement",
25+
Sample = (ref ActivityCreationOptions<ActivityContext> options) => ActivitySamplingResult.AllData,
26+
ActivityStopped = (activity) =>
27+
{
28+
ActivityEvent? evaluationEvent = activity.Events.FirstOrDefault((activityEvent) => activityEvent.Name == "feature_flag");
29+
30+
if (evaluationEvent.HasValue && evaluationEvent.Value.Tags.Any())
31+
{
32+
HandleFeatureFlagEvent(evaluationEvent.Value);
33+
}
34+
}
35+
};
36+
37+
ActivitySource.AddActivityListener(_activityListener);
38+
}
39+
40+
/// <summary>
41+
/// Disposes the resources used by the <see cref="ApplicationInsightsEventPublisher"/>.
42+
/// </summary>
43+
public void Dispose()
44+
{
45+
_activityListener.Dispose();
46+
}
47+
48+
private void HandleFeatureFlagEvent(ActivityEvent activityEvent)
49+
{
50+
var properties = new Dictionary<string, string>();
51+
52+
foreach (var tag in activityEvent.Tags)
53+
{
54+
properties[tag.Key] = tag.Value?.ToString();
55+
}
56+
57+
_telemetryClient.TrackEvent("FeatureEvaluation", properties);
58+
}
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using Microsoft.Extensions.Hosting;
3+
4+
namespace Microsoft.FeatureManagement.Telemetry.ApplicationInsights
5+
{
6+
/// <summary>
7+
/// A hosted service used to construct and dispose the <see cref="ApplicationInsightsEventPublisher"/>
8+
/// </summary>
9+
internal sealed class ApplicationInsightsHostedService : IHostedService
10+
{
11+
private readonly IServiceProvider _serviceProvider;
12+
13+
/// <summary>
14+
/// Initializes a new instance of the <see cref="ApplicationInsightsHostedService"/> class.
15+
/// </summary>
16+
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to get the publisher from.</param>
17+
public ApplicationInsightsHostedService(IServiceProvider serviceProvider)
18+
{
19+
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
20+
}
21+
22+
/// <summary>
23+
/// Uses the service provider to construct a <see cref="ApplicationInsightsEventPublisher"/> which will start listening for activities.
24+
/// </summary>
25+
/// <param name="cancellationToken">The cancellation token.</param>
26+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
27+
public Task StartAsync(CancellationToken cancellationToken)
28+
{
29+
_serviceProvider.GetService<ApplicationInsightsEventPublisher>();
30+
31+
return Task.CompletedTask;
32+
}
33+
34+
/// <summary>
35+
/// Stops this hosted service.
36+
/// </summary>
37+
/// <param name="cancellationToken">The cancellation token.</param>
38+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
39+
public Task StopAsync(CancellationToken cancellationToken)
40+
{
41+
return Task.CompletedTask;
42+
}
43+
}
44+
}

src/Microsoft.FeatureManagement.Telemetry.ApplicationInsights/ApplicationInsightsTelemetryPublisher.cs

-113
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
//
4+
using Microsoft.Extensions.DependencyInjection;
5+
using Microsoft.Extensions.Hosting;
6+
using Microsoft.FeatureManagement.Telemetry.ApplicationInsights;
7+
8+
namespace Microsoft.FeatureManagement
9+
{
10+
/// <summary>
11+
/// Extensions used to add feature management functionality.
12+
/// </summary>
13+
public static class FeatureManagementBuilderExtensions
14+
{
15+
/// <summary>
16+
/// Adds the <see cref="ApplicationInsightsEventPublisher"/> using <see cref="ApplicationInsightsHostedService"/> to the feature management builder.
17+
/// </summary>
18+
/// <param name="builder">The feature management builder.</param>
19+
/// <returns>The feature management builder.</returns>
20+
public static IFeatureManagementBuilder AddApplicationInsightsTelemetryPublisher(this IFeatureManagementBuilder builder)
21+
{
22+
if (builder == null)
23+
{
24+
throw new ArgumentNullException(nameof(builder));
25+
}
26+
27+
if (builder.Services == null)
28+
{
29+
throw new ArgumentException($"The provided builder's services must not be null.", nameof(builder));
30+
}
31+
32+
builder.Services.AddSingleton<ApplicationInsightsEventPublisher>();
33+
34+
if (!builder.Services.Any((ServiceDescriptor d) => d.ServiceType == typeof(IHostedService) && d.ImplementationType == typeof(ApplicationInsightsHostedService)))
35+
{
36+
builder.Services.Insert(0, ServiceDescriptor.Singleton<IHostedService, ApplicationInsightsHostedService>());
37+
}
38+
39+
return builder;
40+
}
41+
}
42+
}

src/Microsoft.FeatureManagement.Telemetry.ApplicationInsights/Microsoft.FeatureManagement.Telemetry.ApplicationInsights.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
<ItemGroup>
3636
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.22.0" />
37+
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
3738
</ItemGroup>
3839

3940
<ItemGroup>

src/Microsoft.FeatureManagement/FeatureManagementBuilderExtensions.cs

-27
Original file line numberDiff line numberDiff line change
@@ -76,32 +76,5 @@ public static IFeatureManagementBuilder WithVariantService<TService>(this IFeatu
7676

7777
return builder;
7878
}
79-
80-
/// <summary>
81-
/// Adds a telemetry publisher to the feature management system.
82-
/// </summary>
83-
/// <param name="builder">The <see cref="IFeatureManagementBuilder"/> used to customize feature management functionality.</param>
84-
/// <returns>A <see cref="IFeatureManagementBuilder"/> that can be used to customize feature management functionality.</returns>
85-
public static IFeatureManagementBuilder AddTelemetryPublisher<T>(this IFeatureManagementBuilder builder) where T : ITelemetryPublisher
86-
{
87-
builder.AddTelemetryPublisher(sp => ActivatorUtilities.CreateInstance(sp, typeof(T)) as ITelemetryPublisher);
88-
89-
return builder;
90-
}
91-
92-
private static IFeatureManagementBuilder AddTelemetryPublisher(this IFeatureManagementBuilder builder, Func<IServiceProvider, ITelemetryPublisher> factory)
93-
{
94-
builder.Services.Configure<FeatureManagementOptions>(options =>
95-
{
96-
if (options.TelemetryPublisherFactories == null)
97-
{
98-
options.TelemetryPublisherFactories = new List<Func<IServiceProvider, ITelemetryPublisher>>();
99-
}
100-
101-
options.TelemetryPublisherFactories.Add(factory);
102-
});
103-
104-
return builder;
105-
}
10679
}
10780
}

src/Microsoft.FeatureManagement/FeatureManagementOptions.cs

-6
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,5 @@ public class FeatureManagementOptions
2626
/// The default value is true.
2727
/// </summary>
2828
public bool IgnoreMissingFeatures { get; set; } = true;
29-
30-
/// <summary>
31-
/// Holds a collection of factories that can be used to create <see cref="ITelemetryPublisher"/> instances.
32-
/// This avoids the need to add the publishers to the service collection.
33-
/// </summary>
34-
internal ICollection<Func<IServiceProvider, ITelemetryPublisher>> TelemetryPublisherFactories { get; set; }
3529
}
3630
}

0 commit comments

Comments
 (0)