Skip to content

Commit f62e0cf

Browse files
authored
chore: track startup times (#22)
Adds profiling traces to the output channel for the startup times of the extension and its plugins.
1 parent d436590 commit f62e0cf

File tree

14 files changed

+302
-157
lines changed

14 files changed

+302
-157
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/extension.ts

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import ms from "ms";
12
import { StatusBarAlignment, window } from "vscode";
23
import type { ExtensionContext } from "vscode";
34

@@ -13,6 +14,7 @@ import { createLocalStackStatusTracker } from "./utils/localstack-status.ts";
1314
import { getOrCreateExtensionSessionId } from "./utils/manage.ts";
1415
import { createSetupStatusTracker } from "./utils/setup-status.ts";
1516
import { createTelemetry } from "./utils/telemetry.ts";
17+
import { createTimeTracker } from "./utils/time-tracker.ts";
1618

1719
const plugins = new PluginManager([
1820
setup,
@@ -29,36 +31,83 @@ export async function activate(context: ExtensionContext) {
2931
});
3032
context.subscriptions.push(outputChannel);
3133

32-
const statusBarItem = window.createStatusBarItem(StatusBarAlignment.Left, -1);
33-
context.subscriptions.push(statusBarItem);
34-
statusBarItem.text = "$(loading~spin) LocalStack";
35-
statusBarItem.show();
34+
const timeTracker = createTimeTracker({ outputChannel });
3635

37-
const containerStatusTracker = await createContainerStatusTracker(
38-
"localstack-main",
39-
outputChannel,
40-
);
41-
context.subscriptions.push(containerStatusTracker);
42-
43-
const localStackStatusTracker = await createLocalStackStatusTracker(
44-
containerStatusTracker,
45-
outputChannel,
46-
);
47-
context.subscriptions.push(localStackStatusTracker);
48-
49-
const setupStatusTracker = await createSetupStatusTracker(outputChannel);
50-
51-
const sessionId = await getOrCreateExtensionSessionId(context);
52-
const telemetry = createTelemetry(outputChannel, sessionId);
53-
54-
await plugins.activate({
55-
context,
56-
outputChannel,
57-
statusBarItem,
36+
const {
5837
containerStatusTracker,
5938
localStackStatusTracker,
6039
setupStatusTracker,
40+
statusBarItem,
6141
telemetry,
42+
} = await timeTracker.run("extension.dependencies", async () => {
43+
const statusBarItem = window.createStatusBarItem(
44+
StatusBarAlignment.Left,
45+
-1,
46+
);
47+
context.subscriptions.push(statusBarItem);
48+
statusBarItem.text = "$(loading~spin) LocalStack";
49+
statusBarItem.show();
50+
51+
const containerStatusTracker = await createContainerStatusTracker(
52+
"localstack-main",
53+
outputChannel,
54+
timeTracker,
55+
);
56+
context.subscriptions.push(containerStatusTracker);
57+
58+
const localStackStatusTracker = await createLocalStackStatusTracker(
59+
containerStatusTracker,
60+
outputChannel,
61+
timeTracker,
62+
);
63+
context.subscriptions.push(localStackStatusTracker);
64+
65+
outputChannel.trace(`[setup-status]: Starting...`);
66+
const startStatusTracker = Date.now();
67+
const setupStatusTracker = await createSetupStatusTracker(
68+
outputChannel,
69+
timeTracker,
70+
);
71+
context.subscriptions.push(setupStatusTracker);
72+
const endStatusTracker = Date.now();
73+
outputChannel.trace(
74+
`[setup-status]: Completed in ${ms(
75+
endStatusTracker - startStatusTracker,
76+
{ long: true },
77+
)}`,
78+
);
79+
80+
const startTelemetry = Date.now();
81+
outputChannel.trace(`[telemetry]: Starting...`);
82+
const sessionId = await getOrCreateExtensionSessionId(context);
83+
const telemetry = createTelemetry(outputChannel, sessionId);
84+
const endTelemetry = Date.now();
85+
outputChannel.trace(
86+
`[telemetry]: Completed in ${ms(endTelemetry - startTelemetry, {
87+
long: true,
88+
})}`,
89+
);
90+
91+
return {
92+
statusBarItem,
93+
containerStatusTracker,
94+
localStackStatusTracker,
95+
setupStatusTracker,
96+
telemetry,
97+
};
98+
});
99+
100+
await timeTracker.run("extension.activatePlugins", async () => {
101+
await plugins.activate({
102+
context,
103+
outputChannel,
104+
statusBarItem,
105+
containerStatusTracker,
106+
localStackStatusTracker,
107+
setupStatusTracker,
108+
telemetry,
109+
timeTracker,
110+
});
62111
});
63112
}
64113

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+
`[extension.plugins]: 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+
`[extension.plugins]: Activated plugin "${activate.name}" in ${ms(
69+
endPlugin - startPlugin,
70+
{ long: true },
71+
)}`,
72+
);
5273
}
5374
}
5475

0 commit comments

Comments
 (0)