Skip to content

Commit

Permalink
fix(sentry): Update and use local client to not pollute global scope (#…
Browse files Browse the repository at this point in the history
…37)

* fix(sentry): Update sentry and use local client to avoid polluting global scope

* fix lint

* Update github actions dependencies

* Update test dependencies
  • Loading branch information
carlos-algms authored Nov 1, 2024
1 parent adecedf commit 37b168a
Show file tree
Hide file tree
Showing 9 changed files with 416 additions and 194 deletions.
4 changes: 2 additions & 2 deletions .github/actions/setup_workspace/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ description: Checkout the repo, setup Node, and install dependencies
runs:
using: composite
steps:
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
cache: yarn

- run: yarn --frozen-lockfile
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/pr_validate.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Validate Pull Requests

on:
pull_request:
pull_request:

jobs:
validate:
Expand All @@ -12,7 +12,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- id: checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0

Expand All @@ -23,6 +23,6 @@ jobs:
- run: make compile_prod

- name: Run headless test
uses: GabrielBB/xvfb-action@v1
uses: coactions/setup-xvfb@v1
with:
run: make test
4 changes: 2 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
},
"editor.formatOnSave": false,
"editor.formatOnSave": true,
"eslint.packageManager": "yarn",
"npm.packageManager": "yarn",
"typescript.tsc.autoDetect": "off",
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,18 +197,18 @@
"dependencies": {},
"devDependencies": {
"@amplitude/node": "^1.10.2",
"@sentry/node": "^7.47.0",
"@sentry/browser": "^8.35.0",
"@types/chai": "^4.3.4",
"@types/chai-as-promised": "^7.1.5",
"@types/glob": "^7.2.0",
"@types/mocha": "^10.0.1",
"@types/node": "^18",
"@types/node": "^20",
"@types/sinon": "^10.0.13",
"@types/sinon-chai": "^3.2.9",
"@types/vscode": "~1.70.0",
"@typescript-eslint/eslint-plugin": "^5.57.1",
"@typescript-eslint/parser": "^5.57.1",
"@vscode/test-electron": "^2.3.0",
"@vscode/test-electron": "^2.4.0",
"chai": "^4.3.7",
"chai-as-promised": "^7.1.1",
"esbuild": "^0.17.15",
Expand Down
85 changes: 0 additions & 85 deletions src/telemetry/AmplitudeVsCodeAnalyticsClient.ts

This file was deleted.

131 changes: 131 additions & 0 deletions src/telemetry/VsCodeAnalyticsClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { init as AmplitudeInit } from '@amplitude/node';
import {
BrowserClient,
defaultStackParser,
getDefaultIntegrations,
makeFetchTransport,
Scope,
} from '@sentry/browser';
import * as crypto from 'crypto';
import { machineIdSync } from 'node-machine-id';
import { userInfo } from 'os';
import {
Event as AnalyticsEvent,
Exception as AnalyticsException,
IAnalyticsClient,
} from 'vscode-extension-analytics';

export { AnalyticsEvent, AnalyticsException };

const API_KEY = '74556fcd07b7703909928dae21126b7f';

// https://github.com/threadheap/serverless-ide-vscode/blob/master/packages/vscode/src/analytics/index.ts
export default class VsCodeAnalyticsClient implements IAnalyticsClient {
static SentryInitialized = false;

private amplitudeInstance = AmplitudeInit(API_KEY);

private deviceId = machineIdSync();

private sessionId = Date.now();

private userId: string;

private sentryScope = new Scope();

private exceptionsSent = new Set<string>();

constructor(private extensionId: string, private extensionVersion: string) {
const user = userInfo({ encoding: 'utf8' });
this.userId = crypto.createHash('md5').update(user.username).digest('hex');
}

initialise(): void {
if (VsCodeAnalyticsClient.SentryInitialized) {
return;
}

VsCodeAnalyticsClient.SentryInitialized = true;

// https://docs.sentry.io/platforms/javascript/best-practices/shared-environments/

// filter integrations that use the global variable
const integrations = getDefaultIntegrations({}).filter((defaultIntegration) => {
return ![
'BrowserApiErrors',
'Breadcrumbs',
'GlobalHandlers',
'OnUncaughtException',
'OnUnhandledRejection',
].includes(defaultIntegration.name);
});

const client = new BrowserClient({
dsn: 'https://[email protected]/5613298',
transport: makeFetchTransport,
stackParser: defaultStackParser,
integrations,
release: `${this.extensionId}@${this.extensionVersion}`,
// Leaving here for historical reasons
// not needed anymore, as the client is not not tied to the global scope
// beforeSend(event) {
// // Filter out events that are not from this extension
// if (event.extra?.['common.extname'] !== APP_NAME) {
// return null;
// }

// return event;
// },
});

this.sentryScope.setClient(client);
this.sentryScope.setUser({
id: this.userId,
});

this.sentryScope.setTags({
deviceId: this.deviceId,
sessionId: this.sessionId.toString(),
});

client.init(); // initializing has to be done after setting the client on the scope
}

async flush(): Promise<void> {
await this.amplitudeInstance.flush();
}

sendEvent(event: AnalyticsEvent): void {
this.amplitudeInstance.logEvent({
event_type: event.action,
user_id: this.userId,
device_id: this.deviceId,
session_id: this.sessionId,
event_properties: event.toJSON(),
});
}

sendException({ error, attributes }: AnalyticsException): void {
if (!this.exceptionsSent.has(error.message)) {
this.exceptionsSent.add(error.message);
const { action, category, label, ...extra } = attributes;

// When running with the Browser client, I need to clone the scope to get isolated data
// https://docs.sentry.io/platforms/javascript/best-practices/multiple-sentry-instances/#using-withscope-with-multiple-clients
const scope = this.sentryScope.clone();

scope.setTags({
action,
category,
label,
} as any);

scope.setExtras(extra);

scope.captureException(error, {
data: attributes,
});
}
}
}
7 changes: 2 additions & 5 deletions src/telemetry/tracking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ import { AnalyticsReporter, Attributes } from 'vscode-extension-analytics';
import { APP_NAME, APP_VERSION } from '../shared/constants';
import getOutputChannel from '../shared/getOutputChannel';

import AmplitudeVsCodeAnalyticsClient, {
AnalyticsEvent,
AnalyticsException,
} from './AmplitudeVsCodeAnalyticsClient';
import VsCodeAnalyticsClient, { AnalyticsEvent, AnalyticsException } from './VsCodeAnalyticsClient';

export type Primitive = string | number | null | undefined;

Expand All @@ -24,7 +21,7 @@ export type StandardAttributes = Attributes & {
let tracker: AnalyticsReporter | null = null;

const createReporter = (extensionId: string, extensionVersion: string): AnalyticsReporter => {
const client = new AmplitudeVsCodeAnalyticsClient(extensionId, extensionVersion);
const client = new VsCodeAnalyticsClient(extensionId, extensionVersion);

return new AnalyticsReporter(extensionId, extensionVersion, client, {
configId: 'make-task-provider.telemetry',
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
"newLine": "lf",
"removeComments": false,
"resolveJsonModule": true,
"useUnknownInCatchVariables": false
"useUnknownInCatchVariables": false,
"skipLibCheck": true
},
"exclude": ["node_modules", "src/test", ".vscode-test", "build", ".temp"]
}
Loading

0 comments on commit 37b168a

Please sign in to comment.