Skip to content

Commit ea7709e

Browse files
merge
2 parents 2b95ab4 + 290d338 commit ea7709e

File tree

7 files changed

+327
-18
lines changed

7 files changed

+327
-18
lines changed

src/AzureAppConfigurationImpl.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,14 @@ import {
2929
SEED_KEY_NAME,
3030
VARIANT_KEY_NAME,
3131
VARIANTS_KEY_NAME,
32-
CONFIGURATION_VALUE_KEY_NAME
32+
CONFIGURATION_VALUE_KEY_NAME,
33+
CONDITIONS_KEY_NAME,
34+
CLIENT_FILTERS_KEY_NAME
3335
} from "./featureManagement/constants.js";
3436
import { AzureKeyVaultKeyValueAdapter } from "./keyvault/AzureKeyVaultKeyValueAdapter.js";
3537
import { RefreshTimer } from "./refresh/RefreshTimer.js";
3638
import { RequestTracingOptions, getConfigurationSettingWithTrace, listConfigurationSettingsWithTrace, requestTracingEnabled } from "./requestTracing/utils.js";
39+
import { FeatureFlagTracingOptions } from "./requestTracing/FeatureFlagTracingOptions.js";
3740
import { KeyFilter, LabelFilter, SettingSelector } from "./types.js";
3841
import { ConfigurationClientManager } from "./ConfigurationClientManager.js";
3942
import { ETAG_LOOKUP_HEADER } from "./EtagUrlPipelinePolicy.js";
@@ -69,6 +72,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
6972
#options: AzureAppConfigurationOptions | undefined;
7073
#isInitialLoadCompleted: boolean = false;
7174
#isFailoverRequest: boolean = false;
75+
#featureFlagTracing: FeatureFlagTracingOptions | undefined;
7276

7377
// Refresh
7478
#refreshInProgress: boolean = false;
@@ -112,6 +116,9 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
112116

113117
// enable request tracing if not opt-out
114118
this.#requestTracingEnabled = requestTracingEnabled();
119+
if (this.#requestTracingEnabled) {
120+
this.#featureFlagTracing = new FeatureFlagTracingOptions();
121+
}
115122

116123
if (options?.trimKeyPrefixes) {
117124
this.#sortedTrimKeyPrefixes = [...options.trimKeyPrefixes].sort((a, b) => b.localeCompare(a));
@@ -190,7 +197,8 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
190197
initialLoadCompleted: this.#isInitialLoadCompleted,
191198
replicaCount: this.#clientManager.getReplicaCount(),
192199
isFailoverRequest: this.#isFailoverRequest,
193-
isCdnUsed: this.#isCdnUsed
200+
isCdnUsed: this.#isCdnUsed,
201+
featureFlagTracing: this.#featureFlagTracing
194202
};
195203
}
196204

@@ -740,6 +748,25 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
740748
};
741749
}
742750

751+
if (this.#requestTracingEnabled && this.#featureFlagTracing !== undefined) {
752+
if (featureFlag[CONDITIONS_KEY_NAME] &&
753+
featureFlag[CONDITIONS_KEY_NAME][CLIENT_FILTERS_KEY_NAME] &&
754+
Array.isArray(featureFlag[CONDITIONS_KEY_NAME][CLIENT_FILTERS_KEY_NAME])) {
755+
for (const filter of featureFlag[CONDITIONS_KEY_NAME][CLIENT_FILTERS_KEY_NAME]) {
756+
this.#featureFlagTracing.updateFeatureFilterTracing(filter[NAME_KEY_NAME]);
757+
}
758+
}
759+
if (featureFlag[VARIANTS_KEY_NAME] && Array.isArray(featureFlag[VARIANTS_KEY_NAME])) {
760+
this.#featureFlagTracing.notifyMaxVariants(featureFlag[VARIANTS_KEY_NAME].length);
761+
}
762+
if (featureFlag[TELEMETRY_KEY_NAME] && featureFlag[TELEMETRY_KEY_NAME][ENABLED_KEY_NAME]) {
763+
this.#featureFlagTracing.usesTelemetry = true;
764+
}
765+
if (featureFlag[ALLOCATION_KEY_NAME] && featureFlag[ALLOCATION_KEY_NAME][SEED_KEY_NAME]) {
766+
this.#featureFlagTracing.usesSeed = true;
767+
}
768+
}
769+
743770
return featureFlag;
744771
}
745772

src/featureManagement/constants.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,8 @@ export const VARIANT_KEY_NAME = "variant";
2020
export const VARIANTS_KEY_NAME = "variants";
2121
export const CONFIGURATION_VALUE_KEY_NAME = "configuration_value";
2222
export const ALLOCATION_ID_KEY_NAME = "AllocationId";
23+
export const CONDITIONS_KEY_NAME = "conditions";
24+
export const CLIENT_FILTERS_KEY_NAME = "client_filters";
25+
26+
export const TIME_WINDOW_FILTER_NAMES = ["TimeWindow", "Microsoft.TimeWindow", "TimeWindowFilter", "Microsoft.TimeWindowFilter"];
27+
export const TARGETING_FILTER_NAMES = ["Targeting", "Microsoft.Targeting", "TargetingFilter", "Microsoft.TargetingFilter"];
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
import { TIME_WINDOW_FILTER_NAMES, TARGETING_FILTER_NAMES } from "../featureManagement/constants.js";
5+
import { CUSTOM_FILTER_KEY, TIME_WINDOW_FILTER_KEY, TARGETING_FILTER_KEY, FF_SEED_USED_TAG, FF_TELEMETRY_USED_TAG, DELIMITER } from "./constants.js";
6+
7+
/**
8+
* Tracing for tracking feature flag usage.
9+
*/
10+
export class FeatureFlagTracingOptions {
11+
/**
12+
* Built-in feature filter usage.
13+
*/
14+
usesCustomFilter: boolean = false;
15+
usesTimeWindowFilter: boolean = false;
16+
usesTargetingFilter: boolean = false;
17+
usesTelemetry: boolean = false;
18+
usesSeed: boolean = false;
19+
maxVariants: number = 0;
20+
21+
resetFeatureFlagTracing(): void {
22+
this.usesCustomFilter = false;
23+
this.usesTimeWindowFilter = false;
24+
this.usesTargetingFilter = false;
25+
this.usesTelemetry = false;
26+
this.usesSeed = false;
27+
this.maxVariants = 0;
28+
}
29+
30+
updateFeatureFilterTracing(filterName: string): void {
31+
if (TIME_WINDOW_FILTER_NAMES.some(name => name === filterName)) {
32+
this.usesTimeWindowFilter = true;
33+
} else if (TARGETING_FILTER_NAMES.some(name => name === filterName)) {
34+
this.usesTargetingFilter = true;
35+
} else {
36+
this.usesCustomFilter = true;
37+
}
38+
}
39+
40+
notifyMaxVariants(currentFFTotalVariants: number): void {
41+
if (currentFFTotalVariants > this.maxVariants) {
42+
this.maxVariants = currentFFTotalVariants;
43+
}
44+
}
45+
46+
usesAnyFeatureFilter(): boolean {
47+
return this.usesCustomFilter || this.usesTimeWindowFilter || this.usesTargetingFilter;
48+
}
49+
50+
usesAnyTracingFeature() {
51+
return this.usesSeed || this.usesTelemetry;
52+
}
53+
54+
createFeatureFiltersString(): string {
55+
if (!this.usesAnyFeatureFilter()) {
56+
return "";
57+
}
58+
59+
let result: string = "";
60+
if (this.usesCustomFilter) {
61+
result += CUSTOM_FILTER_KEY;
62+
}
63+
if (this.usesTimeWindowFilter) {
64+
if (result !== "") {
65+
result += DELIMITER;
66+
}
67+
result += TIME_WINDOW_FILTER_KEY;
68+
}
69+
if (this.usesTargetingFilter) {
70+
if (result !== "") {
71+
result += DELIMITER;
72+
}
73+
result += TARGETING_FILTER_KEY;
74+
}
75+
return result;
76+
}
77+
78+
createFeaturesString(): string {
79+
if (!this.usesAnyTracingFeature()) {
80+
return "";
81+
}
82+
83+
let result: string = "";
84+
if (this.usesSeed) {
85+
result += FF_SEED_USED_TAG;
86+
}
87+
if (this.usesTelemetry) {
88+
if (result !== "") {
89+
result += DELIMITER;
90+
}
91+
result += FF_TELEMETRY_USED_TAG;
92+
}
93+
return result;
94+
}
95+
}

src/requestTracing/constants.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,16 @@ export const FAILOVER_REQUEST_TAG = "Failover";
5555
// Compact feature tags
5656
export const FEATURES_KEY = "Features";
5757
export const LOAD_BALANCE_CONFIGURED_TAG = "LB";
58+
59+
// Feature flag usage tracing
60+
export const FEATURE_FILTER_TYPE_KEY = "Filter";
61+
export const CUSTOM_FILTER_KEY = "CSTM";
62+
export const TIME_WINDOW_FILTER_KEY = "TIME";
63+
export const TARGETING_FILTER_KEY = "TRGT";
64+
65+
export const FF_TELEMETRY_USED_TAG = "Telemetry";
66+
export const FF_MAX_VARIANTS_KEY = "MaxVariants";
67+
export const FF_SEED_USED_TAG = "Seed";
68+
export const FF_FEATURES_KEY = "FFFeatures";
69+
70+
export const DELIMITER = "+";

src/requestTracing/utils.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33

44
import { AppConfigurationClient, ConfigurationSettingId, GetConfigurationSettingOptions, ListConfigurationSettingsOptions } from "@azure/app-configuration";
55
import { AzureAppConfigurationOptions } from "../AzureAppConfigurationOptions.js";
6+
import { FeatureFlagTracingOptions } from "./FeatureFlagTracingOptions.js";
67
import {
78
AZURE_FUNCTION_ENV_VAR,
89
AZURE_WEB_APP_ENV_VAR,
910
CONTAINER_APP_ENV_VAR,
1011
DEV_ENV_VAL,
1112
ENV_AZURE_APP_CONFIGURATION_TRACING_DISABLED,
1213
ENV_KEY,
14+
FEATURE_FILTER_TYPE_KEY,
15+
FF_MAX_VARIANTS_KEY,
16+
FF_FEATURES_KEY,
1317
HOST_TYPE_KEY,
1418
HostType,
1519
KEY_VAULT_CONFIGURED_TAG,
@@ -34,6 +38,7 @@ export interface RequestTracingOptions {
3438
replicaCount: number;
3539
isFailoverRequest: boolean;
3640
isCdnUsed: boolean;
41+
featureFlagTracing: FeatureFlagTracingOptions | undefined;
3742
}
3843

3944
// Utils
@@ -82,6 +87,9 @@ export function createCorrelationContextHeader(requestTracingOptions: RequestTra
8287
Env: identify by env `NODE_ENV` which is a popular but not standard. Usually, the value can be "development", "production".
8388
ReplicaCount: identify how many replicas are found
8489
Features: LB
90+
Filter: CSTM+TIME+TRGT
91+
MaxVariants: identify the max number of variants feature flag uses
92+
FFFeatures: Seed+Telemetry
8593
UsersKeyVault
8694
Failover
8795
CDN
@@ -100,6 +108,16 @@ export function createCorrelationContextHeader(requestTracingOptions: RequestTra
100108
tags.push(KEY_VAULT_CONFIGURED_TAG);
101109
}
102110
}
111+
112+
const featureFlagTracing = requestTracingOptions.featureFlagTracing;
113+
if (featureFlagTracing) {
114+
keyValues.set(FEATURE_FILTER_TYPE_KEY, featureFlagTracing.usesAnyFeatureFilter() ? featureFlagTracing.createFeatureFiltersString() : undefined);
115+
keyValues.set(FF_FEATURES_KEY, featureFlagTracing.usesAnyTracingFeature() ? featureFlagTracing.createFeaturesString() : undefined);
116+
if (featureFlagTracing.maxVariants > 0) {
117+
keyValues.set(FF_MAX_VARIANTS_KEY, featureFlagTracing.maxVariants.toString());
118+
}
119+
}
120+
103121
if (requestTracingOptions.isFailoverRequest) {
104122
tags.push(FAILOVER_REQUEST_TAG);
105123
}

0 commit comments

Comments
 (0)