Repository: microsoft/ApplicationInsights-node.js (ts)

Issue:
Database calls are not correlated with a function app that makes them in the Application Map
I'm not sure this is the right repo to open the issue, let me know if there's a better place for this.

For some reason calls to a Postgresql database that my function app makes originate from a "Web" instance.
Function app is the only application connected to the Application Insights instance, there's nothing Web/frontend in the stack.

<img width="944" alt="Screen Shot 2021-02-08 at 12 40 59" src="https://user-images.githubusercontent.com/1759643/107222502-e9d2f300-6a0c-11eb-9885-d2926b680874.png">

Function App Hosting Plan: Elastic Premium EP1
Application Insights Application Type: Node.JS
applicationinsights npm package version: 1.8.8

Code I use to start tracking:
```js
import * as appInsights from 'applicationinsights'
if (process.env.APPINSIGHTS_INSTRUMENTATIONKEY) {
  appInsights.setup().start()
}
```

Let me know if I can help with anything. Thanks!

Pull request description:
Add cloud role name automatic population
It require user to enable it calling setAutoPopulateAzureProperties method during initialization

Fixes #726 

Patch:
diff --git a/Bootstrap/Default.ts b/Bootstrap/Default.ts
index 072b6c4..a5bddbb 100644
--- a/Bootstrap/Default.ts
+++ b/Bootstrap/Default.ts
@@ -99,6 +99,7 @@ export function setupAndStart(setupString = _setupString): typeof types | null {
 
         // Instrument the SDK
         _appInsights.setup(setupString).setSendLiveMetrics(true);
+        _appInsights.defaultClient.setAutoPopulateAzureProperties(true);
         _appInsights.defaultClient.addTelemetryProcessor(prefixInternalSdkVersion);
         _appInsights.defaultClient.addTelemetryProcessor(copyOverPrefixInternalSdkVersionToHeartBeatMetric);
         _appInsights.start();
diff --git a/Library/TelemetryClient.ts b/Library/TelemetryClient.ts
index 7321db1..094cabc 100644
--- a/Library/TelemetryClient.ts
+++ b/Library/TelemetryClient.ts
@@ -20,6 +20,7 @@ import QuickPulseStateManager = require("./QuickPulseStateManager");
  */
 class TelemetryClient {
     private _telemetryProcessors: { (envelope: Contracts.EnvelopeTelemetry, contextObjects: { [name: string]: any; }): boolean; }[] = [];
+    private _enableAzureProperties: boolean = false;
 
     public config: Config;
     public context: Context;
@@ -144,7 +145,9 @@ class TelemetryClient {
             if (telemetry.time) {
                 envelope.time = telemetry.time.toISOString();
             }
-
+            if (this._enableAzureProperties) {
+                TelemetryProcessors.azureRoleEnvironmentTelemetryProcessor(envelope, this.context);
+            }
             var accepted = this.runTelemetryProcessors(envelope, telemetry.contextObjects);
 
             // Ideally we would have a central place for "internal" telemetry processors and users can configure which ones are in use.
@@ -161,6 +164,15 @@ class TelemetryClient {
         }
     }
 
+    /**
+     * Automatically populate telemetry properties like RoleName when running in Azure
+     *
+      * @param value if true properties will be populated
+     */
+    public setAutoPopulateAzureProperties(value: boolean) {
+        this._enableAzureProperties = value;
+    }
+
     /**
      * Adds telemetry processor to the collection. Telemetry processors will be called one by one
      * before telemetry item is pushed for sending and in the order they were added.
diff --git a/README.md b/README.md
index 74a249f..059b002 100644
--- a/README.md
+++ b/README.md
@@ -228,6 +228,15 @@ appInsights.defaultClient.context.tags[appInsights.defaultClient.context.keys.cl
 appInsights.start();
 ```
 
+If running in Azure App service or Azure functions the SDK will automatically populate the cloud role when following code is added:
+```javascript
+const appInsights = require("applicationinsights");
+appInsights.setup("<instrumentation_key>");
+appInsights.defaultClient.setAutoPopulateAzureProperties(true);
+appInsights.start();
+```
+
+
 ### Automatic third-party instrumentation
 
 In order to track context across asynchronous calls, some changes are required in third party libraries such as mongodb and redis.
diff --git a/TelemetryProcessors/AzureRoleEnvironmentTelemetryInitializer.ts b/TelemetryProcessors/AzureRoleEnvironmentTelemetryInitializer.ts
new file mode 100644
index 0000000..8141931
--- /dev/null
+++ b/TelemetryProcessors/AzureRoleEnvironmentTelemetryInitializer.ts
@@ -0,0 +1,11 @@
+import Contracts = require("../Declarations/Contracts");
+import Context = require("../Library/Context");
+
+/**
+ *  A telemetry processor that handles Azure specific variables.
+ */
+export function azureRoleEnvironmentTelemetryProcessor(envelope: Contracts.EnvelopeTelemetry, context: Context): void {
+    if (process.env.WEBSITE_SITE_NAME) { // Azure Web apps and Functions
+        envelope.tags[context.keys.cloudRole] = process.env.WEBSITE_SITE_NAME;
+    }
+}
diff --git a/TelemetryProcessors/index.ts b/TelemetryProcessors/index.ts
index b5b2532..1beb6e9 100644
--- a/TelemetryProcessors/index.ts
+++ b/TelemetryProcessors/index.ts
@@ -1,2 +1,3 @@
+export * from "./AzureRoleEnvironmentTelemetryInitializer";
 export * from "./SamplingTelemetryProcessor";
 export * from "./PerformanceMetricsTelemetryProcessor";


Test patch:
diff --git a/Tests/Library/Client.tests.ts b/Tests/Library/Client.tests.ts
index 8a51000..beb5ac7 100644
--- a/Tests/Library/Client.tests.ts
+++ b/Tests/Library/Client.tests.ts
@@ -166,8 +166,8 @@ describe("Library/TelemetryClient", () => {
     describe("#trackAvailability()", () => {
         it("should track availability with correct data", () => {
             trackStub.reset();
-            const expectedTelemetryData: Contracts.AvailabilityTelemetry =  {
-                duration: 100, id: "id1", message: "message1",success : true, name: "name1", runLocation: "east us"
+            const expectedTelemetryData: Contracts.AvailabilityTelemetry = {
+                duration: 100, id: "id1", message: "message1", success: true, name: "name1", runLocation: "east us"
             };
 
             client.trackAvailability(expectedTelemetryData);
@@ -388,7 +388,7 @@ describe("Library/TelemetryClient", () => {
             it('should track request with tagOverrides on response finish event', () => {
                 trackStub.reset();
                 clock.reset();
-                client.trackNodeHttpRequest({ request: <any>request, response: <any>response, properties: properties, tagOverrides: {"custom": "A", "ai.device.id": "B"} });
+                client.trackNodeHttpRequest({ request: <any>request, response: <any>response, properties: properties, tagOverrides: { "custom": "A", "ai.device.id": "B" } });
 
                 // emit finish event
                 response.emitFinish();
@@ -492,7 +492,7 @@ describe("Library/TelemetryClient", () => {
             });
             it('should track request with correct data and tag overrides synchronously', () => {
                 trackStub.reset();
-                client.trackNodeHttpRequestSync({ request: <any>request, response: <any>response, duration: 100, properties: properties, tagOverrides: {"custom": "A", "ai.device.id": "B"}});
+                client.trackNodeHttpRequestSync({ request: <any>request, response: <any>response, duration: 100, properties: properties, tagOverrides: { "custom": "A", "ai.device.id": "B" } });
                 assert.ok(trackStub.calledOnce);
                 var requestTelemetry = <Contracts.RequestTelemetry>trackStub.firstCall.args[0];
 
@@ -533,7 +533,7 @@ describe("Library/TelemetryClient", () => {
                         path: '/search?q=test'
                     },
                     request: <any>request, properties: properties,
-                    tagOverrides: {"custom": "A", "ai.device.id": "B"}
+                    tagOverrides: { "custom": "A", "ai.device.id": "B" }
                 });
 
                 // response event was not emitted yet
@@ -553,7 +553,7 @@ describe("Library/TelemetryClient", () => {
                 assert.equal(dependencyTelemetry.target, "bing.com");
                 assert.equal(dependencyTelemetry.dependencyTypeName, Contracts.RemoteDependencyDataConstants.TYPE_HTTP);
                 assert.deepEqual(dependencyTelemetry.properties, properties);
-                assert.deepEqual(dependencyTelemetry.tagOverrides, {"custom": "A", "ai.device.id": "B"});
+                assert.deepEqual(dependencyTelemetry.tagOverrides, { "custom": "A", "ai.device.id": "B" });
             });
 
             it('should track request with correct data on response event', () => {
@@ -937,6 +937,20 @@ describe("Library/TelemetryClient", () => {
             assert.equal(actualData.name, expectedName, "envelope name should be changed by the processor");
         });
 
+        it("setAutoPopulateAzureProperties", () => {
+            trackStub.restore();
+            const env = <{ [id: string]: string }>{};
+            const originalEnv = process.env;
+            env.WEBSITE_SITE_NAME = "testRole";
+            process.env = env;
+            client.setAutoPopulateAzureProperties(true);
+            client.track(testEventTelemetry, Contracts.TelemetryType.Event);
+            process.env = originalEnv;
+            assert.equal(sendStub.callCount, 1, "send called once");
+            var actualData = sendStub.firstCall.args[0] as Contracts.Envelope;
+            assert.equal(actualData.tags[client.context.keys.cloudRole], "testRole");
+        });
+
         it("telemetry processor can access the context object", () => {
             trackStub.restore();
             var expectedName = "I was here";
diff --git a/Tests/TelemetryProcessors/AzureRoleEnvironmentTelemetryInitializer.tests.ts b/Tests/TelemetryProcessors/AzureRoleEnvironmentTelemetryInitializer.tests.ts
new file mode 100644
index 0000000..6a6fdfc
--- /dev/null
+++ b/Tests/TelemetryProcessors/AzureRoleEnvironmentTelemetryInitializer.tests.ts
@@ -0,0 +1,34 @@
+import assert = require("assert");
+import Client = require("../../Library/TelemetryClient");
+import { Contracts } from "../../applicationinsights";
+
+import AzureProps = require("../../TelemetryProcessors/AzureRoleEnvironmentTelemetryInitializer");
+
+describe("TelemetryProcessors/AzureRoleEnvironmentTelemetryInitializer", () => {
+    var ikey = "1aa11111-bbbb-1ccc-8ddd-eeeeffff3333";
+    var envelope: Contracts.Envelope = {
+        ver: 2,
+        name: "name",
+        data: {
+            baseType: "SomeData"
+        },
+        iKey: ikey,
+        sampleRate: 100,
+        seq: "",
+        time: "",
+        tags: []
+    };
+    var client = new Client(ikey);
+
+    describe("#azureRoleEnvironmentTelemetryProcessor()", () => {
+        it("will add cloud role", () => {
+            const env = <{ [id: string]: string }>{};
+            const originalEnv = process.env;
+            env.WEBSITE_SITE_NAME = "testRole";
+            process.env = env;
+            AzureProps.azureRoleEnvironmentTelemetryProcessor(envelope, client.context);
+            assert.equal(envelope.tags[client.context.keys.cloudRole], "testRole");
+            process.env = originalEnv;
+        });
+    });
+});
\ No newline at end of file