Repository: microsoft/ApplicationInsights-node.js
Pull request #1203 (2023-09-06 23:30:08)

[Shim] Fix JSON Config
* Add remaining JSON config values.
* Fix JSON config file path and stop us from overwriting defined env vars with undefined values from the JSON file.


Patch:
diff --git a/src/shim/config.ts b/src/shim/shim-config.ts
similarity index 87%
rename from src/shim/config.ts
rename to src/shim/shim-config.ts
index 41a082b..15a837b 100644
--- a/src/shim/config.ts
+++ b/src/shim/shim-config.ts
@@ -7,9 +7,8 @@ import { DiagLogLevel } from "@opentelemetry/api";
 import { HttpInstrumentationConfig } from "@opentelemetry/instrumentation-http";
 import { DistributedTracingModes, IConfig, IDisabledExtendedMetrics, IWebInstrumentationConfig } from "./types";
 import { Logger } from "../shared/logging";
-import { ShimJsonConfig } from "./jsonConfig";
-import { ApplicationInsightsOptions, ExtendedMetricType } from "../types";
-
+import { ShimJsonConfig } from "./shim-jsonConfig";
+import { ApplicationInsightsOptions, ExtendedMetricType, InstrumentationOptionsType } from "../types";
 
 class Config implements IConfig {
 
@@ -61,6 +60,7 @@ class Config implements IConfig {
     public webInstrumentationSrc: string;
     public webInstrumentationConnectionString?: string;
     public noPatchModules: string;
+    public noDiagnosticChannel: boolean;
 
 
     /**
@@ -130,6 +130,8 @@ class Config implements IConfig {
         this.webInstrumentationConnectionString = jsonConfig.webInstrumentationConnectionString;
         this.webInstrumentationConfig = jsonConfig.webInstrumentationConfig;
         this.webInstrumentationSrc = jsonConfig.webInstrumentationSrc;
+        this.noPatchModules = jsonConfig.noPatchModules;
+        this.noDiagnosticChannel = jsonConfig.noDiagnosticChannel
     }
 
     /**
@@ -147,6 +149,7 @@ class Config implements IConfig {
                 mySql: { enabled: true },
                 redis: { enabled: true },
                 redis4: { enabled: true },
+                postgreSql: { enabled: true },
             },
             logInstrumentationOptions: {
                 console: { enabled: false },
@@ -288,6 +291,44 @@ class Config implements IConfig {
             };
         }
 
+        if (this.noDiagnosticChannel === true) {
+            // Disable all instrumentations except http to conform with AppInsights 2.x behavior
+            for (const mod in options.instrumentationOptions) {
+                if (mod !== "http") {
+                    (options.instrumentationOptions as InstrumentationOptionsType)[mod] = { enabled: false };
+                }
+            }
+            for (const mod in options.logInstrumentationOptions) {
+                (options.logInstrumentationOptions as InstrumentationOptionsType)[mod] = { enabled: false };
+            }
+        }
+
+        if (this.noPatchModules && this.noDiagnosticChannel !== true) {
+            const unpatchedModules: string[] = this.noPatchModules.split(",");
+            // Convert module names not supported by new InstrumentationOptions
+            for (let i = 0; i < unpatchedModules.length; i++) {
+                if (unpatchedModules[i] === "pg-pool" || unpatchedModules[i] === "pg") {
+                    unpatchedModules[i] = "postgresql";
+                } else if (unpatchedModules[i] === "mongodb-core") {
+                    unpatchedModules[i] = "mongodb";
+                } else if (unpatchedModules[i] === "redis") {
+                    unpatchedModules.push("redis4")
+                }
+            }
+
+            // Disable instrumentation for unpatched modules
+            for (const mod in options.instrumentationOptions) {
+                if (unpatchedModules.indexOf(mod.toLowerCase()) !== -1) {
+                    (options.instrumentationOptions as InstrumentationOptionsType)[mod] = { enabled: false };
+                }
+            }
+            for (const mod in options.logInstrumentationOptions) {
+                if (unpatchedModules.indexOf(mod.toLowerCase()) !== -1) {
+                    (options.logInstrumentationOptions as InstrumentationOptionsType)[mod] = { enabled: false };
+                }
+            }
+        }
+
         // NOT SUPPORTED CONFIGURATION OPTIONS
         if (this.disableAppInsights) {
             Logger.getInstance().warn("disableAppInsights configuration no longer supported.");
@@ -338,31 +379,6 @@ class Config implements IConfig {
         }
         return options;
     }
-
-    /**
-    * Validate UUID Format
-    * Specs taken from breeze repo
-    * The definition of a VALID instrumentation key is as follows:
-    * Not none
-    * Not empty
-    * Every character is a hex character [0-9a-f]
-    * 32 characters are separated into 5 sections via 4 dashes
-    * First section has 8 characters
-    * Second section has 4 characters
-    * Third section has 4 characters
-    * Fourth section has 4 characters
-    * Fifth section has 12 characters
-    */
-    private static _validateInstrumentationKey(iKey: string): boolean {
-        if (iKey.startsWith("InstrumentationKey=")) {
-            const startIndex = iKey.indexOf("InstrumentationKey=") + "InstrumentationKey=".length;
-            const endIndex = iKey.indexOf(";", startIndex);
-            iKey = iKey.substring(startIndex, endIndex);
-        }
-        const UUID_Regex = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$";
-        const regexp = new RegExp(UUID_Regex);
-        return regexp.test(iKey);
-    }
 }
 
 export = Config;
\ No newline at end of file
diff --git a/src/shim/jsonConfig.ts b/src/shim/shim-jsonConfig.ts
similarity index 81%
rename from src/shim/jsonConfig.ts
rename to src/shim/shim-jsonConfig.ts
index e2906ea..584310a 100644
--- a/src/shim/jsonConfig.ts
+++ b/src/shim/shim-jsonConfig.ts
@@ -101,7 +101,7 @@ export class ShimJsonConfig implements IJsonConfig {
         // JSON file
         else {
             const configFileName = "applicationinsights.json";
-            const rootPath = path.join(__dirname, "../../../../"); // Root of folder (__dirname = ../dist-esm/src)
+            const rootPath = path.join(__dirname, "../../../"); // Root of folder (__dirname = ../dist-esm/src)
             let tempDir = path.join(rootPath, configFileName); // default
             const configFile = process.env[ENV_CONFIGURATION_FILE];
             if (configFile) {
@@ -119,8 +119,39 @@ export class ShimJsonConfig implements IJsonConfig {
         }
         try {
             const jsonConfig: IJsonConfig = JSON.parse(jsonString);
-            this.connectionString = jsonConfig.connectionString;
-            this.enableAutoCollectExceptions = jsonConfig.enableAutoCollectExceptions;
+            if (jsonConfig.connectionString !== undefined) {
+                this.connectionString = jsonConfig.connectionString;
+            }
+            if (jsonConfig.disableAllExtendedMetrics !== undefined) {
+                this.disableAllExtendedMetrics = jsonConfig.disableAllExtendedMetrics;
+            }
+            if (jsonConfig.extendedMetricDisablers !== undefined) {
+                this.extendedMetricDisablers = jsonConfig.extendedMetricDisablers;
+            }
+            if (jsonConfig.proxyHttpUrl !== undefined) {
+                this.proxyHttpUrl = jsonConfig.proxyHttpUrl;
+            }
+            if (jsonConfig.proxyHttpsUrl !== undefined) {
+                this.proxyHttpsUrl = jsonConfig.proxyHttpsUrl;
+            }
+            if (jsonConfig.noDiagnosticChannel !== undefined) {
+                this.noDiagnosticChannel = jsonConfig.noDiagnosticChannel;
+            }
+            if (jsonConfig.noHttpAgentKeepAlive !== undefined) {
+                this.noHttpAgentKeepAlive = jsonConfig.noHttpAgentKeepAlive;
+            }
+            if (jsonConfig.noPatchModules !== undefined) {
+                this.noPatchModules = jsonConfig.noPatchModules;
+            }
+            if (jsonConfig.enableWebInstrumentation !== undefined) {
+                this.enableWebInstrumentation = jsonConfig.enableWebInstrumentation;
+            }
+            if (jsonConfig.webInstrumentationSrc !== undefined) {
+                this.webInstrumentationSrc = jsonConfig.webInstrumentationSrc;
+            }
+            if (jsonConfig.webInstrumentationConnectionString !== undefined) {
+                this.webInstrumentationConnectionString = jsonConfig.webInstrumentationConnectionString;
+            }
             this.endpointUrl = jsonConfig.endpointUrl;
             this.samplingPercentage = jsonConfig.samplingPercentage;
             this.enableAutoCollectExternalLoggers = jsonConfig.enableAutoCollectExternalLoggers;
@@ -148,18 +179,8 @@ export class ShimJsonConfig implements IJsonConfig {
             this.enableInternalDebugLogging = jsonConfig.enableInternalDebugLogging;
             this.enableInternalWarningLogging = jsonConfig.enableInternalWarningLogging;
             this.enableSendLiveMetrics = jsonConfig.enableSendLiveMetrics;
-            this.disableAllExtendedMetrics = jsonConfig.disableAllExtendedMetrics;
-            this.extendedMetricDisablers = jsonConfig.extendedMetricDisablers;
-            this.noDiagnosticChannel = jsonConfig.noDiagnosticChannel;
-            this.noPatchModules = jsonConfig.noPatchModules;
-            this.noHttpAgentKeepAlive = jsonConfig.noHttpAgentKeepAlive;
-            this.proxyHttpUrl = jsonConfig.proxyHttpUrl;
-            this.proxyHttpsUrl = jsonConfig.proxyHttpsUrl;
-            this.webInstrumentationConnectionString = jsonConfig.webInstrumentationConnectionString;
             this.webInstrumentationConfig = jsonConfig.webInstrumentationConfig;
-            this.webInstrumentationSrc = jsonConfig.webInstrumentationSrc;
             this.quickPulseHost = jsonConfig.quickPulseHost;
-            this.enableWebInstrumentation = jsonConfig.enableWebInstrumentation;
         } catch (err) {
             Logger.getInstance().info("Missing or invalid JSON config file: ", err);
         }
diff --git a/src/shim/telemetryClient.ts b/src/shim/telemetryClient.ts
index 9e406bf..30af5f7 100644
--- a/src/shim/telemetryClient.ts
+++ b/src/shim/telemetryClient.ts
@@ -10,7 +10,7 @@ import { TelemetryItem as Envelope } from "../declarations/generated";
 import { Context } from "./context";
 import { Logger } from "../shared/logging";
 import { Util } from "../shared/util";
-import Config = require("./config");
+import Config = require("./shim-config");
 import { AttributeSpanProcessor } from "../shared/util/attributeSpanProcessor";
 import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
 import { AttributeLogProcessor } from "../shared/util/attributeLogRecordProcessor";
diff --git a/src/types.ts b/src/types.ts
index 0f158f5..b0055a8 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -110,6 +110,10 @@ export interface LogInstrumentationOptions {
     winston?: { enabled: boolean };
 }
 
+export interface InstrumentationOptionsType {
+  [key: string]: { enabled: boolean }
+}
+
 export const enum ExtendedMetricType {
     gc = "gc",
     heap = "heap",


Test patch:
diff --git a/test/unitTests/shim/config.tests.ts b/test/unitTests/shim/config.tests.ts
index e621d32..663b03c 100644
--- a/test/unitTests/shim/config.tests.ts
+++ b/test/unitTests/shim/config.tests.ts
@@ -4,7 +4,7 @@ import azureCoreAuth = require("@azure/core-auth");
 import { DiagLogLevel } from '@opentelemetry/api';
 import { HttpInstrumentationConfig } from '@opentelemetry/instrumentation-http';
 import { Logger } from "../../../src/shared/logging"
-import Config = require('../../../src/shim/config');
+import Config = require('../../../src/shim/shim-config');
 import { TelemetryClient } from "../../../src/shim/telemetryClient";
 import applicationInsights = require("../../../src/index");
 
@@ -28,6 +28,7 @@ class TestTokenCredential implements azureCoreAuth.TokenCredential {
 
 describe("shim/configuration/config", () => {
     const connectionString = "InstrumentationKey=1aa11111-bbbb-1ccc-8ddd-eeeeffff3333;IngestionEndpoint=https://centralus-0.in.applicationinsights.azure.com/";
+    const ignoredOutgoingUrls = ["*.core.windows.net","*.core.chinacloudapi.cn","*.core.cloudapi.de","*.core.usgovcloudapi.net","*.core.microsoft.scloud","*.core.eaglex.ic.gov"];
 
     let originalEnv: NodeJS.ProcessEnv;
     let sandbox: sinon.SinonSandbox;
@@ -109,6 +110,36 @@ describe("shim/configuration/config", () => {
             telemetryClient["_attributeSpanProcessor"]["_attributes"] = { "ai.cloud.role": "testRole", "ai.cloud.roleInstance": "testRoleInstance" };
             telemetryClient["_attributeLogProcessor"]["_attributes"] = { "ai.cloud.role": "testRole", "ai.cloud.roleInstance": "testRoleInstance" };
         });
+
+        it("should disable instrumentations when noDiagnosticChannel is set", () => {
+            const config = new Config(connectionString);
+            config.noDiagnosticChannel = true;
+            let options = config.parseConfig();
+            assert.equal(JSON.stringify(options.instrumentationOptions), JSON.stringify({ 
+                http: { enabled: true, ignoreOutgoingUrls: ignoredOutgoingUrls },
+                azureSdk: { enabled: false },
+                mongoDb: { enabled: false },
+                mySql: { enabled: false },
+                redis: { enabled: false },
+                redis4: { enabled: false },
+                postgreSql: { enabled: false }
+            }));
+        });
+
+        it("should disable specific instrumentations when noPatchModules is set", () => {
+            const config = new Config(connectionString);
+            config.noPatchModules = "azuresdk,mongodb-core,redis,pg-pool";
+            let options = config.parseConfig();
+            assert.equal(JSON.stringify(options.instrumentationOptions), JSON.stringify({ 
+                http: { enabled: true, ignoreOutgoingUrls: ignoredOutgoingUrls },
+                azureSdk: { enabled: false },
+                mongoDb: { enabled: false },
+                mySql: { enabled: true },
+                redis: { enabled: false },
+                redis4: { enabled: false },
+                postgreSql: { enabled: false }
+            }));
+        });
         // TODO: Add test for warning messages
     });
 });
diff --git a/test/unitTests/shim/jsonConfig.tests.ts b/test/unitTests/shim/jsonConfig.tests.ts
index 8141e41..25cde07 100644
--- a/test/unitTests/shim/jsonConfig.tests.ts
+++ b/test/unitTests/shim/jsonConfig.tests.ts
@@ -1,6 +1,6 @@
 import assert = require("assert");
 import path = require("path");
-import { ShimJsonConfig } from "../../../src/shim/jsonConfig";
+import { ShimJsonConfig } from "../../../src/shim/shim-jsonConfig";
 
 
 describe("Json Config", () => {