Telemetry
This commit is contained in:
@ -15,6 +15,7 @@ import { ILogService } from "vs/platform/log/common/log";
|
||||
import pkg from "vs/platform/product/node/package";
|
||||
import product from "vs/platform/product/node/product";
|
||||
import { IRemoteAgentEnvironment } from "vs/platform/remote/common/remoteAgentEnvironment";
|
||||
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
|
||||
import { ExtensionScanner, ExtensionScannerInput } from "vs/workbench/services/extensions/node/extensionPoints";
|
||||
import { DiskFileSystemProvider } from "vs/workbench/services/files/node/diskFileSystemProvider";
|
||||
|
||||
@ -181,6 +182,7 @@ export class ExtensionEnvironmentChannel implements IServerChannel {
|
||||
public constructor(
|
||||
private readonly environment: IEnvironmentService,
|
||||
private readonly log: ILogService,
|
||||
private readonly telemetry: ITelemetryService,
|
||||
) {}
|
||||
|
||||
public listen(_: unknown, event: string): Event<any> {
|
||||
@ -271,7 +273,7 @@ export class ExtensionEnvironmentChannel implements IServerChannel {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
private disableTelemetry(): Promise<void> {
|
||||
throw new Error("not implemented");
|
||||
private async disableTelemetry(): Promise<void> {
|
||||
this.telemetry.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,8 @@ import { validatePaths } from "vs/code/node/paths";
|
||||
import { parseMainProcessArgv } from "vs/platform/environment/node/argvHelper";
|
||||
import { ParsedArgs } from "vs/platform/environment/common/environment";
|
||||
import { buildHelpMessage, buildVersionMessage, options } from "vs/platform/environment/node/argv";
|
||||
import product from "vs/platform/product/node/product";
|
||||
import pkg from "vs/platform/product/node/package";
|
||||
import product from "vs/platform/product/node/product";
|
||||
|
||||
import { MainServer, WebviewServer } from "vs/server/src/server";
|
||||
import "vs/server/src/tar";
|
||||
|
155
src/insights.ts
155
src/insights.ts
@ -1,25 +1,16 @@
|
||||
/**
|
||||
* Used by node
|
||||
*/
|
||||
import * as https from "https";
|
||||
import * as os from "os";
|
||||
|
||||
export const defaultClient = "filler";
|
||||
import * as appInsights from "applicationinsights";
|
||||
|
||||
export class TelemetryClient implements appInsights.TelemetryClient {
|
||||
public config: any = {};
|
||||
|
||||
export class TelemetryClient {
|
||||
public channel = {
|
||||
setUseDiskRetryCaching: (): void => undefined,
|
||||
};
|
||||
|
||||
public constructor() {
|
||||
//
|
||||
}
|
||||
|
||||
public trackEvent(options: {
|
||||
name: string;
|
||||
properties: object;
|
||||
measurements: object;
|
||||
}): void {
|
||||
public trackEvent(options: appInsights.EventTelemetry): void {
|
||||
if (!options.properties) {
|
||||
options.properties = {};
|
||||
}
|
||||
@ -29,41 +20,20 @@ export class TelemetryClient {
|
||||
|
||||
try {
|
||||
const cpus = os.cpus();
|
||||
// tslint:disable-next-line:no-any
|
||||
(options.measurements as any).cpu = {
|
||||
model: cpus[0].model,
|
||||
cores: cpus.length,
|
||||
};
|
||||
} catch (ex) {
|
||||
// Nothin
|
||||
}
|
||||
options.measurements.cores = cpus.length;
|
||||
options.properties["common.cpuModel"] = cpus[0].model;
|
||||
} catch (error) {}
|
||||
|
||||
try {
|
||||
// tslint:disable-next-line:no-any
|
||||
(options.measurements as any).memory = {
|
||||
virtual_free: os.freemem(),
|
||||
virtual_used: os.totalmem(),
|
||||
};
|
||||
} catch (ex) {
|
||||
//
|
||||
}
|
||||
options.measurements.memoryFree = os.freemem();
|
||||
options.measurements.memoryTotal = os.totalmem();
|
||||
} catch (error) {}
|
||||
|
||||
try {
|
||||
// tslint:disable:no-any
|
||||
(options.properties as any)["common.shell"] = os.userInfo().shell;
|
||||
(options.properties as any)["common.release"] = os.release();
|
||||
(options.properties as any)["common.arch"] = os.arch();
|
||||
// tslint:enable:no-any
|
||||
} catch (ex) {
|
||||
//
|
||||
}
|
||||
|
||||
try {
|
||||
// tslint:disable-next-line:no-any
|
||||
(options.properties as any)["common.machineId"] = machineIdSync();
|
||||
} catch (ex) {
|
||||
//
|
||||
}
|
||||
options.properties["common.shell"] = os.userInfo().shell;
|
||||
options.properties["common.release"] = os.release();
|
||||
options.properties["common.arch"] = os.arch();
|
||||
} catch (error) {}
|
||||
|
||||
try {
|
||||
const request = https.request({
|
||||
@ -75,96 +45,15 @@ export class TelemetryClient {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
request.on("error", () => {
|
||||
// Do nothing, we don"t really care
|
||||
});
|
||||
request.on("error", () => { /* We don't care. */ });
|
||||
request.write(JSON.stringify(options));
|
||||
request.end();
|
||||
} catch (ex) {
|
||||
// Suppress all errs
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
public flush(options: appInsights.FlushOptions): void {
|
||||
if (options.callback) {
|
||||
options.callback("");
|
||||
}
|
||||
}
|
||||
|
||||
public flush(options: {
|
||||
readonly callback: () => void;
|
||||
}): void {
|
||||
options.callback();
|
||||
}
|
||||
}
|
||||
|
||||
// Taken from https://github.com/automation-stack/node-machine-id
|
||||
import { exec, execSync } from "child_process";
|
||||
import { createHash } from "crypto";
|
||||
|
||||
const isWindowsProcessMixedOrNativeArchitecture = (): "" | "mixed" | "native" => {
|
||||
// detect if the node binary is the same arch as the Windows OS.
|
||||
// or if this is 32 bit node on 64 bit windows.
|
||||
if (process.platform !== "win32") {
|
||||
return "";
|
||||
}
|
||||
if (process.arch === "ia32" && process.env.hasOwnProperty("PROCESSOR_ARCHITEW6432")) {
|
||||
return "mixed";
|
||||
}
|
||||
|
||||
return "native";
|
||||
};
|
||||
|
||||
let { platform } = process,
|
||||
win32RegBinPath = {
|
||||
native: "%windir%\\System32",
|
||||
mixed: "%windir%\\sysnative\\cmd.exe /c %windir%\\System32",
|
||||
"": "",
|
||||
},
|
||||
guid = {
|
||||
darwin: "ioreg -rd1 -c IOPlatformExpertDevice",
|
||||
win32: `${win32RegBinPath[isWindowsProcessMixedOrNativeArchitecture()]}\\REG ` +
|
||||
"QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography " +
|
||||
"/v MachineGuid",
|
||||
linux: "( cat /var/lib/dbus/machine-id /etc/machine-id 2> /dev/null || hostname ) | head -n 1 || :",
|
||||
freebsd: "kenv -q smbios.system.uuid || sysctl -n kern.hostuuid",
|
||||
// tslint:disable-next-line:no-any
|
||||
} as any;
|
||||
|
||||
const hash = (guid: string): string => {
|
||||
return createHash("sha256").update(guid).digest("hex");
|
||||
};
|
||||
|
||||
const expose = (result: string): string => {
|
||||
switch (platform) {
|
||||
case "darwin":
|
||||
return result
|
||||
.split("IOPlatformUUID")[1]
|
||||
.split("\n")[0].replace(/\=|\s+|\"/ig, "")
|
||||
.toLowerCase();
|
||||
case "win32":
|
||||
return result
|
||||
.toString()
|
||||
.split("REG_SZ")[1]
|
||||
.replace(/\r+|\n+|\s+/ig, "")
|
||||
.toLowerCase();
|
||||
case "linux":
|
||||
return result
|
||||
.toString()
|
||||
.replace(/\r+|\n+|\s+/ig, "")
|
||||
.toLowerCase();
|
||||
case "freebsd":
|
||||
return result
|
||||
.toString()
|
||||
.replace(/\r+|\n+|\s+/ig, "")
|
||||
.toLowerCase();
|
||||
default:
|
||||
throw new Error(`Unsupported platform: ${process.platform}`);
|
||||
}
|
||||
};
|
||||
|
||||
let cachedMachineId: string | undefined;
|
||||
|
||||
const machineIdSync = (): string => {
|
||||
if (cachedMachineId) {
|
||||
return cachedMachineId;
|
||||
}
|
||||
let id: string = expose(execSync(guid[platform]).toString());
|
||||
cachedMachineId = hash(id);
|
||||
|
||||
return cachedMachineId;
|
||||
};
|
||||
|
115
src/server.ts
115
src/server.ts
@ -11,6 +11,7 @@ import * as querystring from "querystring";
|
||||
import { Emitter } from "vs/base/common/event";
|
||||
import { sanitizeFilePath } from "vs/base/common/extpath";
|
||||
import { UriComponents, URI } from "vs/base/common/uri";
|
||||
import { getMachineId } from 'vs/base/node/id';
|
||||
import { IPCServer, ClientConnectionEvent, StaticRouter } from "vs/base/parts/ipc/common/ipc";
|
||||
import { mkdirp } from "vs/base/node/pfs";
|
||||
import { LogsDataCleaner } from "vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner";
|
||||
@ -34,19 +35,25 @@ import { getLogLevel, ILogService } from "vs/platform/log/common/log";
|
||||
import { LogLevelSetterChannel } from "vs/platform/log/common/logIpc";
|
||||
import { SpdLogService } from "vs/platform/log/node/spdlogService";
|
||||
import { IProductConfiguration } from "vs/platform/product/common/product";
|
||||
import pkg from "vs/platform/product/node/package";
|
||||
import product from "vs/platform/product/node/product";
|
||||
import { ConnectionType, ConnectionTypeRequest } from "vs/platform/remote/common/remoteAgentConnection";
|
||||
import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from "vs/platform/remote/common/remoteAgentFileSystemChannel";
|
||||
import { IRequestService } from "vs/platform/request/node/request";
|
||||
import { RequestService } from "vs/platform/request/node/requestService";
|
||||
import ErrorTelemetry from "vs/platform/telemetry/browser/errorTelemetry";
|
||||
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
|
||||
import { NullTelemetryService } from "vs/platform/telemetry/common/telemetryUtils";
|
||||
import { NullTelemetryService, LogAppender, combinedAppender } from "vs/platform/telemetry/common/telemetryUtils";
|
||||
import { TelemetryService, ITelemetryServiceConfig } from "vs/platform/telemetry/common/telemetryService";
|
||||
import { AppInsightsAppender } from "vs/platform/telemetry/node/appInsightsAppender";
|
||||
import { resolveCommonProperties } from "vs/platform/telemetry/node/commonProperties";
|
||||
import { RemoteExtensionLogFileName } from "vs/workbench/services/remote/common/remoteAgentService";
|
||||
// import { TelemetryService } from "vs/workbench/services/telemetry/electron-browser/telemetryService";
|
||||
import { TelemetryChannel } from "vs/platform/telemetry/node/telemetryIpc";
|
||||
import { IWorkbenchConstructionOptions } from "vs/workbench/workbench.web.api";
|
||||
|
||||
import { Connection, ManagementConnection, ExtensionHostConnection } from "vs/server/src/connection";
|
||||
import { ExtensionEnvironmentChannel, FileProviderChannel , } from "vs/server/src/channel";
|
||||
import { TelemetryClient } from "vs/server/src/insights";
|
||||
import { Protocol } from "vs/server/src/protocol";
|
||||
import { getMediaMime, getUriTransformer, useHttpsTransformer } from "vs/server/src/util";
|
||||
|
||||
@ -363,6 +370,7 @@ export class MainServer extends Server {
|
||||
private readonly connections = new Map<ConnectionType, Map<string, Connection>>();
|
||||
|
||||
private readonly services = new ServiceCollection();
|
||||
private readonly servicesPromise: Promise<void>;
|
||||
|
||||
public constructor(
|
||||
options: ServerOptions,
|
||||
@ -381,39 +389,7 @@ export class MainServer extends Server {
|
||||
protocol.getSocket().dispose();
|
||||
}
|
||||
});
|
||||
|
||||
const environmentService = new EnvironmentService(args, process.execPath);
|
||||
const logService = new SpdLogService(RemoteExtensionLogFileName, environmentService.logsPath, getLogLevel(environmentService));
|
||||
this.ipc.registerChannel("loglevel", new LogLevelSetterChannel(logService));
|
||||
|
||||
const router = new StaticRouter((context: any) => {
|
||||
return context.clientId === "renderer";
|
||||
});
|
||||
|
||||
this.services.set(ILogService, logService);
|
||||
this.services.set(IEnvironmentService, environmentService);
|
||||
this.services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.machineSettingsResource]));
|
||||
this.services.set(IRequestService, new SyncDescriptor(RequestService));
|
||||
this.services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
|
||||
this.services.set(ITelemetryService, NullTelemetryService); // TODO: telemetry
|
||||
this.services.set(IDialogService, new DialogChannelClient(this.ipc.getChannel("dialog", router)));
|
||||
this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
|
||||
|
||||
const instantiationService = new InstantiationService(this.services);
|
||||
|
||||
this.services.set(ILocalizationsService, instantiationService.createInstance(LocalizationsService));
|
||||
|
||||
instantiationService.invokeFunction(() => {
|
||||
instantiationService.createInstance(LogsDataCleaner);
|
||||
this.ipc.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, new FileProviderChannel(environmentService, logService));
|
||||
this.ipc.registerChannel("remoteextensionsenvironment", new ExtensionEnvironmentChannel(environmentService, logService));
|
||||
const extensionsService = this.services.get(IExtensionManagementService) as IExtensionManagementService;
|
||||
const extensionsChannel = new ExtensionManagementChannel(extensionsService, (context) => getUriTransformer(context.remoteAuthority));
|
||||
this.ipc.registerChannel("extensions", extensionsChannel);
|
||||
const galleryService = this.services.get(IExtensionGalleryService) as IExtensionGalleryService;
|
||||
const galleryChannel = new ExtensionGalleryChannel(galleryService);
|
||||
this.ipc.registerChannel("gallery", galleryChannel);
|
||||
});
|
||||
this.servicesPromise = this.initializeServices(args);
|
||||
}
|
||||
|
||||
public async listen(): Promise<string> {
|
||||
@ -456,7 +432,11 @@ export class MainServer extends Server {
|
||||
const remoteAuthority = request.headers.host as string;
|
||||
const transformer = getUriTransformer(remoteAuthority);
|
||||
|
||||
await this.webviewServer.listen();
|
||||
await Promise.all([
|
||||
this.webviewServer.listen(),
|
||||
this.servicesPromise,
|
||||
]);
|
||||
|
||||
const webviewEndpoint = this.webviewServer.address(request);
|
||||
|
||||
const cwd = process.env.VSCODE_CWD || process.cwd();
|
||||
@ -577,6 +557,69 @@ export class MainServer extends Server {
|
||||
}
|
||||
}
|
||||
|
||||
private async initializeServices(args: ParsedArgs): Promise<void> {
|
||||
const environmentService = new EnvironmentService(args, process.execPath);
|
||||
const logService = new SpdLogService(RemoteExtensionLogFileName, environmentService.logsPath, getLogLevel(environmentService));
|
||||
this.ipc.registerChannel("loglevel", new LogLevelSetterChannel(logService));
|
||||
|
||||
const router = new StaticRouter((context: any) => {
|
||||
return context.clientId === "renderer";
|
||||
});
|
||||
|
||||
this.services.set(ILogService, logService);
|
||||
this.services.set(IEnvironmentService, environmentService);
|
||||
this.services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.machineSettingsResource]));
|
||||
this.services.set(IRequestService, new SyncDescriptor(RequestService));
|
||||
this.services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
|
||||
if (!environmentService.args["disable-telemetry"]) {
|
||||
const version = `${(pkg as any).codeServerVersion || "development"}-vsc${pkg.version}`;
|
||||
this.services.set(ITelemetryService, new SyncDescriptor(TelemetryService, [{
|
||||
appender: combinedAppender(
|
||||
new AppInsightsAppender("code-server", null, () => new TelemetryClient(), logService),
|
||||
new LogAppender(logService),
|
||||
),
|
||||
commonProperties: resolveCommonProperties(
|
||||
product.commit, version, await getMachineId(),
|
||||
environmentService.installSourcePath, "code-server",
|
||||
),
|
||||
piiPaths: [
|
||||
environmentService.appRoot,
|
||||
environmentService.extensionsPath,
|
||||
...environmentService.extraExtensionPaths,
|
||||
...environmentService.extraBuiltinExtensionPaths,
|
||||
],
|
||||
} as ITelemetryServiceConfig]));
|
||||
} else {
|
||||
this.services.set(ITelemetryService, NullTelemetryService);
|
||||
}
|
||||
this.services.set(IDialogService, new DialogChannelClient(this.ipc.getChannel("dialog", router)));
|
||||
this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
|
||||
|
||||
const instantiationService = new InstantiationService(this.services);
|
||||
|
||||
this.services.set(ILocalizationsService, instantiationService.createInstance(LocalizationsService));
|
||||
|
||||
return new Promise((resolve) => {
|
||||
instantiationService.invokeFunction(() => {
|
||||
instantiationService.createInstance(LogsDataCleaner);
|
||||
this.ipc.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, new FileProviderChannel(environmentService, logService));
|
||||
const telemetryService = this.services.get(ITelemetryService) as ITelemetryService;
|
||||
this.ipc.registerChannel("remoteextensionsenvironment", new ExtensionEnvironmentChannel(environmentService, logService, telemetryService));
|
||||
const extensionsService = this.services.get(IExtensionManagementService) as IExtensionManagementService;
|
||||
const extensionsChannel = new ExtensionManagementChannel(extensionsService, (context) => getUriTransformer(context.remoteAuthority));
|
||||
this.ipc.registerChannel("extensions", extensionsChannel);
|
||||
const galleryService = this.services.get(IExtensionGalleryService) as IExtensionGalleryService;
|
||||
const galleryChannel = new ExtensionGalleryChannel(galleryService);
|
||||
this.ipc.registerChannel("gallery", galleryChannel);
|
||||
const telemetryChannel = new TelemetryChannel(telemetryService);
|
||||
this.ipc.registerChannel("telemetry", telemetryChannel);
|
||||
// tslint:disable-next-line no-unused-expression
|
||||
new ErrorTelemetry(telemetryService);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: implement.
|
||||
*/
|
||||
|
Reference in New Issue
Block a user