Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DO NOT MERGE] Initial check-in for supporting telemetry in vscode #6123

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .chronus/changes/vscode-telemetry-2025-1-24-22-51-43.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- typespec-vscode
---

Support telemetry
3 changes: 3 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@
// Use empty node options and don't debug while profiling to get the most accurate timing
//"TYPESPEC_SERVER_NODE_OPTIONS": "",

// Set the telemetry key environment variable to use if you dont want to set it in package.json
//"TYPESPEC_VSCODE_TELEMETRY_KEY": "{The instrumentation key of your Application Insights}",

"TYPESPEC_SERVER_NODE_OPTIONS": "--nolazy --inspect-brk=4242",
"TYPESPEC_DEVELOPMENT_MODE": "true"
},
Expand Down
4 changes: 4 additions & 0 deletions eng/tsp-core/pipelines/jobs/build-for-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ jobs:
parameters:
useDotNet: true

- script: pnpm run update-telemetry-key $(vscode.telemetryKey)
workingDirectory: $(Build.SourcesDirectory)/packages/typespec-vscode
displayName: Update vscode telemetry key

- template: /eng/tsp-core/pipelines/templates/build.yml

- script: pnpm run test:ci
Expand Down
4 changes: 4 additions & 0 deletions packages/typespec-vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"workspaceContains:**/tspconfig.yaml"
],
"icon": "./icons/logo.png",
"telemetryKey": "00000000-0000-0000-0000-000000000000",
"contributes": {
"viewsWelcome": [
{
Expand Down Expand Up @@ -232,6 +233,7 @@
"copy-tmlanguage": "node scripts/copy-tmlanguage.js",
"generate-language-configuration": "node scripts/generate-language-configuration.js",
"generate-third-party-notices": "typespec-build-tool generate-third-party-notices",
"update-telemetry-key": "node scripts/update-telemetry-key.js",
"package-vsix": "vsce package",
"deploy": "vsce publish",
"open-in-browser": "vscode-test-web --extensionDevelopmentPath=. .",
Expand All @@ -240,6 +242,7 @@
},
"devDependencies": {
"@rollup/plugin-commonjs": "~28.0.2",
"@rollup/plugin-json": "~6.1.0",
"@rollup/plugin-node-resolve": "~16.0.0",
"@rollup/plugin-typescript": "~12.1.0",
"@types/mocha": "^10.0.9",
Expand All @@ -250,6 +253,7 @@
"@typespec/internal-build-utils": "workspace:~",
"@vitest/coverage-v8": "^3.0.4",
"@vitest/ui": "^3.0.3",
"@vscode/extension-telemetry": "^0.6.2",
"@vscode/test-web": "^0.0.65",
"@vscode/vsce": "~3.2.1",
"c8": "^10.1.3",
Expand Down
3 changes: 2 additions & 1 deletion packages/typespec-vscode/rollup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import resolve from "@rollup/plugin-node-resolve";
import typescript from "@rollup/plugin-typescript";
import { dirname } from "path";

import json from "@rollup/plugin-json";
import { defineConfig } from "rollup";
import { fileURLToPath } from "url";
const projDir = dirname(fileURLToPath(import.meta.url));

const plugins = [(resolve as any)({ preferBuiltins: true }), (commonjs as any)()];
const plugins = [(resolve as any)({ preferBuiltins: true }), (commonjs as any)(), (json as any)()];
const baseConfig = defineConfig({
input: "src/extension.ts",
output: {
Expand Down
37 changes: 37 additions & 0 deletions packages/typespec-vscode/scripts/update-telemetry-key.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";

const newKey = process.argv[2];
if (!newKey) {
console.log("One argument for telemetry-key to use is expected. Exit without updating anything");
process.exit(1);
} else {
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const targetPackageJsonFile = path.resolve(__dirname, "../package.json");
console.log(`Updating package.json at ${targetPackageJsonFile}`);
const packageJson = JSON.parse(fs.readFileSync(targetPackageJsonFile, "utf8"));
const oldKey = packageJson.telemetryKey ?? "";
const keyToString = (key) =>
`'${key.substring(0, 8)}...${key.substring(Math.max(0, key.length - 17))}'(length: ${key.length})`;

console.log(`Updating telemetry key from ${keyToString(oldKey)} to ${keyToString(newKey)}`);
packageJson.telemetryKey = newKey;

fs.writeFileSync(targetPackageJsonFile, JSON.stringify(packageJson, null, 2));

// double verify the updated result
const newPackageJson = JSON.parse(fs.readFileSync(targetPackageJsonFile, "utf8"));
const updatedKey = newPackageJson.telemetryKey ?? "";
if (updatedKey !== newKey) {
console.error(
`Failed to update telemetry key in package.json. Actual = ${keyToString(updatedKey)}; Expected = ${keyToString(newKey)}`,
);
process.exit(2);
} else {
console.log(
`telemetryKey in package.json updated to '${keyToString(updatedKey)}' successfully`,
);
}
}
1 change: 1 addition & 0 deletions packages/typespec-vscode/src/const.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export const StartFileName = "main.tsp";
export const TspConfigFileName = "tspconfig.yaml";
export const EmptyGuid = "00000000-0000-0000-0000-000000000000";
76 changes: 56 additions & 20 deletions packages/typespec-vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ import { ExtensionLogListener, getPopupAction } from "./log/extension-log-listen
import logger from "./log/logger.js";
import { TypeSpecLogOutputChannel } from "./log/typespec-log-output-channel.js";
import { createTaskProvider } from "./task-provider.js";
import telemetryClient from "./telemetry/telemetry-client.js";
import { OperationTelemetryEvent, TelemetryEventName } from "./telemetry/telemetry-event.js";
import { TspLanguageClient } from "./tsp-language-client.js";
import {
CommandName,
InstallGlobalCliCommandArgs,
RestartServerCommandArgs,
RestartServerCommandResult,
Result,
ResultCode,
SettingName,
} from "./types.js";
import { isWhitespaceStringOrUndefined } from "./utils.js";
Expand All @@ -30,6 +35,8 @@ logger.registerLogListener("extension-log", new ExtensionLogListener(outputChann
export async function activate(context: ExtensionContext) {
const stateManager = new ExtensionStateManager(context);

context.subscriptions.push(telemetryClient);

context.subscriptions.push(createTaskProvider());

context.subscriptions.push(createCodeActionProvider());
Expand Down Expand Up @@ -67,26 +74,35 @@ export async function activate(context: ExtensionContext) {
context.subscriptions.push(
commands.registerCommand(
CommandName.RestartServer,
async (args: RestartServerCommandArgs | undefined): Promise<TspLanguageClient> => {
async (args: RestartServerCommandArgs | undefined): Promise<RestartServerCommandResult> => {
return vscode.window.withProgress(
{
title: args?.notificationMessage ?? "Restarting TypeSpec language service...",
location: vscode.ProgressLocation.Notification,
},
async () => {
if (args?.forceRecreate === true) {
logger.info("Forcing to recreate TypeSpec LSP server...");
return await recreateLSPClient(context);
}
if (client && client.state === State.Running) {
await client.restart();
return client;
} else {
logger.info(
"TypeSpec LSP server is not running which is not expected, try to recreate and start...",
);
return recreateLSPClient(context);
}
return await telemetryClient.doOperationWithTelemetry(
TelemetryEventName.RestartServer,
async (tel) => {
if (args?.forceRecreate === true) {
logger.info("Forcing to recreate TypeSpec LSP server...");
tel.lastStep = "Recreate LSP client in force";
return await recreateLSPClient(context, tel.activityId);
}
if (client && client.state === State.Running) {
tel.lastStep = "Restart LSP client";
await client.restart();
return { code: ResultCode.Success, value: client };
} else {
logger.info(
"TypeSpec LSP server is not running which is not expected, try to recreate and start...",
);
tel.lastStep = "Recreate LSP client";
return await recreateLSPClient(context, tel.activityId);
}
},
args?.activityId,
);
},
);
},
Expand Down Expand Up @@ -118,7 +134,12 @@ export async function activate(context: ExtensionContext) {
vscode.workspace.onDidChangeConfiguration(async (e: vscode.ConfigurationChangeEvent) => {
if (e.affectsConfiguration(SettingName.TspServerPath)) {
logger.info("TypeSpec server path changed, restarting server...");
await recreateLSPClient(context);
await telemetryClient.doOperationWithTelemetry(
TelemetryEventName.ServerPathSettingChanged,
async (tel) => {
return await recreateLSPClient(context, tel.activityId);
},
);
}
}),
);
Expand Down Expand Up @@ -160,7 +181,12 @@ export async function activate(context: ExtensionContext) {
location: vscode.ProgressLocation.Notification,
},
async () => {
await recreateLSPClient(context);
await telemetryClient.doOperationWithTelemetry(
TelemetryEventName.StartExtension,
async (tel: OperationTelemetryEvent) => {
return await recreateLSPClient(context, tel.activityId);
},
);
},
);
} else {
Expand All @@ -173,13 +199,23 @@ export async function deactivate() {
await client?.stop();
}

async function recreateLSPClient(context: ExtensionContext) {
async function recreateLSPClient(
context: ExtensionContext,
activityId: string,
): Promise<Result<TspLanguageClient>> {
logger.info("Recreating TypeSpec LSP server...");
const oldClient = client;
client = await TspLanguageClient.create(context, outputChannel);
client = await TspLanguageClient.create(activityId, context, outputChannel);
await oldClient?.stop();
await client.start();
return client;
await client.start(activityId);
if (client.state === State.Running) {
telemetryClient.logOperationDetailTelemetry(activityId, {
compilerVersion: client.initializeResult?.serverInfo?.version ?? "< 0.64.0",
});
return { code: ResultCode.Success, value: client };
} else {
return { code: ResultCode.Fail, details: "TspLanguageClient is not running." };
}
}

function showStartUpMessages(stateManager: ExtensionStateManager) {
Expand Down
Loading