Skip to content

Commit

Permalink
Add more telemetry for global TS plugins (#225143)
Browse files Browse the repository at this point in the history
* Add more telemetry for global TS plugins

Log the hashed name of the global TS plugins so we can correlate issues to them

* Remove trailing comma
  • Loading branch information
mjbvz authored Aug 9, 2024
1 parent 3dab2b4 commit aefef1e
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { TypeScriptVersionManager } from './tsServer/versionManager';
import { ITypeScriptVersionProvider, TypeScriptVersion } from './tsServer/versionProvider';
import { ClientCapabilities, ClientCapability, ExecConfig, ITypeScriptServiceClient, ServerResponse, TypeScriptRequests } from './typescriptService';
import { Disposable, DisposableStore, disposeAll } from './utils/dispose';
import { hash } from './utils/hash';
import { isWeb, isWebAndHasSharedArrayBuffers } from './utils/platform';


Expand Down Expand Up @@ -424,22 +425,33 @@ export default class TypeScriptServiceClient extends Disposable implements IType
this.serverState = new ServerState.Running(handle, apiVersion, undefined, true);
this.lastStart = Date.now();

const hasGlobalPlugins = this.pluginManager.plugins.length > 0;

/* __GDPR__FRAGMENT__
"TypeScriptServerEnvCommonProperties" : {
"hasGlobalPlugins": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"globalPluginNameHashes": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
const typeScriptServerEnvCommonProperties = {
hasGlobalPlugins: this.pluginManager.plugins.length > 0,
globalPluginNameHashes: JSON.stringify(this.pluginManager.plugins.map(plugin => hash(plugin.name))),
};

/* __GDPR__
"tsserver.spawned" : {
"owner": "mjbvz",
"${include}": [
"${TypeScriptCommonProperties}"
"${TypeScriptCommonProperties}",
"${TypeScriptServerEnvCommonProperties}"
],
"localTypeScriptVersion": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"typeScriptVersionSource": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"hasGlobalPlugins": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"typeScriptVersionSource": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.logTelemetry('tsserver.spawned', {
...typeScriptServerEnvCommonProperties,
localTypeScriptVersion: this.versionProvider.localVersion ? this.versionProvider.localVersion.displayName : '',
typeScriptVersionSource: version.source,
hasGlobalPlugins,
});

handle.onError((err: Error) => {
Expand All @@ -462,12 +474,14 @@ export default class TypeScriptServiceClient extends Disposable implements IType
"tsserver.error" : {
"owner": "mjbvz",
"${include}": [
"${TypeScriptCommonProperties}"
],
"hasGlobalPlugins": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"${TypeScriptCommonProperties}",
"${TypeScriptServerEnvCommonProperties}"
]
}
*/
this.logTelemetry('tsserver.error', { hasGlobalPlugins });
this.logTelemetry('tsserver.error', {
...typeScriptServerEnvCommonProperties
});
this.serviceExited(false, apiVersion);
});

Expand All @@ -481,17 +495,17 @@ export default class TypeScriptServiceClient extends Disposable implements IType
"tsserver.exitWithCode" : {
"owner": "mjbvz",
"${include}": [
"${TypeScriptCommonProperties}"
"${TypeScriptCommonProperties}",
"${TypeScriptServerEnvCommonProperties}"
],
"code" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"signal" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" },
"hasGlobalPlugins": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
"signal" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
}
*/
this.logTelemetry('tsserver.exitWithCode', {
...typeScriptServerEnvCommonProperties,
code: code ?? undefined,
signal: signal ?? undefined,
hasGlobalPlugins,
});

if (this.token !== mytoken) {
Expand Down Expand Up @@ -1194,6 +1208,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType
break;
}
}

// Add plugin data here
if (telemetryData.telemetryEventName === 'projectInfo') {
if (this.serverState.type === ServerState.Type.Running) {
this.serverState.updateTsserverVersion(properties['version']);
Expand Down
58 changes: 58 additions & 0 deletions extensions/typescript-language-features/src/utils/hash.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

/**
* Return a hash value for an object.
*/
export function hash(obj: any, hashVal = 0): number {
switch (typeof obj) {
case 'object':
if (obj === null) {
return numberHash(349, hashVal);
} else if (Array.isArray(obj)) {
return arrayHash(obj, hashVal);
}
return objectHash(obj, hashVal);
case 'string':
return stringHash(obj, hashVal);
case 'boolean':
return booleanHash(obj, hashVal);
case 'number':
return numberHash(obj, hashVal);
case 'undefined':
return 937 * 31;
default:
return numberHash(obj, 617);
}
}

function numberHash(val: number, initialHashVal: number): number {
return (((initialHashVal << 5) - initialHashVal) + val) | 0; // hashVal * 31 + ch, keep as int32
}

function booleanHash(b: boolean, initialHashVal: number): number {
return numberHash(b ? 433 : 863, initialHashVal);
}

function stringHash(s: string, hashVal: number) {
hashVal = numberHash(149417, hashVal);
for (let i = 0, length = s.length; i < length; i++) {
hashVal = numberHash(s.charCodeAt(i), hashVal);
}
return hashVal;
}

function arrayHash(arr: any[], initialHashVal: number): number {
initialHashVal = numberHash(104579, initialHashVal);
return arr.reduce((hashVal, item) => hash(item, hashVal), initialHashVal);
}

function objectHash(obj: any, initialHashVal: number): number {
initialHashVal = numberHash(181387, initialHashVal);
return Object.keys(obj).sort().reduce((hashVal, key) => {
hashVal = stringHash(key, hashVal);
return hash(obj[key], hashVal);
}, initialHashVal);
}

0 comments on commit aefef1e

Please sign in to comment.