Skip to content

Commit 988c9fb

Browse files
committed
chore: track extension startup times
1 parent 48f244e commit 988c9fb

File tree

13 files changed

+235
-131
lines changed

13 files changed

+235
-131
lines changed

package-lock.json

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@
126126
"@eslint/js": "^9.34.0",
127127
"@types/fs-extra": "^11.0.4",
128128
"@types/mocha": "^10.0.10",
129+
"@types/ms": "^2.1.0",
129130
"@types/node": "20.x",
130131
"@types/vscode": "^1.83.0",
131132
"@vscode/test-cli": "^0.0.11",
@@ -135,6 +136,7 @@
135136
"eslint": "^9.34.0",
136137
"eslint-plugin-import": "^2.32.0",
137138
"fs-extra": "^11.3.1",
139+
"ms": "^2.1.3",
138140
"npm-run-all": "^4.1.5",
139141
"p-min-delay": "^4.1.0",
140142
"typescript": "^5.9.2",

src/plugins.ts

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import ms from "ms";
12
import type { ExtensionContext, LogOutputChannel, StatusBarItem } from "vscode";
23

34
import type { ContainerStatusTracker } from "./utils/container-status.ts";
45
import type { LocalStackStatusTracker } from "./utils/localstack-status.ts";
56
import type { SetupStatusTracker } from "./utils/setup-status.ts";
67
import type { Telemetry } from "./utils/telemetry.ts";
8+
import type { TimeTracker } from "./utils/time-tracker.ts";
79

810
export type Deactivate = () => Promise<void> | void;
911

@@ -15,40 +17,59 @@ export interface PluginOptions {
1517
localStackStatusTracker: LocalStackStatusTracker;
1618
setupStatusTracker: SetupStatusTracker;
1719
telemetry: Telemetry;
20+
timeTracker: TimeTracker;
1821
}
1922

2023
export interface Plugin {
2124
deactivate: Deactivate;
2225
}
2326

24-
export type PluginFactory = (options: PluginOptions) => Promise<Plugin>;
27+
export type PluginDefinition = {
28+
name: string;
29+
factory: (options: PluginOptions) => Promise<Plugin>;
30+
};
2531

2632
export const createPlugin = (
33+
name: string,
2734
// biome-ignore lint/suspicious/noConfusingVoidType: required
2835
handler: (options: PluginOptions) => Promise<Deactivate | void> | void,
29-
): PluginFactory => {
30-
return async (options) => {
31-
const deactivate = (await handler(options)) ?? (() => {});
32-
return {
33-
deactivate,
34-
};
36+
): PluginDefinition => {
37+
return {
38+
name,
39+
async factory(options: PluginOptions): Promise<Plugin> {
40+
const deactivate = (await handler(options)) ?? (() => {});
41+
return {
42+
deactivate,
43+
};
44+
},
3545
};
3646
};
3747

3848
export class PluginManager {
39-
private plugins: PluginFactory[];
49+
private plugins: PluginDefinition[];
4050

4151
private deactivatables: Plugin[];
4252

43-
constructor(plugins: PluginFactory[]) {
53+
constructor(plugins: PluginDefinition[]) {
4454
this.plugins = plugins;
4555
this.deactivatables = [];
4656
}
4757

4858
async activate(options: PluginOptions) {
4959
for (const activate of this.plugins) {
50-
const deactivatable = await activate(options);
60+
const startPlugin = Date.now();
61+
options.outputChannel.trace(
62+
`[plugin-manager]: Activating plugin "${activate.name}"...`,
63+
);
64+
const deactivatable = await activate.factory(options);
5165
this.deactivatables.push(deactivatable);
66+
const endPlugin = Date.now();
67+
options.outputChannel.trace(
68+
`[plugin-manager]: Activated plugin "${activate.name}" in ${ms(
69+
endPlugin - startPlugin,
70+
{ long: true },
71+
)}`,
72+
);
5273
}
5374
}
5475

src/plugins/authenticate.ts

Lines changed: 96 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -6,99 +6,131 @@ import { requestAuthentication, saveAuthToken } from "../utils/authenticate.ts";
66

77
const MIN_TIME_BETWEEN_STEPS_MS = 1_000; // 1s
88

9-
export default createPlugin(({ context, outputChannel, telemetry }) => {
10-
context.subscriptions.push(
11-
commands.registerCommand("localstack.authenticate", async () => {
12-
const startedAt = new Date().toISOString();
13-
const selection = await window.showInformationMessage(
14-
"Choose authentication method",
15-
"Sign in to LocalStack",
16-
"Enter auth token",
17-
);
18-
if (selection === "Sign in to LocalStack") {
19-
window.withProgress(
20-
{
21-
location: ProgressLocation.Notification,
22-
title: "Authenticate",
23-
cancellable: true,
24-
},
25-
async (progress, cancellationToken) => {
26-
/////////////////////////////////////////////////////////////////////
27-
progress.report({
28-
message:
29-
"Waiting for authentication response from the browser...",
30-
});
31-
const { authToken } = await pMinDelay(
32-
requestAuthentication(context, undefined),
33-
MIN_TIME_BETWEEN_STEPS_MS,
34-
);
35-
if (cancellationToken.isCancellationRequested) {
9+
export default createPlugin(
10+
"authenticate",
11+
({ context, outputChannel, telemetry }) => {
12+
context.subscriptions.push(
13+
commands.registerCommand("localstack.authenticate", async () => {
14+
const startedAt = new Date().toISOString();
15+
const selection = await window.showInformationMessage(
16+
"Choose authentication method",
17+
"Sign in to LocalStack",
18+
"Enter auth token",
19+
);
20+
if (selection === "Sign in to LocalStack") {
21+
window.withProgress(
22+
{
23+
location: ProgressLocation.Notification,
24+
title: "Authenticate",
25+
cancellable: true,
26+
},
27+
async (progress, cancellationToken) => {
28+
/////////////////////////////////////////////////////////////////////
29+
progress.report({
30+
message:
31+
"Waiting for authentication response from the browser...",
32+
});
33+
const { authToken } = await pMinDelay(
34+
requestAuthentication(context, undefined),
35+
MIN_TIME_BETWEEN_STEPS_MS,
36+
);
37+
if (cancellationToken.isCancellationRequested) {
38+
telemetry.track({
39+
name: "auth_token_configured",
40+
payload: {
41+
namespace: "onboarding",
42+
origin: "manual_trigger",
43+
position: 2,
44+
started_at: startedAt,
45+
ended_at: new Date().toISOString(),
46+
status: "CANCELLED",
47+
},
48+
});
49+
return;
50+
}
51+
52+
/////////////////////////////////////////////////////////////////////
53+
progress.report({
54+
message: "Authenticating to file...",
55+
});
56+
await pMinDelay(
57+
saveAuthToken(authToken, outputChannel),
58+
MIN_TIME_BETWEEN_STEPS_MS,
59+
);
60+
61+
/////////////////////////////////////////////////////////////////////
62+
window.showInformationMessage("Authentication successful.");
3663
telemetry.track({
3764
name: "auth_token_configured",
3865
payload: {
3966
namespace: "onboarding",
4067
origin: "manual_trigger",
4168
position: 2,
69+
auth_token: authToken,
4270
started_at: startedAt,
4371
ended_at: new Date().toISOString(),
44-
status: "CANCELLED",
72+
status: "COMPLETED",
4573
},
4674
});
47-
return;
48-
}
75+
},
76+
);
77+
} else if (selection === "Enter auth token") {
78+
const token = await window.showInputBox({
79+
prompt: "Enter your LocalStack Auth Token",
80+
placeHolder: "Paste your auth token here",
81+
ignoreFocusOut: true,
82+
});
4983

50-
/////////////////////////////////////////////////////////////////////
51-
progress.report({
52-
message: "Authenticating to file...",
84+
if (!token) {
85+
telemetry.track({
86+
name: "auth_token_configured",
87+
payload: {
88+
namespace: "onboarding",
89+
origin: "manual_trigger",
90+
position: 2,
91+
started_at: startedAt,
92+
ended_at: new Date().toISOString(),
93+
status: "FAILED",
94+
errors: ["No token was provided."],
95+
},
5396
});
54-
await pMinDelay(
55-
saveAuthToken(authToken, outputChannel),
56-
MIN_TIME_BETWEEN_STEPS_MS,
57-
);
97+
return;
98+
}
99+
100+
if (!token.startsWith("ls-")) {
101+
const error_msg = 'The auth token should start with "ls-".';
102+
window.showErrorMessage(error_msg);
58103

59-
/////////////////////////////////////////////////////////////////////
60-
window.showInformationMessage("Authentication successful.");
61104
telemetry.track({
62105
name: "auth_token_configured",
63106
payload: {
64107
namespace: "onboarding",
65108
origin: "manual_trigger",
66109
position: 2,
67-
auth_token: authToken,
68110
started_at: startedAt,
69111
ended_at: new Date().toISOString(),
70-
status: "COMPLETED",
112+
status: "FAILED",
113+
errors: [error_msg],
71114
},
72115
});
73-
},
74-
);
75-
} else if (selection === "Enter auth token") {
76-
const token = await window.showInputBox({
77-
prompt: "Enter your LocalStack Auth Token",
78-
placeHolder: "Paste your auth token here",
79-
ignoreFocusOut: true,
80-
});
116+
return;
117+
}
81118

82-
if (!token) {
119+
await saveAuthToken(token, outputChannel);
120+
window.showInformationMessage("Authentication successful.");
83121
telemetry.track({
84122
name: "auth_token_configured",
85123
payload: {
86124
namespace: "onboarding",
87125
origin: "manual_trigger",
88126
position: 2,
127+
auth_token: token,
89128
started_at: startedAt,
90129
ended_at: new Date().toISOString(),
91-
status: "FAILED",
92-
errors: ["No token was provided."],
130+
status: "COMPLETED",
93131
},
94132
});
95-
return;
96-
}
97-
98-
if (!token.startsWith("ls-")) {
99-
const error_msg = 'The auth token should start with "ls-".';
100-
window.showErrorMessage(error_msg);
101-
133+
} else if (selection === undefined) {
102134
telemetry.track({
103135
name: "auth_token_configured",
104136
payload: {
@@ -107,40 +139,11 @@ export default createPlugin(({ context, outputChannel, telemetry }) => {
107139
position: 2,
108140
started_at: startedAt,
109141
ended_at: new Date().toISOString(),
110-
status: "FAILED",
111-
errors: [error_msg],
142+
status: "CANCELLED",
112143
},
113144
});
114-
return;
115145
}
116-
117-
await saveAuthToken(token, outputChannel);
118-
window.showInformationMessage("Authentication successful.");
119-
telemetry.track({
120-
name: "auth_token_configured",
121-
payload: {
122-
namespace: "onboarding",
123-
origin: "manual_trigger",
124-
position: 2,
125-
auth_token: token,
126-
started_at: startedAt,
127-
ended_at: new Date().toISOString(),
128-
status: "COMPLETED",
129-
},
130-
});
131-
} else if (selection === undefined) {
132-
telemetry.track({
133-
name: "auth_token_configured",
134-
payload: {
135-
namespace: "onboarding",
136-
origin: "manual_trigger",
137-
position: 2,
138-
started_at: startedAt,
139-
ended_at: new Date().toISOString(),
140-
status: "CANCELLED",
141-
},
142-
});
143-
}
144-
}),
145-
);
146-
});
146+
}),
147+
);
148+
},
149+
);

src/plugins/configure-aws.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { commands } from "vscode";
33
import { createPlugin } from "../plugins.ts";
44
import { configureAwsProfiles } from "../utils/configure-aws.ts";
55

6-
export default createPlugin(({ context, telemetry }) => {
6+
export default createPlugin("configure-aws", ({ context, telemetry }) => {
77
context.subscriptions.push(
88
commands.registerCommand("localstack.configureAwsProfiles", async () => {
99
await configureAwsProfiles({

src/plugins/logs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { createPlugin } from "../plugins.ts";
55
import { pipeToLogOutputChannel } from "../utils/spawn.ts";
66

77
export default createPlugin(
8+
"logs",
89
({ context, outputChannel, containerStatusTracker }) => {
910
let logsProcess: ChildProcess | undefined;
1011

0 commit comments

Comments
 (0)