Skip to content

Commit

Permalink
Add Feature Filter Telemetry (#332)
Browse files Browse the repository at this point in the history
  • Loading branch information
avanigupta authored Jul 14, 2022
1 parent 01f2115 commit 93d8513
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ internal IEnumerable<IKeyValueAdapter> Adapters
/// </summary>
internal bool IsKeyVaultRefreshConfigured { get; private set; } = false;

/// <summary>
/// Indicates all types of feature filters used by the application.
/// </summary>
internal FeatureFilterTelemetry FeatureFilterTelemetry { get; set; } = new FeatureFilterTelemetry();

/// <summary>
/// Specify what key-values to include in the configuration provider.
/// <see cref="Select"/> can be called multiple times to include multiple sets of key-values.
Expand Down Expand Up @@ -207,7 +212,7 @@ public AzureAppConfigurationOptions UseFeatureFlags(Action<FeatureFlagOptions> c

if (!_adapters.Any(a => a is FeatureManagementKeyValueAdapter))
{
_adapters.Add(new FeatureManagementKeyValueAdapter());
_adapters.Add(new FeatureManagementKeyValueAdapter(FeatureFilterTelemetry));
}

return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,9 @@ private async Task SetData(IDictionary<string, ConfigurationSetting> data, bool
// Set the application data for the configuration provider
var applicationData = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

// Reset old filter telemetry in order to track the filter types present in the current response from server.
_options.FeatureFilterTelemetry.ResetFeatureFilterTelemetry();

foreach (KeyValuePair<string, ConfigurationSetting> kvp in data)
{
IEnumerable<KeyValuePair<string, string>> keyValuePairs = null;
Expand Down Expand Up @@ -695,7 +698,8 @@ private void SetRequestTracingOptions()
HostType = TracingUtils.GetHostType(),
IsDevEnvironment = TracingUtils.IsDevEnvironment(),
IsKeyVaultConfigured = _options.IsKeyVaultConfigured,
IsKeyVaultRefreshConfigured = _options.IsKeyVaultRefreshConfigured
IsKeyVaultRefreshConfigured = _options.IsKeyVaultRefreshConfigured,
FilterTelemetry = _options.FeatureFilterTelemetry
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ internal class RequestTracingConstants

public const string RequestTypeKey = "RequestType";
public const string HostTypeKey = "Host";
public const string FilterTypeKey = "Filter";
public const string EnvironmentKey = "Env";
public const string DevEnvironmentValue = "Dev";
public const string KeyVaultConfiguredTag = "UsesKeyVault";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManagement
{
/// <summary>
/// Telemetry for tracking built-in feature filter usage.
/// </summary>
internal class FeatureFilterTelemetry
{
private const string CustomFilter = "CSTM";
private const string PercentageFilter = "PRCNT";
private const string TimeWindowFilter = "TIME";
private const string TargetingFilter = "TRGT";
private const string FilterTypeDelimiter = "+";

// Built-in Feature Filter Names
private readonly List<string> PercentageFilterNames = new List<string> { "Percentage", "Microsoft.Percentage", "PercentageFilter", "Microsoft.PercentageFilter" };
private readonly List<string> TimeWindowFilterNames = new List<string> { "TimeWindow", "Microsoft.TimeWindow", "TimeWindowFilter", "Microsoft.TimeWindowFilter" };
private readonly List<string> TargetingFilterNames = new List<string> { "Targeting", "Microsoft.Targeting", "TargetingFilter", "Microsoft.TargetingFilter" };

public bool UsesCustomFilter { get; set; } = false;
public bool UsesPercentageFilter { get; set; } = false;
public bool UsesTimeWindowFilter { get; set; } = false;
public bool UsesTargetingFilter { get; set; } = false;

public bool UsesAnyFeatureFilter()
{
return UsesCustomFilter || UsesPercentageFilter || UsesTimeWindowFilter || UsesTargetingFilter;
}

public void ResetFeatureFilterTelemetry()
{
UsesCustomFilter = false;
UsesPercentageFilter = false;
UsesTimeWindowFilter = false;
UsesTargetingFilter = false;
}

public void UpdateFeatureFilterTelemetry(string filterName)
{
if (PercentageFilterNames.Any(name => string.Equals(name, filterName, StringComparison.OrdinalIgnoreCase)))
{
UsesPercentageFilter = true;
}
else if (TimeWindowFilterNames.Any(name => string.Equals(name, filterName, StringComparison.OrdinalIgnoreCase)))
{
UsesTimeWindowFilter = true;
}
else if (TargetingFilterNames.Any(name => string.Equals(name, filterName, StringComparison.OrdinalIgnoreCase)))
{
UsesTargetingFilter = true;
}
else
{
UsesCustomFilter = true;
}
}

/// <summary>
/// Returns a formatted string containing code names, indicating which feature filters are used by the application.
/// </summary>
/// <returns>Formatted string like: "CSTM+PRCNT+TIME+TRGT", "PRCNT+TRGT", etc. If no filters are used, empty string will be returned.</returns>
public override string ToString()
{
if (!UsesAnyFeatureFilter())
{
return string.Empty;
}

var sb = new StringBuilder();

if (UsesCustomFilter)
{
sb.Append(CustomFilter);
}

if (UsesPercentageFilter)
{
if (sb.Length > 0)
{
sb.Append(FilterTypeDelimiter);
}

sb.Append(PercentageFilter);
}

if (UsesTimeWindowFilter)
{
if (sb.Length > 0)
{
sb.Append(FilterTypeDelimiter);
}

sb.Append(TimeWindowFilter);
}

if (UsesTargetingFilter)
{
if (sb.Length > 0)
{
sb.Append(FilterTypeDelimiter);
}

sb.Append(TargetingFilter);
}

return sb.ToString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManage
{
internal class FeatureManagementKeyValueAdapter : IKeyValueAdapter
{
private FeatureFilterTelemetry _ffTelemetry;

public FeatureManagementKeyValueAdapter(FeatureFilterTelemetry ffTelemetry)
{
_ffTelemetry = ffTelemetry ?? throw new ArgumentNullException(nameof(ffTelemetry));
}

public Task<IEnumerable<KeyValuePair<string, string>>> ProcessKeyValue(ConfigurationSetting setting, CancellationToken cancellationToken)
{
FeatureFlag featureFlag;
Expand Down Expand Up @@ -44,6 +51,8 @@ public Task<IEnumerable<KeyValuePair<string, string>>> ProcessKeyValue(Configura
{
ClientFilter clientFilter = featureFlag.Conditions.ClientFilters[i];

_ffTelemetry.UpdateFeatureFilterTelemetry(clientFilter.Name);

keyValues.Add(new KeyValuePair<string, string>($"{FeatureManagementConstants.SectionName}:{featureFlag.Id}:{FeatureManagementConstants.EnabledFor}:{i}:Name", clientFilter.Name));

foreach (KeyValuePair<string, string> kvp in new JsonFlattener().FlattenJson(clientFilter.Parameters))
Expand Down Expand Up @@ -78,5 +87,6 @@ public bool NeedsRefresh()
{
return false;
}

}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
using Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManagement;

namespace Microsoft.Extensions.Configuration.AzureAppConfiguration
{
internal class RequestTracingOptions
Expand All @@ -24,5 +26,10 @@ internal class RequestTracingOptions
/// Flag to indicate whether the request is from a development environment.
/// </summary>
public bool IsDevEnvironment { get; set; } = false;

/// <summary>
/// Type of feature filters used by the application.
/// </summary>
public FeatureFilterTelemetry FilterTelemetry { get; set; } = new FeatureFilterTelemetry();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
using Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManagement;
using System;
using System.Collections.Generic;
using System.Diagnostics;
Expand Down Expand Up @@ -114,6 +115,11 @@ private static string CreateCorrelationContextHeader(RequestType requestType, Re
correlationContextKeyValues.Add(new KeyValuePair<string, string>(RequestTracingConstants.EnvironmentKey, RequestTracingConstants.DevEnvironmentValue));
}

if (requestTracingOptions.FilterTelemetry.UsesAnyFeatureFilter())
{
correlationContextKeyValues.Add(new KeyValuePair<string, string>(RequestTracingConstants.FilterTypeKey, requestTracingOptions.FilterTelemetry.ToString()));
}

if (requestTracingOptions.IsKeyVaultConfigured)
{
correlationContextTags.Add(RequestTracingConstants.KeyVaultConfiguredTag);
Expand Down

0 comments on commit 93d8513

Please sign in to comment.