Skip to content

Commit 9968fe8

Browse files
Merge pull request #142 from Azure/merge-main-to-preview
Merge main to preview
2 parents e036457 + b178046 commit 9968fe8

File tree

7 files changed

+330
-18
lines changed

7 files changed

+330
-18
lines changed

src/AzureAppConfigurationImpl.ts

Lines changed: 33 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

@@ -61,6 +64,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
6164
#options: AzureAppConfigurationOptions | undefined;
6265
#isInitialLoadCompleted: boolean = false;
6366
#isFailoverRequest: boolean = false;
67+
#featureFlagTracing: FeatureFlagTracingOptions | undefined;
6468

6569
// Refresh
6670
#refreshInProgress: boolean = false;
@@ -92,6 +96,9 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
9296

9397
// Enable request tracing if not opt-out
9498
this.#requestTracingEnabled = requestTracingEnabled();
99+
if (this.#requestTracingEnabled) {
100+
this.#featureFlagTracing = new FeatureFlagTracingOptions();
101+
}
95102

96103
if (options?.trimKeyPrefixes) {
97104
this.#sortedTrimKeyPrefixes = [...options.trimKeyPrefixes].sort((a, b) => b.localeCompare(a));
@@ -203,7 +210,8 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
203210
appConfigOptions: this.#options,
204211
initialLoadCompleted: this.#isInitialLoadCompleted,
205212
replicaCount: this.#clientManager.getReplicaCount(),
206-
isFailoverRequest: this.#isFailoverRequest
213+
isFailoverRequest: this.#isFailoverRequest,
214+
featureFlagTracing: this.#featureFlagTracing
207215
};
208216
}
209217

@@ -367,6 +375,10 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
367375

368376
const featureFlagSettings = await this.#executeWithFailoverPolicy(funcToExecute) as ConfigurationSetting[];
369377

378+
if (this.#requestTracingEnabled && this.#featureFlagTracing !== undefined) {
379+
this.#featureFlagTracing.resetFeatureFlagTracing();
380+
}
381+
370382
// parse feature flags
371383
const featureFlags = await Promise.all(
372384
featureFlagSettings.map(setting => this.#parseFeatureFlag(setting))
@@ -647,6 +659,25 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
647659
};
648660
}
649661

662+
if (this.#requestTracingEnabled && this.#featureFlagTracing !== undefined) {
663+
if (featureFlag[CONDITIONS_KEY_NAME] &&
664+
featureFlag[CONDITIONS_KEY_NAME][CLIENT_FILTERS_KEY_NAME] &&
665+
Array.isArray(featureFlag[CONDITIONS_KEY_NAME][CLIENT_FILTERS_KEY_NAME])) {
666+
for (const filter of featureFlag[CONDITIONS_KEY_NAME][CLIENT_FILTERS_KEY_NAME]) {
667+
this.#featureFlagTracing.updateFeatureFilterTracing(filter[NAME_KEY_NAME]);
668+
}
669+
}
670+
if (featureFlag[VARIANTS_KEY_NAME] && Array.isArray(featureFlag[VARIANTS_KEY_NAME])) {
671+
this.#featureFlagTracing.notifyMaxVariants(featureFlag[VARIANTS_KEY_NAME].length);
672+
}
673+
if (featureFlag[TELEMETRY_KEY_NAME] && featureFlag[TELEMETRY_KEY_NAME][ENABLED_KEY_NAME]) {
674+
this.#featureFlagTracing.usesTelemetry = true;
675+
}
676+
if (featureFlag[ALLOCATION_KEY_NAME] && featureFlag[ALLOCATION_KEY_NAME][SEED_KEY_NAME]) {
677+
this.#featureFlagTracing.usesSeed = true;
678+
}
679+
}
680+
650681
return featureFlag;
651682
}
652683

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
@@ -54,3 +54,16 @@ export const FAILOVER_REQUEST_TAG = "Failover";
5454
// Compact feature tags
5555
export const FEATURES_KEY = "Features";
5656
export const LOAD_BALANCE_CONFIGURED_TAG = "LB";
57+
58+
// Feature flag usage tracing
59+
export const FEATURE_FILTER_TYPE_KEY = "Filter";
60+
export const CUSTOM_FILTER_KEY = "CSTM";
61+
export const TIME_WINDOW_FILTER_KEY = "TIME";
62+
export const TARGETING_FILTER_KEY = "TRGT";
63+
64+
export const FF_TELEMETRY_USED_TAG = "Telemetry";
65+
export const FF_MAX_VARIANTS_KEY = "MaxVariants";
66+
export const FF_SEED_USED_TAG = "Seed";
67+
export const FF_FEATURES_KEY = "FFFeatures";
68+
69+
export const DELIMITER = "+";

src/requestTracing/utils.ts

Lines changed: 17 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,
@@ -32,6 +36,7 @@ export interface RequestTracingOptions {
3236
initialLoadCompleted: boolean;
3337
replicaCount: number;
3438
isFailoverRequest: boolean;
39+
featureFlagTracing: FeatureFlagTracingOptions | undefined;
3540
}
3641

3742
// Utils
@@ -78,6 +83,9 @@ export function createCorrelationContextHeader(requestTracingOptions: RequestTra
7883
Env: identify by env `NODE_ENV` which is a popular but not standard. Usually, the value can be "development", "production".
7984
ReplicaCount: identify how many replicas are found
8085
Features: LB
86+
Filter: CSTM+TIME+TRGT
87+
MaxVariants: identify the max number of variants feature flag uses
88+
FFFeatures: Seed+Telemetry
8189
UsersKeyVault
8290
Failover
8391
*/
@@ -96,6 +104,15 @@ export function createCorrelationContextHeader(requestTracingOptions: RequestTra
96104
}
97105
}
98106

107+
const featureFlagTracing = requestTracingOptions.featureFlagTracing;
108+
if (featureFlagTracing) {
109+
keyValues.set(FEATURE_FILTER_TYPE_KEY, featureFlagTracing.usesAnyFeatureFilter() ? featureFlagTracing.createFeatureFiltersString() : undefined);
110+
keyValues.set(FF_FEATURES_KEY, featureFlagTracing.usesAnyTracingFeature() ? featureFlagTracing.createFeaturesString() : undefined);
111+
if (featureFlagTracing.maxVariants > 0) {
112+
keyValues.set(FF_MAX_VARIANTS_KEY, featureFlagTracing.maxVariants.toString());
113+
}
114+
}
115+
99116
if (requestTracingOptions.isFailoverRequest) {
100117
tags.push(FAILOVER_REQUEST_TAG);
101118
}

0 commit comments

Comments
 (0)