Skip to content

Commit 7a26e28

Browse files
authored
Merge pull request #662 from Microsoft/daveta-appsight-js
Add BotTelemetry instrumentation and utilities
2 parents 9f2c5c4 + f88e8f0 commit 7a26e28

File tree

24 files changed

+10606
-7108
lines changed

24 files changed

+10606
-7108
lines changed

README.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,21 @@ A rich set of samples are available at [BotBuilder-Samples](https://github.com/m
2222
## Packages
2323
| Name | Released Package | Daily Build |
2424
|---------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|--|
25-
| botbuilder | [![BotBuilder Badge](https://img.shields.io/npm/dt/botbuilder.svg?logo=npm&label=botbuilder)](https://www.npmjs.com/package/botbuilder/) | TBD |
26-
| botbuilder-ai | [![BotBuilder Badge](https://img.shields.io/npm/dt/botbuilder-ai.svg?logo=npm&label=botbuilder-ai)](https://www.npmjs.com/package/botbuilder-ai/) | TBD |
27-
| botbuilder-azure | [![BotBuilder Badge](https://img.shields.io/npm/dt/botbuilder-azure.svg?logo=npm&label=botbuilder-azure)](https://www.npmjs.com/package/botbuilder-azure/) | TBD |
28-
| botbuilder-core | [![BotBuilder Badge](https://img.shields.io/npm/dt/botbuilder-core.svg?logo=npm&label=botbuilder-core)](https://www.npmjs.com/package/botbuilder-core/) | TBD |
29-
| botbuilder-dialogs | [![BotBuilder Badge](https://img.shields.io/npm/dt/botbuilder-dialogs.svg?logo=npm&label=botbuilder-dialogs)](https://www.npmjs.com/package/botbuilder-dialogs/) | TBD |
30-
| botframework-config | [![BotBuilder Badge](https://img.shields.io/npm/dt/botbuilder-config.svg?logo=npm&label=botframework-config)](https://www.npmjs.com/package/botframework-config/) | TBD |
31-
| botframework-connector | [![BotBuilder Badge](https://img.shields.io/npm/dt/botframework-connector.svg?logo=npm&label=botframework-connector)](https://www.npmjs.com/package/botframework-connector/) | TBD |
32-
| botframework-schema | [![BotBuilder Badge](https://img.shields.io/npm/dt/botframework-schema.svg?logo=npm&label=botframework-schema)](https://www.npmjs.com/package/botframework-schema/) | TBD |
25+
| botbuilder | [![BotBuilder Badge](https://img.shields.io/npm/dt/botbuilder.svg?logo=npm&label=botbuilder)](https://www.npmjs.com/package/botbuilder/) | TBD |
26+
| botbuilder-ai | [![BotBuilder Badge](https://img.shields.io/npm/dt/botbuilder-ai.svg?logo=npm&label=botbuilder-ai)](https://www.npmjs.com/package/botbuilder-ai/) | TBD |
27+
| botbuilder-applicationinsights | [![BotBuilder Badge](https://img.shields.io/npm/dt/botbuilder-applicationinsights.svg?logo=npm&label=botbuilder-applicationinsights)](https://www.npmjs.com/package/botbuilder-applicationinsights/) | TBD |
28+
| botbuilder-azure | [![BotBuilder Badge](https://img.shields.io/npm/dt/botbuilder-azure.svg?logo=npm&label=botbuilder-azure)](https://www.npmjs.com/package/botbuilder-azure/) | TBD |
29+
| botbuilder-core | [![BotBuilder Badge](https://img.shields.io/npm/dt/botbuilder-core.svg?logo=npm&label=botbuilder-core)](https://www.npmjs.com/package/botbuilder-core/) | TBD |
30+
| botbuilder-dialogs | [![BotBuilder Badge](https://img.shields.io/npm/dt/botbuilder-dialogs.svg?logo=npm&label=botbuilder-dialogs)](https://www.npmjs.com/package/botbuilder-dialogs/) | TBD |
31+
| botframework-config | [![BotBuilder Badge](https://img.shields.io/npm/dt/botbuilder-config.svg?logo=npm&label=botframework-config)](https://www.npmjs.com/package/botframework-config/) | TBD |
32+
| botframework-connector | [![BotBuilder Badge](https://img.shields.io/npm/dt/botframework-connector.svg?logo=npm&label=botframework-connector)](https://www.npmjs.com/package/botframework-connector/) | TBD |
33+
| botframework-schema | [![BotBuilder Badge](https://img.shields.io/npm/dt/botframework-schema.svg?logo=npm&label=botframework-schema)](https://www.npmjs.com/package/botframework-schema/) | TBD |
3334

3435

3536
To use the daily builds, which are published to MyGet, please follow the instructions [here](UsingMyGet.md).
3637

3738

3839

39-
40-
4140
## Contributing
4241

4342
This project welcomes contributions and suggestions. Most contributions require you to agree to a

lerna.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"packages": [
44
"libraries/botbuilder",
55
"libraries/botbuilder-ai",
6+
"libraries/botbuilder-applicationinsights",
67
"libraries/botbuilder-azure",
78
"libraries/botbuilder-core",
89
"libraries/botbuilder-dialogs",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/**/node_modules
2+
/**/.vscode
3+
/**/lib
4+
coverage
5+
.nyc_output
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/**/node_modules
2+
/**/.vscode
3+
coverage
4+
.nyc_output
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"extension": [
3+
".js"
4+
],
5+
"include": [
6+
"lib/**/*.js"
7+
],
8+
"exclude": [
9+
"**/node_modules/**",
10+
"**/tests/**",
11+
"**/coverage/**",
12+
"**/*.d.ts"
13+
],
14+
"reporter": [
15+
"html"
16+
],
17+
"all": true,
18+
"cache": true
19+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Bot Builder Application Insights
2+
3+
Application Insights extensions for Microsoft BotBuilder.
4+
5+
- [Installing](#installing)
6+
- [Basic Use](#use)
7+
- [Documentation](https://docs.microsoft.com/en-us/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0)
8+
- [Class Reference](https://docs.microsoft.com/en-us/javascript/api/botbuilder-azure/)
9+
- [GitHub Repo](https://github.com/Microsoft/botbuilder-js)
10+
- [Report Issues](https://github.com/Microsoft/botbuilder-js/issues)
11+
12+
## Installing
13+
To add the latest version of this package to your bot:
14+
15+
```bash
16+
npm install --save botbuilder-applicationinsights
17+
```
18+
19+
#### Use the Daily Build
20+
21+
To get access to the daily builds of this library, configure npm to use the MyGet feed before installing.
22+
23+
```bash
24+
npm config set registry https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/
25+
```
26+
27+
To reset the registry in order to get the latest published version, run:
28+
```bash
29+
npm config set registry https://registry.npmjs.org/
30+
```
31+
32+
## What's Included
33+
34+
This module contains interfaces to use the Application Insights services to back Bot Builder's metrics and reporting needs.
35+
36+
Using this module along with the `dialog.telemetryClient` property will create Waterfall Dialogs that emit Application Insights
37+
events for each step of the dialog, and which can automatically be correlated with all other actions taken to fufill an incoming request.
38+
39+
In addition, this module can be used alongside the [QnA Maker sample app](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/20.qna-with-appinsights)
40+
and [LUIS sample app](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/21.luis-with-appinsights), both of which
41+
are instrumented with additional Application Insight events.
42+
43+
## Use
44+
45+
Import the module into your app. Make sure to import both the `ApplicationInsightsTelemetryClient` class and the `ApplicationInsightsWebserverMiddleware` function.
46+
In order to function properly, it is recommended that you import this module as early as possible in your application code -- preferably as the first import.
47+
48+
```javascript
49+
const { ApplicationInsightsTelemetryClient, ApplicationInsightsWebserverMiddleware } = require('botbuilder-applicationinsights');
50+
```
51+
52+
Create an instance of the ApplicationInsightsTelemetryClient. This requires an `Instrumentation Key` which can be acquired by creating
53+
the Application Insights instance within the Azure portal. This key should be stored in the environment, or in your applications .bot file.
54+
55+
The resulting `appInsightsClient` can be used to track events, traces, and other information to Application Insights.
56+
57+
```javascript
58+
const appInsightsClient = new ApplicationInsightsTelemetryClient(process.env.instrumentationKey);
59+
```
60+
Now, bind the ApplicationInsightsWebserverMiddleware to your webserver. The example below shows this being used with Restify,
61+
though this should also work with Express and other webserver modules that follow the Express middleware pattern.
62+
63+
This middleware will ensure that Application Insights has the appropriate information to correlate activities to the
64+
original incoming message, user and session that triggered them.
65+
66+
```javascript
67+
let server = restify.createServer();
68+
server.use(ApplicationInsightsWebserverMiddleware);
69+
```
70+
71+
### Configure Options
72+
73+
By default, the `botbuilder-applicationinsights` is configured to automatically track and correlate data from many parts of your application.
74+
If you would like to tune the settings of your client, use the configuration functions documented [here as part of the official Application Insights client](https://github.com/Microsoft/ApplicationInsights-node.js#configuration), but call them from the `appInsightsClient.configuration` property as seen below:
75+
76+
```javascript
77+
// enable application insights to capture console.log output and send it as trace events
78+
appInsightsClient.configuration.setAutoCollectConsole(true, true);
79+
```
80+
81+
### Use with Waterfall Dialogs
82+
83+
As of version `4.2`, Waterfall Dialogs included in [botbuilder-dialogs](https://github.com/Microsoft/botbuilder-js/tree/master/libraries/botbuilder-dialogs) can
84+
tracked automatically with a properly configured telemetry client. To use Application Insights to track a waterfall dialog, set the `dialog.telemetryClient` property:
85+
86+
```javascript
87+
const myDialog = new WaterfallDialog(DIALOG_ID, array_of_steps);
88+
myDialog.telemetryClient = appInsightsClient;
89+
```
90+
91+
You may also set the `telemetryClient` field on `DialogSet` and `ComponentDialog` objects. Setting the property on these classes will apply it to all contained dialogs automatically.
92+
93+
Once enabled, expect to see `WaterfallStart`, `WaterfallStep`, `WaterfallComplete` and `WaterfallCancel` events logged in Application Insights.
94+
These custom events will also include the dialog id, a unique instance id for each use of the dialog, and the name of the dialog step.
95+
96+
### Use Directly
97+
98+
This telemetry client includes access to the common event types used by Application Insights. The signatures for these functions match the [official
99+
Application Insights client module](https://github.com/Microsoft/ApplicationInsights-node.js#track-custom-telemetry).
100+
101+
```javascript
102+
appInsightsClient.trackEvent({name: "my custom event", properties: {customProperty: "custom property value"}});
103+
appInsightsClient.trackException({exception: new Error("handled exceptions can be logged with this method")});
104+
appInsightsClient.trackTrace({message: "trace message"});
105+
appInsightsClient.trackDependency({target:"http://dbname", name:"select customers proc", data:"SELECT * FROM Customers", duration:231, resultCode:0, success: true, dependencyTypeName: "ZSQL"});
106+
```
107+
108+
## Learn More
109+
Learn how to build great bots.
110+
111+
* [Sample: Using Application Insights with QnA Maker](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/20.qna-with-appinsights)
112+
* [Sample: Using Application Insights with LUIS](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_nodejs/21.luis-with-appinsights)
113+
* [BotTelemetryClient class interface](https://github.com/Microsoft/botbuilder-js/tree/master/libraries/botbuilder-core/src/botTelemetryClient.ts)
114+
* [Application Insights official client](https://github.com/Microsoft/ApplicationInsights-node.js)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"name": "botbuilder-applicationinsights",
3+
"author": "Microsoft Corp.",
4+
"description": "Application Insights extensions for Microsoft BotBuilder.",
5+
"version": "4.1.6",
6+
"license": "MIT",
7+
"keywords": [
8+
"botbuilder",
9+
"botframework",
10+
"bots",
11+
"chatbots",
12+
"applicationinsights"
13+
],
14+
"bugs": {
15+
"url": "https://github.com/Microsoft/botbuilder-js/issues"
16+
},
17+
"repository": {
18+
"type": "git",
19+
"url": "https://github.com/Microsoft/botbuilder-js.git"
20+
},
21+
"main": "./lib/index.js",
22+
"typings": "./lib/index.d.ts",
23+
"dependencies": {
24+
"appinsights-usage": "^1.0.2",
25+
"applicationinsights": "^1.0.7",
26+
"applicationinsights-js": "^1.0.20",
27+
"botbuilder-core": "^4.1.6",
28+
"cls-hooked": "^4.2.2"
29+
},
30+
"devDependencies": {
31+
"@types/mocha": "^2.2.47",
32+
"assert": "^1.4.1",
33+
"chatdown": "^1.0.2",
34+
"codelyzer": "^4.1.0",
35+
"mocha": "^5.2.0",
36+
"nyc": "^11.4.1",
37+
"source-map-support": "^0.5.3",
38+
"ts-node": "^4.1.0",
39+
"unzip": "^0.1.11"
40+
},
41+
"scripts": {
42+
"test": "tsc && nyc mocha tests/",
43+
"build": "tsc",
44+
"build-docs": "typedoc --theme markdown --entryPoint botbuilder-applicationinsights --excludePrivate --includeDeclarations --ignoreCompilerErrors --module amd --out ..\\..\\doc\\botbuilder-applicationinsights .\\lib\\index.d.ts --hideGenerator --name \"Bot Builder SDK - Application Insights\" --readme none",
45+
"clean": "erase /q /s .\\lib",
46+
"set-version": "npm version --allow-same-version ${Version}"
47+
}
48+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/**
2+
* @module botbuilder-applicationinsights
3+
*/
4+
/**
5+
* Copyright (c) Microsoft Corporation. All rights reserved.
6+
* Licensed under the MIT License.
7+
*/
8+
import * as appInsights from 'applicationinsights';
9+
import { Activity, BotTelemetryClient, TelemetryDependency, TelemetryEvent, TelemetryException, TelemetryTrace } from 'botbuilder-core';
10+
import { clsHooked as cls } from 'cls-hooked';
11+
const ns: any = cls.createNamespace('my.request');
12+
13+
// This is the currently recommended work-around for using Application Insights with async/await
14+
// https://github.com/Microsoft/ApplicationInsights-node.js/issues/296
15+
// This allows AppInsights to automatically apply the appropriate context objects deep inside the async/await chain.
16+
// tslint:disable-next-line:no-submodule-imports
17+
import { CorrelationContext, CorrelationContextManager } from 'applicationinsights/out/AutoCollection/CorrelationContextManager';
18+
const origGetCurrentContext: any = CorrelationContextManager.getCurrentContext;
19+
20+
function getCurrentContext(): any {
21+
// tslint:disable-next-line:no-backbone-get-set-outside-model
22+
return ns.get('ctx') || origGetCurrentContext();
23+
}
24+
25+
// Overwrite the built-in getCurrentContext() method with a new one.
26+
CorrelationContextManager.getCurrentContext = getCurrentContext;
27+
28+
export const ApplicationInsightsWebserverMiddleware: any = (req: any, res: any, next: any): void => {
29+
30+
// Check to see if the request contains an incoming request.
31+
// If so, set it into the Application Insights context.
32+
const activity: Partial<Activity> = req.body;
33+
if (activity && activity.id) {
34+
const context: CorrelationContext = appInsights.getCorrelationContext();
35+
36+
// tslint:disable-next-line:no-string-literal
37+
context['activity'] = req.body;
38+
}
39+
40+
ns.bindEmitter(req);
41+
ns.bindEmitter(res);
42+
ns.run((): void => {
43+
// tslint:disable-next-line:no-backbone-get-set-outside-model
44+
ns.set('ctx', origGetCurrentContext());
45+
next();
46+
});
47+
48+
};
49+
50+
/* ApplicationInsightsTelemetryClient Class
51+
* This is a wrapper class around the Application Insights node client.
52+
* This is primarily designed to be used alongside the WaterfallDialog telemetry collection.
53+
* It provides a pre-configured App Insights client, and wrappers around
54+
* the major tracking functions, allowing it to conform to Botbuilder's generic BotTelemetryClient interface.
55+
* To use it, create pass in an instrumentation key:
56+
*
57+
* ```
58+
* const myDialog = new WaterfallDialog('my_dialog', steps);
59+
* const appInsightsClient = new ApplicationInsightsTelemetryClient(my_instrumentation_key);
60+
* myDialog.telemetryClient = appInsightsClient;
61+
* ```
62+
*/
63+
export class ApplicationInsightsTelemetryClient implements BotTelemetryClient {
64+
65+
private client: appInsights.TelemetryClient;
66+
private config: appInsights.Configuration;
67+
68+
/* The settings parameter is passed directly into appInsights.setup().
69+
* https://www.npmjs.com/package/applicationinsights#basic-usage
70+
* This function currently takes an app insights instrumentation key only.
71+
*/
72+
constructor(instrumentationKey: string) {
73+
74+
this.config = appInsights.setup(instrumentationKey)
75+
.setAutoDependencyCorrelation(true)
76+
.setAutoCollectRequests(true)
77+
.setAutoCollectPerformance(true)
78+
.setAutoCollectExceptions(true)
79+
.setAutoCollectDependencies(true)
80+
.start();
81+
82+
this.client = appInsights.defaultClient;
83+
84+
this.client.addTelemetryProcessor(addBotIdentifiers);
85+
}
86+
87+
/* configuration()
88+
* Provides access to the Application Insights configuration that is running here.
89+
* Allows developers to adjust the options, for example:
90+
* `appInsightsClient.configuration.setAutoCollectDependencies(false)`
91+
*/
92+
get configuration(): appInsights.Configuration {
93+
return this.config;
94+
}
95+
96+
/* defaultClient()
97+
* Provides direct access to the telemetry client object, which might be necessary for some operations.
98+
*/
99+
get defaultClient(): appInsights.TelemetryClient {
100+
return this.client;
101+
}
102+
103+
public trackDependency(telemetry: TelemetryDependency): void {
104+
this.defaultClient.trackDependency(telemetry as appInsights.Contracts.DependencyTelemetry);
105+
}
106+
107+
public trackEvent(telemetry: TelemetryEvent): void {
108+
this.defaultClient.trackEvent(telemetry as appInsights.Contracts.EventTelemetry);
109+
}
110+
111+
public trackException(telemetry: TelemetryException): void {
112+
this.defaultClient.trackException(telemetry as appInsights.Contracts.ExceptionTelemetry);
113+
}
114+
115+
public trackTrace(telemetry: TelemetryTrace): void {
116+
this.defaultClient.trackTrace(telemetry as appInsights.Contracts.TraceTelemetry);
117+
}
118+
119+
public flush(): void {
120+
this.defaultClient.flush();
121+
}
122+
}
123+
124+
/* Define the telemetry initializer function which is responsible for setting the userId. sessionId and some other values
125+
* so that application insights can correlate related events.
126+
*/
127+
function addBotIdentifiers(envelope: appInsights.Contracts.Envelope, context: { [name: string]: any }): boolean {
128+
if (context.correlationContext && context.correlationContext.activity) {
129+
const activity: Partial<Activity> = context.correlationContext.activity;
130+
// tslint:disable-next-line:no-string-literal
131+
const telemetryItem: any = envelope.data['baseData']; // TODO: update when envelope ts definition includes baseData
132+
const userId: string = activity.from ? activity.from.id : '';
133+
const channelId: string = activity.channelId || '';
134+
const conversationId: string = activity.conversation ? activity.conversation.id : '';
135+
136+
// set user id and session id
137+
envelope.tags[appInsights.defaultClient.context.keys.userId] = channelId + userId;
138+
envelope.tags[appInsights.defaultClient.context.keys.sessionId] = conversationId;
139+
140+
// Add additional properties
141+
telemetryItem.properties = telemetryItem.properties || {};
142+
telemetryItem.properties.activityId = activity.id;
143+
telemetryItem.properties.channelId = channelId;
144+
telemetryItem.properties.activityType = activity.type;
145+
}
146+
147+
return true;
148+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* @module botbuilder-applicationinsights
3+
*/
4+
/**
5+
* Copyright (c) Microsoft Corporation. All rights reserved.
6+
* Licensed under the MIT License.
7+
*/
8+
export * from './applicationInsightsTelemetryClient';

0 commit comments

Comments
 (0)