Skip to content

Commit fc0838e

Browse files
committed
Telemetry: non-blocking, broader categories, simple opt-out setting
- initTelemetry is now fire-and-forget — client.start() runs in the background so activation never blocks on it. trackEvent() was already in-memory + batched, so subsequent sendEvent calls are non-blocking. - Drop per-command COMMAND_INVOKED events. Replace with FEATURE_USED, bucketed by broad category (sandbox/webdav/content/codeSync/apiBrowser/ debugger/scaffold/cap/pageDesigner/logs/instance) and deduped to one event per category per session. - Add b2c-dx.telemetry.enabled setting (default true). Telemetry is off if any of the three signals say off: VS Code's telemetry.telemetryLevel, this setting, or the SF/SFCC env var. - Strip the Telemetry section from the docs site — the setting's description in the VS Code Settings UI is the disclosure surface. Add the row to the configuration page's settings reference table. - Wire markFeatureUsed into the page-designer command and the script debugger factory so non-safety-wrapped features still register usage.
1 parent fb75abd commit fc0838e

8 files changed

Lines changed: 126 additions & 107 deletions

File tree

.changeset/vscode-telemetry.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
'b2c-vs-extension': minor
33
---
44

5-
Add anonymous usage telemetry (extension lifecycle, command invocations, exceptions) to help prioritize fixes during the Developer Preview. Honors VS Code's `telemetry.telemetryLevel` setting and the `SFCC_DISABLE_TELEMETRY` / `SF_DISABLE_TELEMETRY` environment variables. No credentials, hostnames, or business data are collected. See the [Telemetry section in Configuration](https://salesforcecommercecloud.github.io/b2c-developer-tooling/vscode-extension/configuration#telemetry) for opt-out instructions and the full data inventory.
5+
Add anonymous usage telemetry (extension activation/deactivation lifecycle, broad feature-category usage, exceptions) to help prioritize fixes during the Developer Preview. Sending is non-blocking. Honors the new `b2c-dx.telemetry.enabled` setting (default `true`), VS Code's `telemetry.telemetryLevel`, and the `SFCC_DISABLE_TELEMETRY` / `SF_DISABLE_TELEMETRY` environment variables. No credentials, hostnames, or business data are collected.

docs/vscode-extension/configuration.md

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ This page covers:
1111
- [Connecting to a B2C Instance](#connecting-to-a-b2c-instance) — credentials per feature.
1212
- [Switching the Active Instance](#switching-the-active-instance) — single-workspace, multi-instance.
1313
- [Project Root Pinning](#project-root-pinning) — multi-root workspaces.
14-
- [Telemetry](#telemetry) — what we collect and how to opt out.
1514
- [Settings Reference](#settings-reference) — the `b2c-dx.*` toggles and verbosity controls.
1615

1716
## Connecting to a B2C Instance
@@ -76,24 +75,6 @@ In a multi-root workspace, the extension auto-detects the project root by walkin
7675

7776
The pin is workspace-scoped (stored in workspace state).
7877

79-
## Telemetry
80-
81-
The extension reports anonymous usage data to help us prioritize fixes during the Developer Preview.
82-
83-
**What we collect:** extension lifecycle events (`EXTENSION_ACTIVATED`, `EXTENSION_DEACTIVATED`, `ACTIVATION_FAILED`), command invocations (command ID, success/failure, duration), and exceptions. Each event includes anonymous session and machine identifiers, plus environment info (VS Code version, platform, architecture, Node.js version).
84-
85-
**What we don't collect:** credentials, hostnames, sandbox IDs, file contents, command arguments, or any business data. String attributes have `$HOME` redacted to `~` before transmission.
86-
87-
**Opt out** in any of the following ways — telemetry is disabled if **any** of them is true:
88-
89-
| Source | Setting |
90-
| ------ | ------- |
91-
| VS Code `settings.json` | `"telemetry.telemetryLevel": "off"` (also disables when set to `crash` or `error`) |
92-
| Environment variable | `SFCC_DISABLE_TELEMETRY=true` |
93-
| Environment variable | `SF_DISABLE_TELEMETRY=true` (Salesforce CLI standard) |
94-
95-
The extension respects VS Code's built-in `telemetry.telemetryLevel` first, so opting out of all VS Code telemetry automatically disables ours.
96-
9778
## Settings Reference
9879

9980
These VS Code settings live under the `b2c-dx.*` namespace. **You usually don't need to change any of them** — they exist for niche cases like disabling a feature you don't use, or quieting the log channel for a bug report. To browse: **Settings** (Cmd+,) → search for `b2c-dx`.
@@ -115,12 +96,13 @@ Each feature is enabled by default. Set to `false` to skip its activation entire
11596

11697
The B2C Script Debugger registers regardless of these toggles — it activates only when a `b2c-script` launch configuration is used.
11798

118-
### Verbosity & polling
99+
### Verbosity, polling, telemetry
119100

120101
| Setting | Default | Description |
121102
| ------- | ------- | ----------- |
122103
| `b2c-dx.logLevel` | `info` | Verbosity for the **B2C DX** output channel. Allowed: `trace`, `debug`, `info`, `warn`, `error`, `silent`. Applied immediately on change. Drop to `debug` or `trace` when filing a bug. |
123104
| `b2c-dx.sandbox.pollingInterval` | `10` | Seconds between polls while a sandbox is in a transitional state (`creating`, `starting`, `stopping`, `deleting`, `cloning`). Range: 2–300. Polling stops automatically once the realm settles. |
105+
| `b2c-dx.telemetry.enabled` | `true` | Send anonymous usage telemetry. Honors VS Code's `telemetry.telemetryLevel` — disabling that disables this regardless of this setting. |
124106

125107
### Complete defaults (copy-paste)
126108

@@ -136,7 +118,8 @@ The B2C Script Debugger registers regardless of these toggles — it activates o
136118
"b2c-dx.features.apiBrowser": true,
137119
"b2c-dx.features.cap": true,
138120
"b2c-dx.logLevel": "info",
139-
"b2c-dx.sandbox.pollingInterval": 10
121+
"b2c-dx.sandbox.pollingInterval": 10,
122+
"b2c-dx.telemetry.enabled": true
140123
}
141124
```
142125

packages/b2c-vs-extension/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@
9696
"type": "boolean",
9797
"default": true,
9898
"description": "Enable Code Sync (watch and deploy cartridges)."
99+
},
100+
"b2c-dx.telemetry.enabled": {
101+
"type": "boolean",
102+
"default": true,
103+
"description": "Send anonymous usage telemetry (extension lifecycle and broad feature-category events). Honors VS Code's telemetry.telemetryLevel — disabling that disables this regardless of this setting."
99104
}
100105
}
101106
},

packages/b2c-vs-extension/src/debugger/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import {B2CScriptDebugAdapter} from '@salesforce/b2c-tooling-sdk/operations/debu
88
import type {DebugSessionConfig} from '@salesforce/b2c-tooling-sdk/operations/debug';
99
import * as vscode from 'vscode';
1010
import type {B2CExtensionConfig} from '../config-provider.js';
11+
import {markFeatureUsed} from '../telemetry.js';
1112

1213
const DEBUG_TYPE = 'b2c-script';
1314

1415
class B2CDebugAdapterFactory implements vscode.DebugAdapterDescriptorFactory {
1516
constructor(private readonly configProvider: B2CExtensionConfig) {}
1617

1718
createDebugAdapterDescriptor(_session: vscode.DebugSession): vscode.ProviderResult<vscode.DebugAdapterDescriptor> {
19+
markFeatureUsed('debugger');
1820
const config = this.configProvider.getConfig();
1921
if (!config || !config.hasB2CInstanceConfig()) {
2022
void vscode.window.showErrorMessage(

packages/b2c-vs-extension/src/extension.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {registerApiBrowser} from './api-browser/index.js';
2222
import {registerDebugger} from './debugger/index.js';
2323
import {registerCodeSync} from './code-sync/index.js';
2424
import {registerWebDavTree} from './webdav-tree/index.js';
25-
import {disposeTelemetry, initTelemetry, sendEvent, sendException} from './telemetry.js';
25+
import {disposeTelemetry, initTelemetry, markFeatureUsed, sendEvent, sendException} from './telemetry.js';
2626

2727
function getWebviewContent(context: vscode.ExtensionContext): string {
2828
const htmlPath = path.join(context.extensionPath, 'src', 'webview.html');
@@ -115,15 +115,14 @@ export async function activate(context: vscode.ExtensionContext) {
115115

116116
applyLogLevel(log);
117117

118-
// Best-effort telemetry init. A no-op when the user has VS Code telemetry
119-
// disabled, when SFCC_DISABLE_TELEMETRY/SF_DISABLE_TELEMETRY is set, or when
120-
// no connection string is configured.
121-
await initTelemetry(context);
122-
const activationStart = Date.now();
118+
// Best-effort telemetry init. Non-blocking: client.start() runs in the
119+
// background. No-ops entirely when the user has telemetry disabled or no
120+
// connection string is configured.
121+
initTelemetry(context);
123122

124123
try {
125124
const result = await activateInner(context, log);
126-
sendEvent('EXTENSION_ACTIVATED', {durationMs: Date.now() - activationStart});
125+
sendEvent('EXTENSION_ACTIVATED');
127126
return result;
128127
} catch (err) {
129128
const message = err instanceof Error ? err.message : String(err);
@@ -132,7 +131,7 @@ export async function activate(context: vscode.ExtensionContext) {
132131
if (stack) log.appendLine(stack);
133132
console.error('B2C DX extension activation failed:', err);
134133
if (err instanceof Error) sendException(err, {phase: 'activate'});
135-
sendEvent('ACTIVATION_FAILED', {durationMs: Date.now() - activationStart});
134+
sendEvent('ACTIVATION_FAILED');
136135
vscode.window.showErrorMessage(`B2C DX: Extension failed to activate. See Output > B2C DX. Error: ${message}`);
137136
const showActivationError = () => {
138137
log.show();
@@ -166,6 +165,7 @@ async function activateInner(context: vscode.ExtensionContext, log: vscode.Outpu
166165
registerSafety(context, configProvider);
167166

168167
const disposable = vscode.commands.registerCommand('b2c-dx.openUI', () => {
168+
markFeatureUsed('pageDesigner');
169169
vscode.window.showInformationMessage('B2C DX: Opening Page Designer Assistant.');
170170

171171
const panel = vscode.window.createWebviewPanel(

packages/b2c-vs-extension/src/safety.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,32 @@ import {createSafetyMiddleware, globalMiddlewareRegistry} from '@salesforce/b2c-
1515
import {getLogger} from '@salesforce/b2c-tooling-sdk/logging';
1616
import * as vscode from 'vscode';
1717
import type {B2CExtensionConfig} from './config-provider.js';
18-
import {wrapCommandHandler} from './telemetry.js';
18+
import {type FeatureCategory, markFeatureUsed} from './telemetry.js';
19+
20+
/**
21+
* Map command-id prefixes to broad feature categories so usage telemetry can
22+
* answer "did the user use feature X this session" without per-command noise.
23+
* Commands not matched here don't emit a usage event (the lifecycle events
24+
* still fire). Keep the prefix list short and stable.
25+
*/
26+
const COMMAND_PREFIX_TO_CATEGORY: Array<readonly [string, FeatureCategory]> = [
27+
['b2c-dx.sandbox.', 'sandbox'],
28+
['b2c-dx.webdav.', 'webdav'],
29+
['b2c-dx.content.', 'content'],
30+
['b2c-dx.codeSync.', 'codeSync'],
31+
['b2c-dx.apiBrowser.', 'apiBrowser'],
32+
['b2c-dx.scaffold.', 'scaffold'],
33+
['b2c-dx.cap.', 'cap'],
34+
['b2c-dx.logs.', 'logs'],
35+
['b2c-dx.instance.', 'instance'],
36+
];
37+
38+
function categoryForCommand(commandId: string): FeatureCategory | undefined {
39+
for (const [prefix, category] of COMMAND_PREFIX_TO_CATEGORY) {
40+
if (commandId.startsWith(prefix)) return category;
41+
}
42+
return undefined;
43+
}
1944

2045
const PROVIDER_NAME = 'vscode-safety-guard';
2146

@@ -100,9 +125,10 @@ export function runWithSafety<T>(operation: () => Promise<T>, detail?: string):
100125
// Matches vscode.commands.registerCommand's own signature, which uses any[] for context-menu args.
101126
// eslint-disable-next-line @typescript-eslint/no-explicit-any
102127
export function registerSafeCommand(commandId: string, handler: (...args: any[]) => any): vscode.Disposable {
103-
const tracedHandler = wrapCommandHandler(commandId, handler);
128+
const category = categoryForCommand(commandId);
104129
// eslint-disable-next-line @typescript-eslint/no-explicit-any
105130
return vscode.commands.registerCommand(commandId, async (...args: any[]) => {
131+
if (category) markFeatureUsed(category);
106132
try {
107133
currentGuard.assert({type: 'command', commandId});
108134
} catch (err) {
@@ -119,13 +145,13 @@ export function registerSafeCommand(commandId: string, handler: (...args: any[])
119145
if (choice !== 'Proceed') return undefined;
120146
const release = currentGuard.temporarilyAllow(err.evaluation.operation);
121147
try {
122-
return await tracedHandler(...args);
148+
return await handler(...args);
123149
} finally {
124150
release();
125151
}
126152
}
127153
throw err;
128154
}
129-
return tracedHandler(...args);
155+
return handler(...args);
130156
});
131157
}

0 commit comments

Comments
 (0)