Telemetry

This commit is contained in:
Asher 2019-07-16 14:57:02 -05:00
parent 1a3fc86894
commit b6fdb7d0e7
No known key found for this signature in database
GPG Key ID: D63C1EF81242354A
7 changed files with 380 additions and 299 deletions

View File

@ -1,5 +1,4 @@
# code-server
[!["Open Issues"](https://img.shields.io/github/issues-raw/cdr/code-server.svg)](https://github.com/cdr/code-server/issues)
[!["Latest Release"](https://img.shields.io/github/release/cdr/code-server.svg)](https://github.com/cdr/code-server/releases/latest)
[![MIT license](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/cdr/code-server/blob/master/LICENSE)
@ -23,18 +22,14 @@ docker run -it -p 127.0.0.1:8443:8443 -p 127.0.0.1:8444:8444 -v "$PWD:/home/code
![Screenshot](/doc/assets/ide.png)
## Getting Started
### Run over SSH
Use [sshcode](https://github.com/codercom/sshcode) for a simple setup.
### Docker
See docker oneliner mentioned above. Dockerfile is at
[/Dockerfile](/Dockerfile).
### Binaries
1. [Download a binary](https://github.com/cdr/code-server/releases) (Linux and
OS X supported. Windows coming soon)
2. Start the binary with the project directory as the first argument
@ -65,28 +60,15 @@ How to [secure your setup](/doc/security/ssl.md).
compile the build directory as well.
- For now `@coder/nbin` is a global dependency.
- Run `yarn build ${codeServerVersion} ${vscodeVersion} ${target} ${arch}` in
this directory (for example: `yarn build development 1.35.0 linux x64`).
this directory (for example: `yarn build development 1.36.0 linux x64`).
- If you target the same VS Code version our Travis builds do everything will
work but if you target some other version it might not (we have to do some
patching to VS Code so different versions aren't always compatible).
- You can run the built code with `node path/to/build/out/vs/server/main.js` or run
`yarn binary` with the same arguments in the previous step to package the
code into a single binary.
### Development
```fish
git clone https://github.com/microsoft/vscode
cd vscode
git clone https://github.com/cdr/code-server src/vs/server
cd src/vs/server
yarn patch:apply
yarn
yarn watch
# Wait for the initial compilation to complete (it will say "Finished compilation").
yarn start --allow-http --no-auth
# Visit http://localhost:8443
```
## Known Issues
- Creating custom VS Code extensions and debugging them doesn't work.
- To debug Golang using
[ms-vscode-go extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.Go),
@ -102,31 +84,48 @@ yarn start --allow-http --no-auth
- Run VS Code unit tests against our builds to ensure features work as expected.
## Extensions
At the moment we can't use the official VSCode Marketplace. We've created a
custom extension marketplace focused around open-sourced extensions. However,
if you have access to the `.vsix` file, you can manually install the extension.
## Telemetry
Use the `--disable-telemetry` flag to completely disable telemetry.
Set the `telemetry.enableTelemetry` user setting to false to disable telemetry.
We use data collected to improve code-server.
We use the data collected to improve code-server.
## Contributing
Development guides are coming soon.
### Development
```fish
git clone https://github.com/microsoft/vscode
cd vscode
git clone https://github.com/cdr/code-server src/vs/server
cd src/vs/server
yarn patch:apply
yarn
yarn watch
# Wait for the initial compilation to complete (it will say "Finished compilation").
yarn start --allow-http --no-auth
# Visit http://localhost:8443
```
### Upgrading VS Code
We have to patch VS Code to provide and fix some functionality. As the web
portion of VS Code matures, we'll be able to shrink and maybe even entirely
eliminate our patch. In the meantime, however, upgrading the VS Code version
requires ensuring that the patch still applies and has the intended effects.
To generate a new patch, **stage all the changes** you want to be included in
the patch in the VS Code source, then run `yarn patch:generate` in this
directory.
## License
[MIT](LICENSE)
## Enterprise
Visit [our enterprise page](https://coder.com/enterprise) for more information
about our enterprise offering.
## Commercialization
If you would like to commercialize code-server, please contact
contact@coder.com.

View File

@ -1,14 +1,21 @@
# Getting Started
[code-server](https://coder.com) is used by developers at Azure, Google, Reddit, and more to give them access to VS Code in the browser.
[code-server](https://coder.com) is used by developers at Azure, Google,
Reddit, and more to give them access to VS Code in the browser.
## Quickstart Guide
> NOTE: If you get stuck or need help, [file an issue](https://github.com/cdr/code-server/issues/new?&title=Improve+self-hosted+quickstart+guide), [tweet (@coderhq)](https://twitter.com/coderhq) or [email](mailto:support@coder.com?subject=Self-hosted%20quickstart%20guide).
> NOTE: If you get stuck or need help, [file an issue](https://github.com/cdr/code-server/issues/new?&title=Improve+self-hosted+quickstart+guide),
> [tweet (@coderhq)](https://twitter.com/coderhq) or
> [email](mailto:support@coder.com?subject=Self-hosted%20quickstart%20guide).
This document pertains to Coder specific implementations of VS Code. For documentation on how to use VS Code itself, please refer to the official [documentation for VS Code](https://code.visualstudio.com/docs)
This document pertains to Coder-specific implementations of VS Code. For
documentation on how to use VS Code itself, please refer to the official
[documentation for VS Code](https://code.visualstudio.com/docs)
It takes just a few minutes to get your own self-hosted server running. If you've got a machine running macOS, Windows, or Linux, you're ready to start the binary which listens on port `8443` by default.
It takes just a few minutes to get your own self-hosted server running. If
you've got a machine running macOS, Windows, or Linux, you're ready to start
the binary which listens on ports `8443` and `8444` by default.
<!--
DO NOT CHANGE THIS TO A CODEBLOCK.
@ -16,110 +23,122 @@ It takes just a few minutes to get your own self-hosted server running. If you'v
This uses line breaks that are rendered but not copy-pasted to the clipboard.
-->
1. Visit [the releases](https://github.com/cdr/code-server/releases) page and
download the latest cli for your operating system.
2. Double click the executable to run in the current directory.
3. Copy the password that appears in the CLI. <img src="../assets/cli.png">
4. In your browser navigate to `localhost:8443`.
5. Paste the password from the cli into the login window. <img src="../assets/server-password-modal.png">
1. Visit [the releases](https://github.com/cdr/code-server/releases) page and download the latest cli for your operating system
2. Double click the executable to run in the current directory
3. Copy the password that appears in the cli<img src="../assets/cli.png">
4. In your browser navigate to `localhost:8443`
5. Paste the password from the cli into the login window<img src="../assets/server-password-modal.png">
> NOTE: Be careful with your password as sharing it will grant those users access to your server's file system
> NOTE: Be careful with your password as sharing it will grant those users
> access to your server's file system
### Things To Know
- When you visit the IP for your code-server instance, you will be greeted with a page similar to the following screenshot. Code-server is using a self-signed SSL certificate for easy setup. In Chrome/Chromium, click **"Advanced"** then click **"proceed anyway"**. In Firefox, click **Advanced**, then **Add Exception**, then finally **Confirm Security Exception**.<img src ="../assets/chrome_warning.png">
- When you visit the IP for your code-server instance, you will be greeted with
a page similar to the following screenshot. Code-server is using a
self-signed SSL certificate for easy setup. In Chrome/Chromium, click
**"Advanced"** then click **"proceed anyway"**. In Firefox, click
**Advanced**, then **Add Exception**, then finally **Confirm Security
Exception**. <img src ="../assets/chrome_warning.png">
## Usage
<pre class="pre-wrap"><code>code-server<span class="virtual-br"></span> --help</code></pre>
code-server can be ran with a number of arguments to customize your working directory, host, port, and SSL certificate.
```
Usage: code-server [options]
Run VS Code on a remote server.
Options:
-V, --version output the version number
--cert <value>
--cert-key <value>
-e, --extensions-dir <dir> Override the main default path for user extensions.
--extra-extensions-dir [dir] Path to an extra user extension directory (repeatable). (default: [])
--extra-builtin-extensions-dir [dir] Path to an extra built-in extension directory (repeatable). (default: [])
-d, --user-data-dir <dir> Specifies the directory that user data is kept in, useful when running as root.
-h, --host <value> Customize the hostname. (default: "0.0.0.0")
-o, --open Open in the browser on startup.
-p, --port <number> Port to bind on. (default: 8443)
-N, --no-auth Start without requiring authentication.
-H, --allow-http Allow http connections.
--disable-telemetry Disables ALL telemetry.
--socket <value> Listen on a UNIX socket. Host and port will be ignored when set.
--trust-proxy Trust the X-Forwarded-For header, useful when using a reverse proxy.
--install-extension <value> Install an extension by its ID.
-h, --help output usage information
code-server --help
```
### Data Directory
Use `code-server -d (path/to/directory)` or `code-server --user-data-dir=(path/to/directory)`, excluding the parentheses to specify the root folder that VS Code will start in.
code-server can be ran with a number of arguments to customize your working
directory, host, port, and SSL certificate.
### Host
By default, code-server will use `0.0.0.0` as its address. This can be changed by using `code-server -h` or `code-server --host=` followed by the address you want to use.
> Example: `code-server -h 127.0.0.1`
### Data Directory
Use `code-server --user-data-dir path/to/directory` to specify the root folder
that VS Code will start in.
### Open
You can have the server automatically open the VS Code in your browser on startup by using the `code-server -o` or `code-server --open` flags
### Host
By default, code-server will use `127.0.0.1` for insecure connections and
`0.0.0.0` for secure connections. This can be changed by using
`code-server --host `.
### Port
By default, code-server will use `8443` as its port. This can be changed by using `code-server -p` or `code-server --port=` followed by the port you want to use.
> Example: `code-server -p 9000`
> Example: `code-server --host 127.0.0.1`
### Telemetry
Disable all telemetry with `code-server --disable-telemetry`.
### Open
You can have the server automatically open the VS Code in your browser on
startup by using the `code-server -o` or `code-server --open` flags
### Cert and Cert Key
To encrypt the traffic between the browser and server use `code-server --cert=` followed by the path to your `.cer` file. Additionally, you can use certificate keys with `code-server --cert-key` followed by the path to your `.key` file.
> Example (certificate and key): `code-server --cert /etc/letsencrypt/live/example.com/fullchain.cer --cert-key /etc/letsencrypt/live/example.com/fullchain.key`
> Example (if you are using Letsencrypt or similar): `code-server --cert /etc/letsencrypt/live/example.com/fullchain.pem --cert-key /etc/letsencrypt/live/example.com/privkey.key`
### Port
By default, code-server will use `8443` as its port. This can be changed by
using `code-server -p` or `code-server --port=` followed by the port you want
to use.
> To ensure the connection between you and your server is encrypted view our guide on [securing your setup](../security/ssl.md)
> Example: `code-server -p 9000`
### Nginx Reverse Proxy
Below is a virtual host example that works with code-server. Please also pass `--allow-http` and `--trust-proxy` to code-server to allow the proxy to connect. You can also use Let's Encrypt to get a SSL certificates for free.
```
server {
listen 80;
listen [::]:80;
server_name code.example.com code.example.org;
location / {
proxy_pass http://localhost:8443/;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection upgrade;
proxy_set_header Accept-Encoding gzip;
}
### Cert and Cert Key
To encrypt the traffic between the browser and server use `code-server --cert`
followed by the path to your `.cer` file. Additionally, you can use certificate
keys with `code-server --cert-key` followed by the path to your `.key` file.
Example:
```
code-server --cert /path/to/certificate/fullchain.cer --cert-key /path/to/certificate/fullchain.key
```
Example for Let's Encrypt:
```
code-server --cert /etc/letsencrypt/live/example.com/fullchain.pem --cert-key /etc/letsencrypt/live/example.com/privkey.key
```
To ensure the connection between you and your server is encrypted view our
guide on [securing your setup](../security/ssl.md).
### Nginx Reverse Proxy
Below is a virtual host example that works with code-server. Please also pass
`--allow-http` and `--trust-proxy` to code-server to allow the proxy to
connect. You can also use Let's Encrypt to get a SSL certificates for free.
```
server {
listen 80;
listen [::]:80;
server_name code.example.com code.example.org;
location / {
proxy_pass http://localhost:8443/;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection upgrade;
proxy_set_header Accept-Encoding gzip;
}
```
}
```
### Apache Reverse Proxy
Example of a HTTPS virtualhost configuration for Apache as a reverse proxy. Please also pass `--allow-http` and `--trust-proxy` to code-server to allow the proxy to connect. You can also use Let's Encrypt to get a SSL certificates for free.
```
<VirtualHost *:80>
ServerName code.example.com
### Apache Reverse Proxy
Example of an HTTPS virtualhost configuration for Apache as a reverse proxy.
Please also pass `--allow-http` and `--trust-proxy` to code-server to allow the
proxy to connect. You can also use Let's Encrypt to get a SSL certificates for
free.
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://localhost:8443/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /(.*) http://localhost:8443/$1 [P,L]
```
<VirtualHost *:80>
ServerName code.example.com
ProxyRequests off
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://localhost:8443/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /(.*) http://localhost:8443/$1 [P,L]
RequestHeader set X-Forwarded-Proto https
RequestHeader set X-Forwarded-Port 443
ProxyRequests off
ProxyPass / http://localhost:8443/ nocanon
ProxyPassReverse / http://localhost:8443/
RequestHeader set X-Forwarded-Proto https
RequestHeader set X-Forwarded-Port 443
</VirtualHost>
```
*Important:* For more details about Apache reverse proxy configuration checkout the [documentation](https://httpd.apache.org/docs/current/mod/mod_proxy.html) - especially the [Securing your Server](https://httpd.apache.org/docs/current/mod/mod_proxy.html#access) section
ProxyPass / http://localhost:8443/ nocanon
ProxyPassReverse / http://localhost:8443/
### Help
Use `code-server --help` to view the usage for the CLI. This is also shown at the beginning of this section.
</VirtualHost>
```
*Important:* For more details about Apache reverse proxy configuration checkout
the [documentation](https://httpd.apache.org/docs/current/mod/mod_proxy.html) -
especially the [Securing your Server](https://httpd.apache.org/docs/current/mod/mod_proxy.html#access)
section.
### Help
Use `code-server --help` to view the usage for the CLI.

View File

@ -214,6 +214,103 @@ index e09049c5b9..d93ffa527a 100644
-}
\ No newline at end of file
+}
diff --git a/src/vs/platform/log/common/logIpc.ts b/src/vs/platform/log/common/logIpc.ts
index 9f68b645b6..fe380bb6f8 100644
--- a/src/vs/platform/log/common/logIpc.ts
+++ b/src/vs/platform/log/common/logIpc.ts
@@ -26,6 +26,7 @@ export class LogLevelSetterChannel implements IServerChannel {
call(_: unknown, command: string, arg?: any): Promise<any> {
switch (command) {
case 'setLevel': this.service.setLevel(arg); return Promise.resolve();
+ case 'getLevel': return Promise.resolve(this.service.getLevel());
}
throw new Error(`Call not found: ${command}`);
@@ -40,6 +41,10 @@ export class LogLevelSetterChannelClient {
return this.channel.listen('onDidChangeLogLevel');
}
+ getLevel(): Promise<LogLevel> {
+ return this.channel.call('getLevel');
+ }
+
setLevel(level: LogLevel): void {
this.channel.call('setLevel', level);
}
@@ -56,4 +61,4 @@ export class FollowerLogService extends DelegatedLogService implements ILogServi
setLevel(level: LogLevel): void {
this.master.setLevel(level);
}
-}
\ No newline at end of file
+}
diff --git a/src/vs/platform/telemetry/node/telemetryIpc.ts b/src/vs/platform/telemetry/node/telemetryIpc.ts
index 8e1b68eb36..2b6a0d5b15 100644
--- a/src/vs/platform/telemetry/node/telemetryIpc.ts
+++ b/src/vs/platform/telemetry/node/telemetryIpc.ts
@@ -6,6 +6,9 @@
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { ITelemetryAppender } from 'vs/platform/telemetry/common/telemetryUtils';
import { Event } from 'vs/base/common/event';
+import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
+import { ITelemetryData } from 'vs/base/common/actions';
+import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings';
export interface ITelemetryLog {
eventName: string;
@@ -41,3 +44,52 @@ export class TelemetryAppenderClient implements ITelemetryAppender {
// TODO
}
}
+
+export class TelemetryChannel implements IServerChannel {
+
+ constructor(private service: ITelemetryService) {}
+
+ listen(_: unknown, event: string): Event<any> {
+ throw new Error(`Invalid listen ${event}`);
+ }
+
+ call(_: unknown, command: string, args?: any): Promise<any> {
+ switch (command) {
+ case 'publicLog': return this.service.publicLog(args[0], args[1], args[2]);
+ case 'publicLog2': return this.service.publicLog2(args[0], args[1], args[2]);
+ case 'setEnabled': return Promise.resolve(this.service.setEnabled(args[0]));
+ case 'getTelemetryInfo': return this.service.getTelemetryInfo();
+ }
+
+ throw new Error(`Invalid call ${command}`);
+ }
+}
+
+export class TelemetryChannelClient implements ITelemetryService {
+
+ _serviceBrand: any;
+
+ constructor(
+ private readonly channel: IChannel,
+ ) { }
+
+ public publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise<void> {
+ return this.channel.call('publicLog', [eventName, data, anonymizeFilePaths]);
+ }
+
+ public publicLog2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyCheck<T, E>, anonymizeFilePaths?: boolean): Promise<void> {
+ return this.channel.call('publicLog2', [eventName, data, anonymizeFilePaths]);
+ }
+
+ public setEnabled(value: boolean): void {
+ this.channel.call('setEnable', [value]);
+ }
+
+ public getTelemetryInfo(): Promise<ITelemetryInfo> {
+ return this.channel.call('getTelemetryInfo');
+ }
+
+ public get isOptedIn(): boolean {
+ return true;
+ }
+}
diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts
index 1986fb6642..afbe385af6 100644
--- a/src/vs/workbench/browser/web.main.ts
@ -236,21 +333,22 @@ index 1986fb6642..afbe385af6 100644
\ No newline at end of file
+}
diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts
index b253e573ae..2e4dfb393a 100644
index b253e573ae..bde667d045 100644
--- a/src/vs/workbench/browser/web.simpleservices.ts
+++ b/src/vs/workbench/browser/web.simpleservices.ts
@@ -53,6 +53,10 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
@@ -53,6 +53,11 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings';
import { IProcessEnvironment } from 'vs/base/common/platform';
+import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
+import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/node/extensionManagementIpc';
+import { ExtensionGalleryChannelClient } from "vs/platform/extensionManagement/node/extensionGalleryIpc";
+import { ExtensionGalleryChannelClient } from 'vs/platform/extensionManagement/node/extensionGalleryIpc';
+import { TelemetryChannelClient } from 'vs/platform/telemetry/node/telemetryIpc';
+import { IProductService } from 'vs/platform/product/common/product';
//#region Backup File
@@ -125,13 +129,11 @@ export class SimpleClipboardService implements IClipboardService {
@@ -125,13 +130,11 @@ export class SimpleClipboardService implements IClipboardService {
writeText(text: string, type?: string): void { }
readText(type?: string): string {
@ -266,7 +364,7 @@ index b253e573ae..2e4dfb393a 100644
}
writeFindText(text: string): void { }
@@ -239,7 +241,17 @@ export class SimpleExtensionGalleryService implements IExtensionGalleryService {
@@ -239,7 +242,17 @@ export class SimpleExtensionGalleryService implements IExtensionGalleryService {
}
}
@ -285,7 +383,7 @@ index b253e573ae..2e4dfb393a 100644
//#endregion
@@ -262,7 +274,7 @@ export class SimpleExtensionsWorkbenchService implements IExtensionsWorkbenchSer
@@ -262,7 +275,7 @@ export class SimpleExtensionsWorkbenchService implements IExtensionsWorkbenchSer
checkForUpdates: any;
allowedBadgeProviders: string[];
}
@ -294,7 +392,7 @@ index b253e573ae..2e4dfb393a 100644
//#endregion
//#region ICommentService
@@ -375,7 +387,10 @@ export class SimpleExtensionTipsService implements IExtensionTipsService {
@@ -375,7 +388,10 @@ export class SimpleExtensionTipsService implements IExtensionTipsService {
}
getAllIgnoredRecommendations(): { global: string[]; workspace: string[]; } {
@ -306,7 +404,7 @@ index b253e573ae..2e4dfb393a 100644
}
}
@@ -436,7 +451,16 @@ export class SimpleExtensionManagementService implements IExtensionManagementSer
@@ -436,7 +452,16 @@ export class SimpleExtensionManagementService implements IExtensionManagementSer
}
}
@ -324,7 +422,24 @@ index b253e573ae..2e4dfb393a 100644
//#endregion
@@ -1288,4 +1312,4 @@ class SimpleTunnelService implements ITunnelService {
@@ -680,7 +705,15 @@ export class SimpleTelemetryService implements ITelemetryService {
}
}
-registerSingleton(ITelemetryService, SimpleTelemetryService);
+// registerSingleton(ITelemetryService, SimpleTelemetryService);
+class TelemetryService extends TelemetryChannelClient {
+ public constructor(
+ @IRemoteAgentService remoteAgentService: IRemoteAgentService,
+ ) {
+ super(remoteAgentService.getConnection()!.getChannel('telemetry'));
+ }
+}
+registerSingleton(ITelemetryService, TelemetryService);
//#endregion
@@ -1288,4 +1321,4 @@ class SimpleTunnelService implements ITunnelService {
registerSingleton(ITunnelService, SimpleTunnelService);
@ -911,6 +1026,20 @@ index c08a6e37c1..31640d7e66 100644
}
return this._extensionAllowedBadgeProviders;
}
diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts
index 9235c739fb..32d203eb32 100644
--- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts
+++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts
@@ -55,7 +55,8 @@ class RemoteChannelsContribution extends Disposable implements IWorkbenchContrib
const connection = remoteAgentService.getConnection();
if (connection) {
const logLevelClient = new LogLevelSetterChannelClient(connection.getChannel('loglevel'));
- logLevelClient.setLevel(logService.getLevel());
+ logLevelClient.getLevel().then((level) => logService.setLevel(level));
+ logLevelClient.onDidChangeLogLevel((level) => logService.setLevel(level));
this._register(logService.onDidChangeLogLevel(level => logLevelClient.setLevel(level)));
}
}
diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts
index 3525569601..a91a5fce7d 100644
--- a/src/vs/workbench/services/environment/browser/environmentService.ts

View File

@ -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);
}
}

View File

@ -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";

View File

@ -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;
};

View File

@ -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.
*/