Skip to content

Commit

Permalink
add request tracing for cdn
Browse files Browse the repository at this point in the history
  • Loading branch information
zhiyuanliang-ms committed Nov 10, 2024
1 parent 14364c5 commit 62ac2b9
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 14 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion src/AzureAppConfigurationImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
#client: AppConfigurationClient;
#clientEndpoint: string | undefined;
#options: AzureAppConfigurationOptions | undefined;
#isCdnUsed: boolean;
#isInitialLoadCompleted: boolean = false;

// Refresh
Expand All @@ -80,11 +81,13 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
constructor(
client: AppConfigurationClient,
clientEndpoint: string | undefined,
options: AzureAppConfigurationOptions | undefined
options: AzureAppConfigurationOptions | undefined,
isCdnUsed: boolean
) {
this.#client = client;
this.#clientEndpoint = clientEndpoint;
this.#options = options;
this.#isCdnUsed = isCdnUsed;

// Enable request tracing if not opt-out
this.#requestTracingEnabled = requestTracingEnabled();
Expand Down Expand Up @@ -197,6 +200,7 @@ export class AzureAppConfigurationImpl implements AzureAppConfiguration {
return {
requestTracingEnabled: this.#requestTracingEnabled,
initialLoadCompleted: this.#isInitialLoadCompleted,
isCdnUsed: this.#isCdnUsed,
appConfigOptions: this.#options
};
}
Expand Down
11 changes: 6 additions & 5 deletions src/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import * as RequestTracing from "./requestTracing/constants.js";

const MIN_DELAY_FOR_UNHANDLED_ERROR: number = 5000; // 5 seconds

// Empty token credential to be used when loading from CDN
const emptyTokenCredential: TokenCredential = {
getToken: async () => ({ token: "", expiresOnTimestamp: 0 })
};

/**
* Loads the data from Azure App Configuration service and returns an instance of AzureAppConfiguration.
* @param connectionString The connection string for the App Configuration store.
Expand Down Expand Up @@ -67,7 +72,7 @@ export async function load(
}

try {
const appConfiguration = new AzureAppConfigurationImpl(client, clientEndpoint, options);
const appConfiguration = new AzureAppConfigurationImpl(client, clientEndpoint, options, credentialOrOptions === emptyTokenCredential);
await appConfiguration.load();
return appConfiguration;
} catch (error) {
Expand All @@ -93,10 +98,6 @@ export async function loadFromCdn(
cdnEndpoint: string | URL,
appConfigOptions?: AzureAppConfigurationOptions
): Promise<AzureAppConfiguration> {
const emptyTokenCredential: TokenCredential = {
getToken: async () => ({ token: "", expiresOnTimestamp: 0 })
};

if (appConfigOptions === undefined) {
appConfigOptions = { clientOptions: {}};
}
Expand Down
1 change: 1 addition & 0 deletions src/requestTracing/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@ export enum RequestType {

// Tag names
export const KEY_VAULT_CONFIGURED_TAG = "UsesKeyVault";
export const CDN_USED_TAG = "CDN";
16 changes: 11 additions & 5 deletions src/requestTracing/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
HOST_TYPE_KEY,
HostType,
KEY_VAULT_CONFIGURED_TAG,
CDN_USED_TAG,
KUBERNETES_ENV_VAR,
NODEJS_DEV_ENV_VAL,
NODEJS_ENV_VAR,
Expand All @@ -27,18 +28,19 @@ export function listConfigurationSettingsWithTrace(
requestTracingOptions: {
requestTracingEnabled: boolean;
initialLoadCompleted: boolean;
isCdnUsed: boolean;
appConfigOptions: AzureAppConfigurationOptions | undefined;
},
client: AppConfigurationClient,
listOptions: ListConfigurationSettingsOptions
) {
const { requestTracingEnabled, initialLoadCompleted, appConfigOptions } = requestTracingOptions;
const { requestTracingEnabled, initialLoadCompleted, isCdnUsed, appConfigOptions } = requestTracingOptions;

const actualListOptions = { ...listOptions };
if (requestTracingEnabled) {
actualListOptions.requestOptions = {
customHeaders: {
[CORRELATION_CONTEXT_HEADER_NAME]: createCorrelationContextHeader(appConfigOptions, initialLoadCompleted)
[CORRELATION_CONTEXT_HEADER_NAME]: createCorrelationContextHeader(appConfigOptions, initialLoadCompleted, isCdnUsed)
}
};
}
Expand All @@ -50,27 +52,28 @@ export function getConfigurationSettingWithTrace(
requestTracingOptions: {
requestTracingEnabled: boolean;
initialLoadCompleted: boolean;
isCdnUsed: boolean;
appConfigOptions: AzureAppConfigurationOptions | undefined;
},
client: AppConfigurationClient,
configurationSettingId: ConfigurationSettingId,
getOptions?: GetConfigurationSettingOptions,
) {
const { requestTracingEnabled, initialLoadCompleted, appConfigOptions } = requestTracingOptions;
const { requestTracingEnabled, initialLoadCompleted, isCdnUsed, appConfigOptions } = requestTracingOptions;
const actualGetOptions = { ...getOptions };

if (requestTracingEnabled) {
actualGetOptions.requestOptions = {
customHeaders: {
[CORRELATION_CONTEXT_HEADER_NAME]: createCorrelationContextHeader(appConfigOptions, initialLoadCompleted)
[CORRELATION_CONTEXT_HEADER_NAME]: createCorrelationContextHeader(appConfigOptions, initialLoadCompleted, isCdnUsed)
}
};
}

return client.getConfigurationSetting(configurationSettingId, actualGetOptions);
}

export function createCorrelationContextHeader(options: AzureAppConfigurationOptions | undefined, isInitialLoadCompleted: boolean): string {
export function createCorrelationContextHeader(options: AzureAppConfigurationOptions | undefined, isInitialLoadCompleted: boolean, isCdnUsed: boolean): string {
/*
RequestType: 'Startup' during application starting up, 'Watch' after startup completed.
Host: identify with defined envs
Expand All @@ -89,6 +92,9 @@ export function createCorrelationContextHeader(options: AzureAppConfigurationOpt
tags.push(KEY_VAULT_CONFIGURED_TAG);
}
}
if (isCdnUsed) {
tags.push(CDN_USED_TAG);
}

const contextParts: string[] = [];
for (const [k, v] of keyValues) {
Expand Down
2 changes: 1 addition & 1 deletion test/exportedApi.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

export { load } from "../src";
export { load, loadFromCdn } from "../src";
14 changes: 13 additions & 1 deletion test/requestTracing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as chaiAsPromised from "chai-as-promised";
chai.use(chaiAsPromised);
const expect = chai.expect;
import { createMockedConnectionString, createMockedKeyValue, createMockedTokenCredential, mockAppConfigurationClientListConfigurationSettings, restoreMocks, sleepInMs } from "./utils/testHelper.js";
import { load } from "./exportedApi.js";
import { load, loadFromCdn } from "./exportedApi.js";

class HttpRequestHeadersPolicy {
headers: any;
Expand Down Expand Up @@ -77,6 +77,18 @@ describe("request tracing", function () {
expect(correlationContext.includes("UsesKeyVault")).eq(true);
});

it("should have cdn tag in correlation-context header", async () => {
try {
await loadFromCdn(fakeEndpoint, {
clientOptions
});
} catch (e) { /* empty */ }
expect(headerPolicy.headers).not.undefined;
const correlationContext = headerPolicy.headers.get("Correlation-Context");
expect(correlationContext).not.undefined;
expect(correlationContext.includes("CDN")).eq(true);
});

it("should detect env in correlation-context header", async () => {
process.env.NODE_ENV = "development";
try {
Expand Down

0 comments on commit 62ac2b9

Please sign in to comment.