mirror of
https://github.com/coder/code-server.git
synced 2024-11-29 14:57:30 +03:00
a73549539b
Might make sense to cache the rest as well, and evict from the cache periodically. For now this is enough to fix a hang I often see in our deployment of Coder. Might only be surfacing now because new telemetry calls were added to startup.
164 lines
7.0 KiB
Diff
164 lines
7.0 KiB
Diff
Add support for telemetry endpoint
|
|
|
|
To test:
|
|
1. Create a mock API using [RequestBin](https://requestbin.io/) or [Beeceptor](https://beeceptor.com/)
|
|
2. Run code-server with `CS_TELEMETRY_URL` set:
|
|
i.e. `CS_TELEMETRY_URL="https://requestbin.io/1ebub9z1" ./code-server-<version>-macos-amd64/bin/code-server`
|
|
NOTE: it has to be a production build.
|
|
3. Load code-server in browser an do things (i.e. open a file)
|
|
4. Refresh RequestBin and you should see logs
|
|
|
|
Index: code-server/lib/vscode/src/vs/server/node/serverServices.ts
|
|
===================================================================
|
|
--- code-server.orig/lib/vscode/src/vs/server/node/serverServices.ts
|
|
+++ code-server/lib/vscode/src/vs/server/node/serverServices.ts
|
|
@@ -65,6 +65,7 @@ import { IExtensionsScannerService } fro
|
|
import { ExtensionsScannerService } from 'vs/server/node/extensionsScannerService';
|
|
import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService';
|
|
import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile';
|
|
+import { TelemetryClient } from 'vs/server/node/telemetryClient';
|
|
import { NullPolicyService } from 'vs/platform/policy/common/policy';
|
|
import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender';
|
|
import { LoggerService } from 'vs/platform/log/node/loggerService';
|
|
@@ -147,11 +148,23 @@ export async function setupServerService
|
|
const requestService = new RequestService(configurationService, environmentService, logService, loggerService);
|
|
services.set(IRequestService, requestService);
|
|
|
|
+ let isContainer = undefined;
|
|
+ try {
|
|
+ await Promises.stat('/run/.containerenv');
|
|
+ isContainer = true;
|
|
+ } catch (error) { /* Does not exist, probably. */ }
|
|
+ if (!isContainer) {
|
|
+ try {
|
|
+ const content = await Promises.readFile('/proc/self/cgroup', 'utf8')
|
|
+ isContainer = content.includes('docker');
|
|
+ } catch (error) { /* Permission denied, probably. */ }
|
|
+ }
|
|
+
|
|
let oneDsAppender: ITelemetryAppender = NullAppender;
|
|
const isInternal = isInternalTelemetry(productService, configurationService);
|
|
if (supportsTelemetry(productService, environmentService)) {
|
|
- if (!isLoggingOnly(productService, environmentService) && productService.aiConfig?.ariaKey) {
|
|
- oneDsAppender = new OneDataSystemAppender(requestService, isInternal, eventPrefix, null, productService.aiConfig.ariaKey);
|
|
+ if (!isLoggingOnly(productService, environmentService) && productService.telemetryEndpoint) {
|
|
+ oneDsAppender = new OneDataSystemAppender(requestService, isInternal, eventPrefix, null, () => new TelemetryClient(productService.telemetryEndpoint!, machineId, isContainer));
|
|
disposables.add(toDisposable(() => oneDsAppender?.flush())); // Ensure the AI appender is disposed so that it flushes remaining data
|
|
}
|
|
|
|
Index: code-server/lib/vscode/src/vs/server/node/telemetryClient.ts
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ code-server/lib/vscode/src/vs/server/node/telemetryClient.ts
|
|
@@ -0,0 +1,71 @@
|
|
+import { AppInsightsCore, IExtendedTelemetryItem, ITelemetryItem } from '@microsoft/1ds-core-js';
|
|
+import * as https from 'https';
|
|
+import * as http from 'http';
|
|
+import * as os from 'os';
|
|
+
|
|
+interface SystemInfo {
|
|
+ measurements: Record<string, number | undefined>;
|
|
+ properties: Record<string, string | boolean | null | undefined>;
|
|
+}
|
|
+
|
|
+export class TelemetryClient extends AppInsightsCore {
|
|
+ private readonly systemInfo: SystemInfo = {
|
|
+ measurements: {},
|
|
+ properties: {},
|
|
+ };
|
|
+
|
|
+ public constructor(
|
|
+ private readonly endpoint: string,
|
|
+ machineId: string,
|
|
+ isContainer: boolean | undefined) {
|
|
+ super();
|
|
+
|
|
+ // os.cpus() can take a very long time sometimes (personally I see 1-2
|
|
+ // seconds in a Coder workspace). This adds up significantly, especially
|
|
+ // when many telemetry requests are sent during startup, which can cause
|
|
+ // connection timeouts. Try to cache as much as we can.
|
|
+ try {
|
|
+ const cpus = os.cpus();
|
|
+ this.systemInfo.measurements.cores = cpus.length;
|
|
+ this.systemInfo.properties['common.cpuModel'] = cpus[0].model;
|
|
+ } catch (error) {}
|
|
+
|
|
+ try {
|
|
+ this.systemInfo.properties['common.shell'] = os.userInfo().shell;
|
|
+ this.systemInfo.properties['common.release'] = os.release();
|
|
+ this.systemInfo.properties['common.arch'] = os.arch();
|
|
+ } catch (error) {}
|
|
+
|
|
+ this.systemInfo.properties['common.remoteMachineId'] = machineId;
|
|
+ this.systemInfo.properties['common.isContainer'] = isContainer;
|
|
+ }
|
|
+
|
|
+ public override track(item: IExtendedTelemetryItem | ITelemetryItem): void {
|
|
+ const options = item.baseData || {}
|
|
+ options.measurements = {
|
|
+ ...(options.measurements || {}),
|
|
+ ...this.systemInfo.measurements,
|
|
+ }
|
|
+ options.properties = {
|
|
+ ...(options.properties || {}),
|
|
+ ...this.systemInfo.properties,
|
|
+ }
|
|
+
|
|
+ try {
|
|
+ options.measurements.memoryFree = os.freemem();
|
|
+ options.measurements.memoryTotal = os.totalmem();
|
|
+ } catch (error) {}
|
|
+
|
|
+ try {
|
|
+ const request = (/^http:/.test(this.endpoint) ? http : https).request(this.endpoint, {
|
|
+ method: 'POST',
|
|
+ headers: {
|
|
+ 'Content-Type': 'application/json',
|
|
+ },
|
|
+ });
|
|
+ request.on('error', () => { /* We don't care. */ });
|
|
+ request.write(JSON.stringify(options));
|
|
+ request.end();
|
|
+ } catch (error) {}
|
|
+ }
|
|
+}
|
|
Index: code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
|
===================================================================
|
|
--- code-server.orig/lib/vscode/src/vs/server/node/webClientServer.ts
|
|
+++ code-server/lib/vscode/src/vs/server/node/webClientServer.ts
|
|
@@ -318,6 +318,8 @@ export class WebClientServer {
|
|
scope: vscodeBase + '/',
|
|
path: base + '/_static/out/browser/serviceWorker.js',
|
|
},
|
|
+ enableTelemetry: this._productService.enableTelemetry,
|
|
+ telemetryEndpoint: this._productService.telemetryEndpoint,
|
|
embedderIdentifier: 'server-distro',
|
|
extensionsGallery: this._productService.extensionsGallery,
|
|
} satisfies Partial<IProductConfiguration>;
|
|
Index: code-server/lib/vscode/src/vs/base/common/product.ts
|
|
===================================================================
|
|
--- code-server.orig/lib/vscode/src/vs/base/common/product.ts
|
|
+++ code-server/lib/vscode/src/vs/base/common/product.ts
|
|
@@ -64,6 +64,7 @@ export interface IProductConfiguration {
|
|
readonly path: string;
|
|
readonly scope: string;
|
|
}
|
|
+ readonly telemetryEndpoint?: string
|
|
|
|
readonly version: string;
|
|
readonly date?: string;
|
|
Index: code-server/lib/vscode/src/vs/platform/product/common/product.ts
|
|
===================================================================
|
|
--- code-server.orig/lib/vscode/src/vs/platform/product/common/product.ts
|
|
+++ code-server/lib/vscode/src/vs/platform/product/common/product.ts
|
|
@@ -55,7 +55,8 @@ else if (globalThis._VSCODE_PRODUCT_JSON
|
|
resourceUrlTemplate: "https://open-vsx.org/vscode/asset/{publisher}/{name}/{version}/Microsoft.VisualStudio.Code.WebResources/{path}",
|
|
controlUrl: "",
|
|
recommendationsUrl: "",
|
|
- })
|
|
+ }),
|
|
+ telemetryEndpoint: env.CS_TELEMETRY_URL || product.telemetryEndpoint || "https://v1.telemetry.coder.com/track",
|
|
});
|
|
}
|
|
|