Skip to content

Commit 324ff40

Browse files
Adds targeting middleware and targeting initializer (#350)
* Adds targeting middleware and targeting initializer * Added logger and added prefix to targetingId * Apply suggestions from code review Co-authored-by: Jimmy Campbell <jimmyca@microsoft.com> * Adjustments from comments --------- Co-authored-by: Jimmy Campbell <jimmyca@microsoft.com>
1 parent 3c35411 commit 324ff40

File tree

4 files changed

+183
-0
lines changed

4 files changed

+183
-0
lines changed

Microsoft.FeatureManagement.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.FeatureManagement
2727
EndProject
2828
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EvaluationDataToApplicationInsights", "examples\EvaluationDataToApplicationInsights\EvaluationDataToApplicationInsights.csproj", "{1502529E-47E9-4306-98C4-BF6CF7C7C275}"
2929
EndProject
30+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.FeatureManagement.Telemetry.ApplicationInsights.AspNetCore", "src\Microsoft.FeatureManagement.Telemetry.ApplicationInsights.AspNetCore\Microsoft.FeatureManagement.Telemetry.ApplicationInsights.AspNetCore.csproj", "{C647611B-A8E5-4AD7-9DBA-60DDE276644B}"
31+
EndProject
3032
Global
3133
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3234
Debug|Any CPU = Debug|Any CPU
@@ -73,6 +75,10 @@ Global
7375
{1502529E-47E9-4306-98C4-BF6CF7C7C275}.Debug|Any CPU.Build.0 = Debug|Any CPU
7476
{1502529E-47E9-4306-98C4-BF6CF7C7C275}.Release|Any CPU.ActiveCfg = Release|Any CPU
7577
{1502529E-47E9-4306-98C4-BF6CF7C7C275}.Release|Any CPU.Build.0 = Release|Any CPU
78+
{C647611B-A8E5-4AD7-9DBA-60DDE276644B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
79+
{C647611B-A8E5-4AD7-9DBA-60DDE276644B}.Debug|Any CPU.Build.0 = Debug|Any CPU
80+
{C647611B-A8E5-4AD7-9DBA-60DDE276644B}.Release|Any CPU.ActiveCfg = Release|Any CPU
81+
{C647611B-A8E5-4AD7-9DBA-60DDE276644B}.Release|Any CPU.Build.0 = Release|Any CPU
7682
EndGlobalSection
7783
GlobalSection(SolutionProperties) = preSolution
7884
HideSolutionNode = FALSE
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
//
4+
5+
using Microsoft.AspNetCore.Http;
6+
using Microsoft.Extensions.Logging;
7+
using Microsoft.FeatureManagement.FeatureFilters;
8+
using System;
9+
using System.Threading.Tasks;
10+
11+
namespace Microsoft.FeatureManagement
12+
{
13+
/// <summary>
14+
/// Used to add targeting information to HTTP context.
15+
/// </summary>
16+
public class TargetingHttpContextMiddleware
17+
{
18+
private readonly RequestDelegate _next;
19+
private readonly ILogger _logger;
20+
21+
private const string TargetingIdKey = $"Microsoft.FeatureManagement.TargetingId";
22+
23+
/// <summary>
24+
/// Creates an instance of the TargetingHttpContextMiddleware
25+
/// </summary>
26+
public TargetingHttpContextMiddleware(RequestDelegate next, ILoggerFactory loggerFactory) {
27+
_next = next ?? throw new ArgumentNullException(nameof(next));
28+
_logger = loggerFactory?.CreateLogger<TargetingHttpContextMiddleware>() ?? throw new ArgumentNullException(nameof(loggerFactory));
29+
}
30+
31+
/// <summary>
32+
/// Adds targeting information to the HTTP context.
33+
/// </summary>
34+
/// <param name="context">The <see cref="HttpContext"/> to add the targeting information to.</param>
35+
/// <param name="targetingContextAccessor">The <see cref="ITargetingContextAccessor"/> to retrieve the targeting information from.</param>
36+
/// <exception cref="ArgumentNullException">Thrown if the provided context or targetingContextAccessor is null.</exception>
37+
public async Task InvokeAsync(HttpContext context, ITargetingContextAccessor targetingContextAccessor)
38+
{
39+
if (context == null)
40+
{
41+
throw new ArgumentNullException(nameof(context));
42+
}
43+
44+
if (targetingContextAccessor == null)
45+
{
46+
throw new ArgumentNullException(nameof(targetingContextAccessor));
47+
}
48+
49+
TargetingContext targetingContext = await targetingContextAccessor.GetContextAsync().ConfigureAwait(false);
50+
51+
if (targetingContext != null)
52+
{
53+
context.Items[TargetingIdKey] = targetingContext.UserId;
54+
}
55+
else
56+
{
57+
_logger.LogDebug("The targeting context accessor returned a null TargetingContext");
58+
}
59+
60+
await _next(context);
61+
}
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<Import Project="..\..\build\NugetProperties.props" />
3+
4+
<!-- Official Version -->
5+
<PropertyGroup>
6+
<MajorVersion>4</MajorVersion>
7+
<MinorVersion>0</MinorVersion>
8+
<PatchVersion>0</PatchVersion>
9+
<PreviewVersion>-preview</PreviewVersion>
10+
</PropertyGroup>
11+
12+
<Import Project="..\..\build\Versioning.props" />
13+
14+
<PropertyGroup>
15+
<TargetFramework>net6.0</TargetFramework>
16+
<ImplicitUsings>enable</ImplicitUsings>
17+
<SignAssembly>true</SignAssembly>
18+
<DelaySign>false</DelaySign>
19+
<AssemblyOriginatorKeyFile>..\..\build\Microsoft.FeatureManagement.snk</AssemblyOriginatorKeyFile>
20+
</PropertyGroup>
21+
22+
<PropertyGroup>
23+
<Description>Microsoft.FeatureManagement.Telemetry.ApplicationInsights.AspNetCore provides a solution for tagging Application Insights telemetry with Microsoft.FeatureManagement targeting information.</Description>
24+
<Authors>Microsoft</Authors>
25+
<Company>Microsoft</Company>
26+
<PackageLicenseUrl>https://licenses.nuget.org/MIT</PackageLicenseUrl>
27+
<PackageProjectUrl>https://github.com/Azure/AppConfiguration</PackageProjectUrl>
28+
<PackageReleaseNotes>Release notes can be found at https://aka.ms/MicrosoftFeatureManagementReleaseNotes</PackageReleaseNotes>
29+
<PackageTags>Microsoft FeatureManagement FeatureFlags ApplicationInsights</PackageTags>
30+
<PackageIconUrl>https://aka.ms/AzureAppConfigurationPackageIcon</PackageIconUrl>
31+
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
32+
</PropertyGroup>
33+
34+
<ItemGroup>
35+
<PackageReference Include="Microsoft.ApplicationInsights" Version="2.22.0" />
36+
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.22.0" />
37+
</ItemGroup>
38+
39+
<ItemGroup>
40+
<ProjectReference Include="..\Microsoft.FeatureManagement\Microsoft.FeatureManagement.csproj" />
41+
</ItemGroup>
42+
43+
<PropertyGroup>
44+
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\XMLComments\$(MSBuildProjectName).xml</DocumentationFile>
45+
</PropertyGroup>
46+
47+
<Target Name="PrepublishScript" BeforeTargets="PrepareForPublish">
48+
<Copy SourceFiles="$(DocumentationFile)" DestinationFolder="$(OutDir)\XMLComments" SkipUnchangedFiles="false" />
49+
</Target>
50+
51+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
//
4+
5+
using Microsoft.ApplicationInsights.AspNetCore.TelemetryInitializers;
6+
using Microsoft.ApplicationInsights.Channel;
7+
using Microsoft.ApplicationInsights.DataContracts;
8+
using Microsoft.AspNetCore.Http;
9+
10+
namespace Microsoft.FeatureManagement.Telemetry.ApplicationInsights.AspNetCore
11+
{
12+
/// <summary>
13+
/// Used to add targeting information to outgoing Application Insights telemetry.
14+
/// </summary>
15+
public class TargetingTelemetryInitializer : TelemetryInitializerBase
16+
{
17+
private const string TargetingIdKey = $"Microsoft.FeatureManagement.TargetingId";
18+
19+
/// <summary>
20+
/// Creates an instance of the TargetingTelemetryInitializer
21+
/// </summary>
22+
public TargetingTelemetryInitializer(IHttpContextAccessor httpContextAccessor) : base(httpContextAccessor)
23+
{
24+
}
25+
26+
/// <summary>
27+
/// When telemetry is initialized, adds targeting information to all relevant telemetry.
28+
/// </summary>
29+
/// <param name="httpContext">The <see cref="HttpContext"/> to get the targeting information from.</param>
30+
/// <param name="requestTelemetry">The <see cref="RequestTelemetry"/> relevant to the telemetry.</param>
31+
/// <param name="telemetry">The <see cref="ITelemetry"/> to be initialized.</param>
32+
/// <exception cref="ArgumentNullException">Thrown if the any param is null.</exception>
33+
protected override void OnInitializeTelemetry(HttpContext httpContext, RequestTelemetry requestTelemetry, ITelemetry telemetry)
34+
{
35+
if (telemetry == null)
36+
{
37+
throw new ArgumentNullException("telemetry");
38+
}
39+
40+
if (httpContext == null)
41+
{
42+
throw new ArgumentNullException("httpContext");
43+
}
44+
45+
// Extract the targeting id from the http context
46+
string targetingId = null;
47+
48+
if (httpContext.Items.TryGetValue(TargetingIdKey, out object value))
49+
{
50+
targetingId = value?.ToString();
51+
}
52+
53+
if (!string.IsNullOrEmpty(targetingId))
54+
{
55+
// Telemetry.Properties is deprecated in favor of ISupportProperties
56+
if (telemetry is ISupportProperties telemetryWithSupportProperties)
57+
{
58+
telemetryWithSupportProperties.Properties["TargetingId"] = targetingId;
59+
}
60+
}
61+
}
62+
}
63+
}

0 commit comments

Comments
 (0)