Skip to content

Commit

Permalink
iKey env variable deprecation (#952)
Browse files Browse the repository at this point in the history
  • Loading branch information
hectorhdzg authored Apr 25, 2022
1 parent 5c72f4e commit 45357b7
Show file tree
Hide file tree
Showing 12 changed files with 326 additions and 279 deletions.
33 changes: 15 additions & 18 deletions Bootstrap/Default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,22 @@ import azureCore = require("@azure/core-http");
import * as types from "../applicationinsights";
import * as Helpers from "./Helpers";
import Constants = require("../Declarations/Constants");
import { StatusLogger, StatusContract } from "./StatusLogger";
import { StatusLogger } from "./StatusLogger";
import { DiagnosticLogger } from "./DiagnosticLogger";
import { JsonConfig } from "../Library/JsonConfig";
import Config = require("../Library/Config");

// Private configuration vars
let _appInsights: typeof types | null;
let _prefix = "ad_"; // App Services, Default
let _logger: DiagnosticLogger = new DiagnosticLogger(console);
let _statusLogger: StatusLogger = new StatusLogger(console);

export const defaultConfig = new Config(); // Will read env variables, expose for Agent initialization
const _instrumentationKey = defaultConfig.instrumentationKey;
let _logger: DiagnosticLogger = new DiagnosticLogger(console, _instrumentationKey);
let _statusLogger: StatusLogger = new StatusLogger(console, _instrumentationKey);

// Env var local constants
const _setupString = JsonConfig.getInstance().connectionString || process.env.APPINSIGHTS_INSTRUMENTATIONKEY;
const forceStart = process.env.APPLICATIONINSIGHTS_FORCE_START === "true";

// Other local constants
const defaultStatus: StatusContract = {
...StatusLogger.DEFAULT_STATUS,
Ikey: _setupString
};

/**
* Sets the attach-time logger
Expand All @@ -45,13 +42,13 @@ export function setStatusLogger(statusLogger: StatusLogger) {

/**
* Try to setup and start this app insights instance if attach is enabled.
* @param setupString connection string or instrumentation key
* @param setupString connection string
*/
export function setupAndStart(setupString = _setupString, aadTokenCredential?: azureCore.TokenCredential): typeof types | null {
export function setupAndStart(setupString?: string, aadTokenCredential?: azureCore.TokenCredential): typeof types | null {
// If app already contains SDK, skip agent attach
if (!forceStart && Helpers.sdkAlreadyExists(_logger)) {
_statusLogger.logStatus({
...defaultStatus,
...StatusLogger.DEFAULT_STATUS,
AgentInitializedSuccessfully: false,
SDKPresent: true,
Reason: "SDK already exists"
Expand All @@ -60,10 +57,10 @@ export function setupAndStart(setupString = _setupString, aadTokenCredential?: a
}

if (!setupString) {
const message = "Application Insights wanted to be started, but no Connection String or Instrumentation Key was provided";
const message = "Application Insights wanted to be started, but no Connection String was provided";
_logger.logError(message);
_statusLogger.logStatus({
...defaultStatus,
...StatusLogger.DEFAULT_STATUS,
AgentInitializedSuccessfully: false,
Reason: message
});
Expand Down Expand Up @@ -108,7 +105,7 @@ export function setupAndStart(setupString = _setupString, aadTokenCredential?: a
_logger.logMessage("Using AAD Token Credential");
_appInsights.defaultClient.config.aadTokenCredential = aadTokenCredential;
}

_appInsights.start();
// Add attach flag in Statsbeat
let statsbeat = _appInsights.defaultClient.getStatsbeat();
Expand All @@ -119,13 +116,13 @@ export function setupAndStart(setupString = _setupString, aadTokenCredential?: a
// Agent successfully instrumented the SDK
_logger.logMessage("Application Insights was started with setupString: " + setupString);
_statusLogger.logStatus({
...defaultStatus,
...StatusLogger.DEFAULT_STATUS,
AgentInitializedSuccessfully: true
});
} catch (e) {
_logger.logError("Error setting up Application Insights", e);
_statusLogger.logStatus({
...defaultStatus,
...StatusLogger.DEFAULT_STATUS,
AgentInitializedSuccessfully: false,
Reason: `Error setting up Application Insights: ${e && e.message}`
})
Expand Down
6 changes: 4 additions & 2 deletions Bootstrap/DiagnosticLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ export class DiagnosticLogger {
language: "nodejs",
operation: "Startup",
siteName: process.env.WEBSITE_SITE_NAME,
ikey: process.env.APPINSIGHTS_INSTRUMENTATIONKEY,
ikey: "unknown",
extensionVersion: process.env.ApplicationInsightsAgent_EXTENSION_VERSION,
sdkVersion: APPLICATION_INSIGHTS_SDK_VERSION,
subscriptionId: process.env.WEBSITE_OWNER_NAME ? process.env.WEBSITE_OWNER_NAME.split("+")[0] : null
}
}

constructor(private _writer: DataModel.AgentLogger = console) { }
constructor(private _writer: DataModel.AgentLogger = console, instrumentationKey: string = "unknown") {
DiagnosticLogger.DefaultEnvelope.properties.ikey = instrumentationKey;
}

logMessage(message: DataModel.DiagnosticLog | string, cb?: (err: Error) => void) {
if (typeof cb === "function" && this._writer instanceof FileWriter) {
Expand Down
4 changes: 3 additions & 1 deletion Bootstrap/StatusLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ export class StatusLogger {
PID: String(process.pid)
}

constructor(public _writer: DataModel.AgentLogger = console) {}
constructor(public _writer: DataModel.AgentLogger = console, instrumentationKey: string = "unknown") {
StatusLogger.DEFAULT_STATUS.Ikey = instrumentationKey;
}

public logStatus(data: StatusContract, cb?: (err: Error) => void) {
if (typeof cb === "function" && this._writer instanceof FileWriter) {
Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio
# How to contribute to the Application Insights Node.js SDK

1. Install all dependencies with `npm install`.
2. Set an environment variable to your instrumentation key (optional).
2. Set an environment variable to your Connection String (optional).
```bash
// windows
set APPINSIGHTS_INSTRUMENTATIONKEY=<insert_your_instrumentation_key_here>
set APPINSIGHTS_INSTRUAPPLICATIONINSIGHTS_CONNECTION_STRINGMENTATIONKEY=<YOUR_CONNECTION_STRING>
// linux/macos
export APPINSIGHTS_INSTRUMENTATIONKEY=<insert_your_instrumentation_key_here>
export APPLICATIONINSIGHTS_CONNECTION_STRING=<YOUR_CONNECTION_STRING>
```
3. Run tests
```bash
Expand Down
24 changes: 9 additions & 15 deletions Library/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class Config implements IConfig {
private _setCorrelationId: (v: string) => void;
private _profileQueryEndpoint: string;
private _instrumentationKey: string;



constructor(setupString?: string) {
Expand All @@ -77,7 +77,13 @@ class Config implements IConfig {
? null // CS was valid but instrumentation key was not provided, null and grab from env var
: setupString; // CS was invalid, so it must be an ikey

this.instrumentationKey = csCode.instrumentationkey || iKeyCode /* === instrumentationKey */ || csEnv.instrumentationkey || Config._getInstrumentationKey();
const instrumentationKeyEnv: string | undefined = this._instrumentationKey;
this.instrumentationKey = csCode.instrumentationkey || iKeyCode /* === instrumentationKey */ || csEnv.instrumentationkey || instrumentationKeyEnv;

if (!this.instrumentationKey || this.instrumentationKey == "") {
throw new Error("Instrumentation key not found, please provide a connection string before starting the server");
}

let endpoint = `${this.endpointUrl || csCode.ingestionendpoint || csEnv.ingestionendpoint || this._endpointBase}`;
if (endpoint.endsWith("/")) {
// Remove extra '/' if present
Expand Down Expand Up @@ -136,6 +142,7 @@ class Config implements IConfig {
private _mergeConfig() {
let jsonConfig = JsonConfig.getInstance();
this._connectionString = jsonConfig.connectionString;
this._instrumentationKey = jsonConfig.instrumentationKey;
this.correlationHeaderExcludedDomains = jsonConfig.correlationHeaderExcludedDomains;
this.correlationIdRetryIntervalMs = jsonConfig.correlationIdRetryIntervalMs;
this.disableAllExtendedMetrics = jsonConfig.disableAllExtendedMetrics;
Expand Down Expand Up @@ -171,19 +178,6 @@ class Config implements IConfig {
this.enableAutoWebSnippetInjection = jsonConfig.enableAutoWebSnippetInjection;
}

private static _getInstrumentationKey(): string {
// check for both the documented env variable and the azure-prefixed variable
var iKey = process.env[Config.ENV_iKey]
|| process.env[Config.ENV_azurePrefix + Config.ENV_iKey]
|| process.env[Config.legacy_ENV_iKey]
|| process.env[Config.ENV_azurePrefix + Config.legacy_ENV_iKey];
if (!iKey || iKey == "") {
throw new Error("Instrumentation key not found, pass the key in the config to this method or set the key in the environment variable APPINSIGHTS_INSTRUMENTATIONKEY before starting the server");
}

return iKey;
}

/**
* Validate UUID Format
* Specs taken from breeze repo
Expand Down
12 changes: 12 additions & 0 deletions Library/JsonConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import { IDisabledExtendedMetrics } from "../AutoCollection/NativePerformance";
const ENV_CONFIGURATION_FILE = "APPLICATIONINSIGHTS_CONFIGURATION_FILE";
// Azure Connection String
const ENV_connectionString = "APPLICATIONINSIGHTS_CONNECTION_STRING";
// Instrumentation Key
const ENV_azurePrefix = "APPSETTING_"; // Azure adds this prefix to all environment variables
const ENV_instrumentationKey = "APPINSIGHTS_INSTRUMENTATIONKEY";
const ENV_legacyInstrumentationKey = "APPINSIGHTS_INSTRUMENTATION_KEY"
// Native Metrics Opt Outs
const ENV_nativeMetricsDisablers = "APPLICATION_INSIGHTS_DISABLE_EXTENDED_METRIC";
const ENV_nativeMetricsDisableAll = "APPLICATION_INSIGHTS_DISABLE_ALL_EXTENDED_METRICS"
Expand Down Expand Up @@ -73,6 +77,14 @@ export class JsonConfig implements IJsonConfig {
constructor() {
// Load env variables first
this.connectionString = process.env[ENV_connectionString];
this.instrumentationKey = process.env[ENV_instrumentationKey]
|| process.env[ENV_azurePrefix + ENV_instrumentationKey]
|| process.env[ENV_legacyInstrumentationKey]
|| process.env[ENV_azurePrefix + ENV_legacyInstrumentationKey];

if (!this.connectionString && this.instrumentationKey) {
Logging.warn("APPINSIGHTS_INSTRUMENTATIONKEY is in path of deprecation, please use APPLICATIONINSIGHTS_CONNECTION_STRING env variable to setup the SDK.");
}
this.disableAllExtendedMetrics = !!process.env[ENV_nativeMetricsDisableAll];
this.extendedMetricDisablers = process.env[ENV_nativeMetricsDisablers];
this.proxyHttpUrl = process.env[ENV_http_proxy];
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ You can manually track more aspects of your app and system using the API describ

## Getting Started

> *Important:* On March 31st, 2025, support for instrumentation key ingestion will end. Instrumentation key ingestion will continue to work, but we’ll no longer provide updates or support for the feature. [Transition to connection strings](https://docs.microsoft.com/en-us/azure/azure-monitor/app/migrate-from-instrumentation-keys-to-connection-strings) to take advantage of [new capabilities](https://docs.microsoft.com/en-us/azure/azure-monitor/app/migrate-from-instrumentation-keys-to-connection-strings#new-capabilities).
1. Create an Application Insights resource in Azure by following [these instructions][].
2. Grab the _Connection String_ from the resource you created in
step 1. Later, you'll either add it to your app's environment variables or
Expand Down Expand Up @@ -76,7 +78,7 @@ let appInsights = require("applicationinsights");
appInsights.setup("YOUR_CONNECTION_STRING").start();
```
* If the instrumentation key is set in the environment variable
* If the connection string is set in the environment variable
APPLICATIONINSIGHTS\_CONNECTION\_STRING, `.setup()` can be called with no
arguments. This makes it easy to use different connection strings for different
environments.
Expand Down
72 changes: 29 additions & 43 deletions Tests/Bootstrap/Default.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { DiagnosticLogger } from "../../Bootstrap/DiagnosticLogger";
import * as DataModel from "../../Bootstrap/DataModel";
import * as Helpers from "../../Bootstrap/Helpers";
import * as DefaultTypes from "../../Bootstrap/Default";
import { JsonConfig } from "../../Library/JsonConfig";


const appInsights = require("../../applicationinsights");

Expand All @@ -20,27 +22,33 @@ class LoggerSpy implements DataModel.AgentLogger {
}

describe("#setupAndStart()", () => {
const startSpy = sinon.spy(appInsights, "start");
let startSpy: sinon.SinonSpy;
var sandbox: sinon.SinonSandbox;
let originalEnv: NodeJS.ProcessEnv;

before(() => {
startSpy.reset();
sandbox = sinon.sandbox.create();
});

afterEach(() => {
startSpy.reset();
delete require.cache[require.resolve("../../Bootstrap/Default")];
beforeEach(() => {
startSpy = sandbox.spy(appInsights, "start");
originalEnv = process.env;
JsonConfig["_instance"] = undefined;
});

after(() => {
startSpy.restore();
afterEach(() => {
process.env = originalEnv;
sandbox.restore();
delete require.cache[require.resolve("../../Bootstrap/Default")];
});

it("should return the client if started multiple times", () => {
const logger = new LoggerSpy();
const origEnv = process.env.ApplicationInsightsAgent_EXTENSION_VERSION;
process.env.ApplicationInsightsAgent_EXTENSION_VERSION = "~2";
const alreadyExistsStub = sinon.stub(Helpers, "sdkAlreadyExists", () => false);

const env = <{ [id: string]: string }>{};
env["ApplicationInsightsAgent_EXTENSION_VERSION"] = "~2";
env["APPLICATIONINSIGHTS_CONNECTION_STRING"] = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/";
process.env = env;
sandbox.stub(Helpers, "sdkAlreadyExists", () => false);
// Test
const Default = require("../../Bootstrap/Default") as typeof DefaultTypes;
Default.setLogger(new DiagnosticLogger(logger));
Expand All @@ -52,18 +60,18 @@ describe("#setupAndStart()", () => {
assert.deepEqual(instance2.defaultClient["_telemetryProcessors"].length, 2)

// Cleanup
alreadyExistsStub.restore();
instance1.dispose();
instance2.dispose();
process.env.ApplicationInsightsAgent_EXTENSION_VERSION = origEnv;
})

it("should setup and start the SDK", () => {
// Setup env vars before requiring loader
const logger = new LoggerSpy();
const origEnv = process.env.ApplicationInsightsAgent_EXTENSION_VERSION;
process.env.ApplicationInsightsAgent_EXTENSION_VERSION = "~2";
const alreadyExistsStub = sinon.stub(Helpers, "sdkAlreadyExists", () => false);
const env = <{ [id: string]: string }>{};
env["ApplicationInsightsAgent_EXTENSION_VERSION"] = "~2";
env["APPLICATIONINSIGHTS_CONNECTION_STRING"] = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/";
process.env = env;
sandbox.stub(Helpers, "sdkAlreadyExists", () => false);

// Test
const Default = require("../../Bootstrap/Default") as typeof DefaultTypes;
Expand All @@ -72,9 +80,7 @@ describe("#setupAndStart()", () => {
assert.deepEqual(instance, appInsights);

// Cleanup
alreadyExistsStub.restore();
instance.dispose();
process.env.ApplicationInsightsAgent_EXTENSION_VERSION = origEnv;

// start was called once
assert.equal(startSpy.callCount, 1);
Expand All @@ -87,32 +93,12 @@ describe("#setupAndStart()", () => {
it("should not setup and start the SDK if no setupString is provided", () => {
// Setup
const logger = new LoggerSpy();
const origEnv = process.env.ApplicationInsightsAgent_EXTENSION_VERSION;
const origIkey = process.env.APPINSIGHTS_INSTRUMENTATIONKEY;
const origCs = process.env.APPLICATIONINSIGHTS_CONNECTION_STRING;
process.env.ApplicationInsightsAgent_EXTENSION_VERSION = "~2";
delete process.env.APPINSIGHTS_INSTRUMENTATIONKEY;
delete process.env.APPLICATIONINSIGHTS_CONNECTION_STRING;
const alreadyExistsStub = sinon.stub(Helpers, "sdkAlreadyExists", () => false);
const env = <{ [id: string]: string }>{};
env["ApplicationInsightsAgent_EXTENSION_VERSION"] = "~2";
process.env = env;

sinon.stub(Helpers, "sdkAlreadyExists", () => false);
// Test
const Default = require("../../Bootstrap/Default") as typeof DefaultTypes;
Default.setLogger(new DiagnosticLogger(logger));
const instance = Default.setupAndStart();
assert.equal(instance, null);

// Cleanup
alreadyExistsStub.restore();
process.env.ApplicationInsightsAgent_EXTENSION_VERSION = origEnv;

// start was never called
assert.equal(startSpy.callCount, 0);

// No logging was done
assert.equal(logger.logCount, 0);
assert.equal(logger.errorCount, 1, "Should log if attach is attempted");

process.env.APPINSIGHTS_INSTRUMENTATIONKEY = origIkey;
process.env.APPLICATIONINSIGHTS_CONNECTION_STRING = origCs;
assert.throws(function () { const Default = require("../../Bootstrap/Default") as typeof DefaultTypes; });
});
});
2 changes: 1 addition & 1 deletion Tests/Bootstrap/DiagnosticLogger.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe("DiagnosticLogger", () => {
language: "nodejs",
operation: "Startup",
siteName: undefined,
ikey: undefined,
ikey: "unknown",
extensionVersion: undefined,
sdkVersion: version,
subscriptionId: null,
Expand Down
4 changes: 2 additions & 2 deletions Tests/Library/AuthorizationHandler.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe("Library/AuthorizationHandler", () => {

describe("#addAuthorizationHeader()", () => {
it("should add Authorization header to options", async () => {
var config = new Config("");
var config = new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333");
config.aadTokenCredential = new TestTokenCredential();
var handler = new AuthorizationHandler(config.aadTokenCredential);
var options = {
Expand All @@ -53,7 +53,7 @@ describe("Library/AuthorizationHandler", () => {
});

it("should refresh token if expired", async () => {
var config = new Config("");
var config = new Config("1aa11111-bbbb-1ccc-8ddd-eeeeffff3333");
var tokenCredential = new TestTokenCredential(new Date(new Date().getMilliseconds() - 500));
config.aadTokenCredential = tokenCredential;
var handler = new AuthorizationHandler(config.aadTokenCredential);
Expand Down
2 changes: 1 addition & 1 deletion applicationinsights.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ let _performanceLiveMetrics: AutoCollectPerformance;
*
* @param setupString the Connection String or Instrumentation Key to use. Optional, if
* this is not specified, the value will be read from the environment
* variable APPLICATIONINSIGHTS_CONNECTION_STRING or APPINSIGHTS_INSTRUMENTATIONKEY.
* variable APPLICATIONINSIGHTS_CONNECTION_STRING.
* @returns {Configuration} the configuration class to initialize
* and start the SDK.
*/
Expand Down
Loading

0 comments on commit 45357b7

Please sign in to comment.