Skip to content

Commit eaf6aac

Browse files
committed
Initial support for 1ds sdk
1 parent c77fda6 commit eaf6aac

File tree

6 files changed

+278
-6
lines changed

6 files changed

+278
-6
lines changed

package-lock.json

Lines changed: 103 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
"compile": "tsc -noEmit -p 'src/browser/tsconfig.json' && tsc -noEmit -p 'src/node/tsconfig.json' && npm run build"
1919
},
2020
"devDependencies": {
21+
"@microsoft/1ds-core-js": "^3.2.3",
22+
"@microsoft/1ds-post-js": "^3.2.3",
2123
"@microsoft/applicationinsights-web": "^2.6.4",
2224
"@types/mocha": "^9.1.1",
2325
"@types/node": "^16.11.32",

src/browser/1dsTelemetryReporter.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*---------------------------------------------------------
2+
* Copyright (C) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------*/
4+
5+
import * as vscode from "vscode";
6+
import { BaseTelemetryAppender } from "../common/baseTelemetryAppender";
7+
import { BaseTelemetryReporter, ReplacementOption } from "../common/baseTelemetryReporter";
8+
import { oneDataSystemClientFactory } from "../common/1dsClientFactory";
9+
10+
11+
export default class TelemetryReporter extends BaseTelemetryReporter {
12+
// TODO add ReplacementOption support
13+
constructor(extensionId: string, extensionVersion: string, key: string, firstParty?: boolean, _replacementOptions?: ReplacementOption[]) {
14+
const appender = new BaseTelemetryAppender(key, key => oneDataSystemClientFactory(key));
15+
// First party is always true for 1DS
16+
firstParty = true;
17+
super(extensionId, extensionVersion, appender, {
18+
release: navigator.appVersion,
19+
platform: "web",
20+
architecture: "web",
21+
}, vscode, firstParty);
22+
}
23+
}
24+

src/common/1dsClientFactory.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import type { AppInsightsCore, IExtendedConfiguration } from "@microsoft/1ds-core-js";
7+
import type { IChannelConfiguration, IXHROverride, PostChannel } from "@microsoft/1ds-post-js";
8+
import type { BaseTelemetryClient } from "./baseTelemetryAppender";
9+
import { AppenderData } from "./baseTelemetryReporter";
10+
11+
/**
12+
* Configures 1DS properly and returns the core client object
13+
* @param key The ingestion key
14+
* @param xhrOverride An optional override to use for requests instead of the XHTMLRequest object. Useful for node environments
15+
* @returns The AI core object
16+
*/
17+
const getAICore = async (key: string, xhrOverride?: IXHROverride): Promise<AppInsightsCore> => {
18+
const oneDs = await import("@microsoft/1ds-core-js");
19+
const postPlugin = await import("@microsoft/1ds-post-js");
20+
const appInsightsCore = new oneDs.AppInsightsCore();
21+
const collectorChannelPlugin: PostChannel = new postPlugin.PostChannel();
22+
// Configure the app insights core to send to collector++ and disable logging of debug info
23+
const coreConfig: IExtendedConfiguration = {
24+
instrumentationKey: key,
25+
endpointUrl: "https://mobile.events.data.microsoft.com/OneCollector/1.0",
26+
loggingLevelTelemetry: 0,
27+
loggingLevelConsole: 0,
28+
disableCookiesUsage: true,
29+
disableDbgExt: true,
30+
disableInstrumentationKeyValidation: true,
31+
channels: [[
32+
collectorChannelPlugin
33+
]]
34+
};
35+
36+
if (xhrOverride) {
37+
coreConfig.extensionConfig = {};
38+
// Configure the channel to use a XHR Request override since it's not available in node
39+
const channelConfig: IChannelConfiguration = {
40+
alwaysUseXhrOverride: true,
41+
httpXHROverride: xhrOverride
42+
};
43+
coreConfig.extensionConfig[collectorChannelPlugin.identifier] = channelConfig;
44+
}
45+
46+
appInsightsCore.initialize(coreConfig, []);
47+
48+
appInsightsCore.addTelemetryInitializer((envelope) => {
49+
envelope["ext"] = envelope["ext"] ?? {};
50+
envelope["ext"]["utc"] = envelope["ext"]["utc"] ?? {};
51+
// Sets it to be internal only based on Windows UTC flagging
52+
envelope["ext"]["utc"]["flags"] = 0x0000811ECD;
53+
});
54+
55+
return appInsightsCore;
56+
};
57+
58+
/**
59+
* Configures and creates a telemetry client using the 1DS sdk
60+
* @param key The ingestion key
61+
* @param xhrOverride An optional override to use for requests instead of the XHTMLRequest object. Useful for node environments
62+
*/
63+
export const oneDataSystemClientFactory = async (key: string, xhrOverride?: IXHROverride): Promise<BaseTelemetryClient> => {
64+
const appInsightsCore = await getAICore(key, xhrOverride);
65+
// Shape the app insights core from 1DS into a standard format
66+
const telemetryClient: BaseTelemetryClient = {
67+
logEvent: (eventName: string, data?: AppenderData) => {
68+
try {
69+
appInsightsCore?.track({
70+
name: eventName,
71+
data: { ...data?.properties, ...data?.measurements },
72+
});
73+
} catch (e: any) {
74+
throw new Error("Failed to log event to app insights!\n" + e.message);
75+
}
76+
},
77+
logException: (_exception: Error, _data?: AppenderData) => {
78+
throw new Error("1DS SDK does not support logging exceptions, please use logEvent for exception tracking");
79+
},
80+
flush: async () => {
81+
try {
82+
appInsightsCore?.unload();
83+
} catch (e: any) {
84+
throw new Error("Failed to flush app insights!\n" + e.message);
85+
}
86+
}
87+
};
88+
return telemetryClient;
89+
};

src/node/1dsTelemetryReporter.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*---------------------------------------------------------
2+
* Copyright (C) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------*/
4+
5+
import type { IPayloadData, IXHROverride } from "@microsoft/1ds-post-js";
6+
import * as os from "os";
7+
import * as https from "https";
8+
import * as vscode from "vscode";
9+
import { BaseTelemetryAppender } from "../common/baseTelemetryAppender";
10+
import { BaseTelemetryReporter, ReplacementOption } from "../common/baseTelemetryReporter";
11+
import { oneDataSystemClientFactory } from "../common/1dsClientFactory";
12+
13+
14+
function getXHROverride() {
15+
// Override the way events get sent since node doesn't have XHTMLRequest
16+
const customHttpXHROverride: IXHROverride = {
17+
sendPOST: (payload: IPayloadData, oncomplete) => {
18+
const options = {
19+
method: "POST",
20+
headers: {
21+
...payload.headers,
22+
"Content-Type": "application/json",
23+
"Content-Length": Buffer.byteLength(payload.data)
24+
}
25+
};
26+
try {
27+
const req = https.request(payload.urlString, options, res => {
28+
res.on("data", function (responseData) {
29+
oncomplete(res.statusCode ?? 200, res.headers as Record<string, any>, responseData.toString());
30+
});
31+
// On response with error send status of 0 and a blank response to oncomplete so we can retry events
32+
res.on("error", function () {
33+
oncomplete(0, {});
34+
});
35+
});
36+
req.write(payload.data);
37+
req.end();
38+
} catch {
39+
// If it errors out, send status of 0 and a blank response to oncomplete so we can retry events
40+
oncomplete(0, {});
41+
}
42+
}
43+
};
44+
return customHttpXHROverride;
45+
}
46+
47+
export default class TelemetryReporter extends BaseTelemetryReporter {
48+
// TODO add ReplacementOption support
49+
constructor(extensionId: string, extensionVersion: string, key: string, firstParty?: boolean, _replacementOptions?: ReplacementOption[]) {
50+
const appender = new BaseTelemetryAppender(key, (key) => oneDataSystemClientFactory(key, getXHROverride()));
51+
// 1DS only supports first party so this is always true
52+
firstParty = true;
53+
super(extensionId, extensionVersion, appender, {
54+
release: os.release(),
55+
platform: os.platform(),
56+
architecture: os.arch(),
57+
}, vscode, firstParty);
58+
}
59+
}

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"noEmitOnError": false,
44
"strict": true,
55
"target": "ES2020",
6+
"module": "ES2020",
67
"esModuleInterop": true,
78
"forceConsistentCasingInFileNames": true,
89
"sourceMap": true,

0 commit comments

Comments
 (0)