Skip to content

Commit 3082a6e

Browse files
committed
feat(telemetry): adding initial telemetry functionality to the cli
1 parent 903cc00 commit 3082a6e

File tree

16 files changed

+3360
-35
lines changed

16 files changed

+3360
-35
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@
122122
"@socketregistry/packageurl-js": "1.0.9",
123123
"@socketsecurity/config": "3.0.1",
124124
"@socketsecurity/registry": "1.1.17",
125-
"@socketsecurity/sdk": "1.4.94",
125+
"@socketsecurity/sdk": "1.4.95",
126126
"@types/blessed": "0.1.25",
127127
"@types/cmd-shim": "5.0.2",
128128
"@types/js-yaml": "4.0.9",

pnpm-lock.yaml

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

src/cli.mts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,20 @@ import { AuthError, InputError, captureException } from './utils/errors.mts'
1818
import { failMsgWithBadge } from './utils/fail-msg-with-badge.mts'
1919
import { meowWithSubcommands } from './utils/meow-with-subcommands.mts'
2020
import { serializeResultJson } from './utils/serialize-result-json.mts'
21+
import {
22+
finalizeTelemetry,
23+
trackCliComplete,
24+
trackCliError,
25+
trackCliStart,
26+
} from './utils/telemetry/integration.mts'
2127
import { socketPackageLink } from './utils/terminal-link.mts'
2228

2329
const __filename = fileURLToPath(import.meta.url)
2430

2531
void (async () => {
32+
// Track CLI start for telemetry.
33+
const cliStartTime = await trackCliStart(process.argv)
34+
2635
const registryUrl = lookupRegistryUrl()
2736
await updateNotifier({
2837
authInfo: lookupRegistryAuthToken(registryUrl, { recursive: true }),
@@ -50,8 +59,14 @@ void (async () => {
5059
},
5160
{ aliases: rootAliases },
5261
)
62+
63+
// Track successful CLI completion.
64+
await trackCliComplete(process.argv, cliStartTime, process.exitCode)
5365
} catch (e) {
5466
process.exitCode = 1
67+
68+
// Track CLI error for telemetry.
69+
await trackCliError(process.argv, cliStartTime, e, process.exitCode)
5570
debugFn('error', 'CLI uncaught error')
5671
debugDir('error', e)
5772

@@ -104,5 +119,50 @@ void (async () => {
104119
}
105120

106121
await captureException(e)
122+
} finally {
123+
// Finalize telemetry to ensure all events are sent.
124+
// This runs on both success and error paths.
125+
await finalizeTelemetry()
107126
}
108-
})()
127+
})().catch(async err => {
128+
// Fatal error in main async function.
129+
console.error('Fatal error:', err)
130+
131+
// Track CLI error for fatal exceptions.
132+
await trackCliError(process.argv, Date.now(), err, 1)
133+
134+
// Finalize telemetry before fatal exit.
135+
await finalizeTelemetry()
136+
137+
// eslint-disable-next-line n/no-process-exit
138+
process.exit(1)
139+
})
140+
141+
// Handle uncaught exceptions.
142+
process.on('uncaughtException', async err => {
143+
console.error('Uncaught exception:', err)
144+
145+
// Track CLI error for uncaught exception.
146+
await trackCliError(process.argv, Date.now(), err, 1)
147+
148+
// Finalize telemetry before exit.
149+
await finalizeTelemetry()
150+
151+
// eslint-disable-next-line n/no-process-exit
152+
process.exit(1)
153+
})
154+
155+
// Handle unhandled promise rejections.
156+
process.on('unhandledRejection', async (reason, promise) => {
157+
console.error('Unhandled rejection at:', promise, 'reason:', reason)
158+
159+
// Track CLI error for unhandled rejection.
160+
const error = reason instanceof Error ? reason : new Error(String(reason))
161+
await trackCliError(process.argv, Date.now(), error, 1)
162+
163+
// Finalize telemetry before exit.
164+
await finalizeTelemetry()
165+
166+
// eslint-disable-next-line n/no-process-exit
167+
process.exit(1)
168+
})

src/commands/npm/cmd-npm.mts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import { commonFlags, outputFlags } from '../../flags.mts'
1212
import { filterFlags } from '../../utils/cmd.mts'
1313
import { meowOrExit } from '../../utils/meow-with-subcommands.mts'
1414
import { getFlagApiRequirementsOutput } from '../../utils/output-formatting.mts'
15+
import {
16+
trackSubprocessExit,
17+
trackSubprocessStart,
18+
} from '../../utils/telemetry/integration.mts'
1519

1620
import type {
1721
CliCommandConfig,
@@ -86,14 +90,22 @@ async function run(
8690
const argsToForward = filterFlags(argv, { ...commonFlags, ...outputFlags }, [
8791
FLAG_JSON,
8892
])
93+
94+
// Track subprocess start.
95+
const subprocessStartTime = await trackSubprocessStart(NPM)
96+
8997
const { spawnPromise } = await shadowNpmBin(argsToForward, {
9098
stdio: 'inherit',
9199
})
92100

101+
// Handle exit codes and signals using event-based pattern.
93102
// See https://nodejs.org/api/child_process.html#event-exit.
94103
spawnPromise.process.on(
95104
'exit',
96-
(code: string | null, signalName: NodeJS.Signals | null) => {
105+
async (code: number | null, signalName: NodeJS.Signals | null) => {
106+
// Track subprocess exit and flush telemetry.
107+
await trackSubprocessExit(NPM, subprocessStartTime, code)
108+
97109
if (signalName) {
98110
process.kill(process.pid, signalName)
99111
} else if (typeof code === 'number') {

src/commands/npx/cmd-npx.mts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import constants, { FLAG_DRY_RUN, FLAG_HELP, NPX } from '../../constants.mts'
66
import { commonFlags } from '../../flags.mts'
77
import { meowOrExit } from '../../utils/meow-with-subcommands.mts'
88
import { getFlagApiRequirementsOutput } from '../../utils/output-formatting.mts'
9+
import {
10+
trackSubprocessExit,
11+
trackSubprocessStart,
12+
} from '../../utils/telemetry/integration.mts'
913

1014
import type {
1115
CliCommandConfig,
@@ -74,12 +78,19 @@ async function run(
7478

7579
process.exitCode = 1
7680

81+
// Track subprocess start.
82+
const subprocessStartTime = await trackSubprocessStart(NPX)
83+
7784
const { spawnPromise } = await shadowNpxBin(argv, { stdio: 'inherit' })
7885

86+
// Handle exit codes and signals using event-based pattern.
7987
// See https://nodejs.org/api/child_process.html#event-exit.
8088
spawnPromise.process.on(
8189
'exit',
82-
(code: string | null, signalName: NodeJS.Signals | null) => {
90+
async (code: number | null, signalName: NodeJS.Signals | null) => {
91+
// Track subprocess exit and flush telemetry.
92+
await trackSubprocessExit(NPX, subprocessStartTime, code)
93+
8394
if (signalName) {
8495
process.kill(process.pid, signalName)
8596
} else if (typeof code === 'number') {

src/commands/pnpm/cmd-pnpm.mts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import { commonFlags } from '../../flags.mts'
77
import { filterFlags } from '../../utils/cmd.mts'
88
import { meowOrExit } from '../../utils/meow-with-subcommands.mts'
99
import { getFlagApiRequirementsOutput } from '../../utils/output-formatting.mts'
10+
import {
11+
trackSubprocessExit,
12+
trackSubprocessStart,
13+
} from '../../utils/telemetry/integration.mts'
1014

1115
import type {
1216
CliCommandConfig,
@@ -81,10 +85,29 @@ async function run(
8185
// Filter Socket flags from argv.
8286
const filteredArgv = filterFlags(argv, config.flags)
8387

88+
// Track subprocess start.
89+
const subprocessStartTime = await trackSubprocessStart(PNPM)
90+
8491
const { spawnPromise } = await shadowPnpmBin(filteredArgv, {
8592
stdio: 'inherit',
8693
})
8794

95+
// Handle exit codes and signals using event-based pattern.
96+
// See https://nodejs.org/api/child_process.html#event-exit.
97+
spawnPromise.process.on(
98+
'exit',
99+
async (code: number | null, signalName: NodeJS.Signals | null) => {
100+
// Track subprocess exit and flush telemetry.
101+
await trackSubprocessExit(PNPM, subprocessStartTime, code)
102+
103+
if (signalName) {
104+
process.kill(process.pid, signalName)
105+
} else if (typeof code === 'number') {
106+
// eslint-disable-next-line n/no-process-exit
107+
process.exit(code)
108+
}
109+
},
110+
)
111+
88112
await spawnPromise
89-
process.exitCode = 0
90113
}

src/commands/yarn/cmd-yarn.mts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import { commonFlags } from '../../flags.mts'
77
import { filterFlags } from '../../utils/cmd.mts'
88
import { meowOrExit } from '../../utils/meow-with-subcommands.mts'
99
import { getFlagApiRequirementsOutput } from '../../utils/output-formatting.mts'
10+
import {
11+
trackSubprocessExit,
12+
trackSubprocessStart,
13+
} from '../../utils/telemetry/integration.mts'
1014

1115
import type {
1216
CliCommandConfig,
@@ -81,10 +85,29 @@ async function run(
8185
// Filter Socket flags from argv.
8286
const filteredArgv = filterFlags(argv, config.flags)
8387

88+
// Track subprocess start.
89+
const subprocessStartTime = await trackSubprocessStart(YARN)
90+
8491
const { spawnPromise } = await shadowYarnBin(filteredArgv, {
8592
stdio: 'inherit',
8693
})
8794

95+
// Handle exit codes and signals using event-based pattern.
96+
// See https://nodejs.org/api/child_process.html#event-exit.
97+
spawnPromise.process.on(
98+
'exit',
99+
async (code: number | null, signalName: NodeJS.Signals | null) => {
100+
// Track subprocess exit and flush telemetry.
101+
await trackSubprocessExit(YARN, subprocessStartTime, code)
102+
103+
if (signalName) {
104+
process.kill(process.pid, signalName)
105+
} else if (typeof code === 'number') {
106+
// eslint-disable-next-line n/no-process-exit
107+
process.exit(code)
108+
}
109+
},
110+
)
111+
88112
await spawnPromise
89-
process.exitCode = 0
90113
}

0 commit comments

Comments
 (0)