Skip to content

Commit 21b6e38

Browse files
piyalbasuclaude
andauthored
Set anonymous Sentry user so alerts report users affected (#2840)
* Set anonymous Sentry user so alerts report users affected Sentry alerts showed "Users: 0" because no user context was ever attached to events. On @sentry/browser v9, sendDefaultPii defaults to false (no IP), and the extension never called Sentry.setUser, so every event arrived with an empty user and could not be attributed. Attach the existing persisted anonymous analytics id (the same one Amplitude uses) via Sentry.setUser, gated on the existing data-sharing consent. No PII or wallet address — just an opaque per-install id, so Sentry and Amplitude user counts stay aligned. Mirrors freighter-mobile. Also harden generateRandomUserId: Math.random() can yield values < 1e-6 (exponential notation) or exactly 0, where split(".")[1] is undefined; fall back to "0" so we never hand Sentry/Amplitude an undefined id. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Address review: clear Sentry user on opt-out, stub getUserId in jest mock - Clear the per-install user id (Sentry.setUser(null)) before Sentry.close on data-sharing opt-out. Sentry.close may still report (anonymized) until the next refresh; without this those reports would keep the id attached. - Add getUserId to the helpers/metrics jest mock in setupTests so rendering ErrorTracking under test doesn't call an undefined mock. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 4112516 commit 21b6e38

3 files changed

Lines changed: 20 additions & 4 deletions

File tree

config/jest/setupTests.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ jest.mock("helpers/metrics", () => ({
7979
registerHandler: () => {},
8080
emitMetric: () => {},
8181
initAmplitude: () => {},
82+
getUserId: () => "test-user-id",
8283
metricsMiddleware: jest.fn(
8384
() => () => (next: any) => (action: any) => next(action),
8485
),

extension/src/helpers/metrics.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,10 @@ const AMPLITUDE_FLUSH_INTERVAL_MS = 500;
111111

112112
/** Mirrors mobile's `generateRandomUserId` — a numeric decimal string. */
113113
const generateRandomUserId = (): string =>
114-
Math.random().toString().split(".")[1];
114+
// Math.random() can yield values < 1e-6 (exponential notation, e.g. "4e-7")
115+
// or exactly 0 ("0"), where `split(".")[1]` is undefined. Fall back to "0"
116+
// so we never persist or hand Sentry/Amplitude an undefined user id.
117+
Math.random().toString().split(".")[1] ?? "0";
115118

116119
/** Session-level cache, mirrors mobile's module-level `sessionUserId` fallback. */
117120
let sessionUserId: string | null = null;
@@ -122,7 +125,7 @@ let sessionUserId: string | null = null;
122125
* - Reads from localStorage under key `"metrics_user_id"`
123126
* - Falls back to a session-only ID if storage is unavailable
124127
*/
125-
const getUserId = (): string => {
128+
export const getUserId = (): string => {
126129
try {
127130
const stored = localStorage.getItem(METRICS_USER_ID);
128131
if (stored) {

extension/src/popup/components/ErrorTracking/index.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useSelector } from "react-redux";
22
import * as Sentry from "@sentry/browser";
33

44
import { SENTRY_KEY } from "constants/env";
5+
import { getUserId } from "helpers/metrics";
56
import { settingsDataSharingSelector } from "popup/ducks/settings";
67
import { scrubPathGkey } from "popup/helpers/formatters";
78
import { INDEXER_URL } from "@shared/constants/mercury";
@@ -45,14 +46,25 @@ export const ErrorTracking = () => {
4546
return event;
4647
},
4748
});
49+
50+
// Attach a stable anonymous user ID so Sentry can report "users
51+
// affected" counts. Reuses the same persisted random ID as Amplitude
52+
// (helpers/metrics getUserId) so user counts stay aligned across the two.
53+
// No PII / wallet address — just an opaque per-install identifier.
54+
// Mirrors freighter-mobile's Sentry.setUser in src/components/App.tsx.
55+
Sentry.setUser({ id: getUserId() });
4856
}
4957

5058
if (!isDataSharingAllowed) {
51-
/*
52-
Note: Sentry.close does not completely disable calls to Sentry. Sentry will still report, but with a completely anonymized payload.
59+
/*
60+
Note: Sentry.close does not completely disable calls to Sentry. Sentry will still report, but with a completely anonymized payload.
5361
When you refresh/reopen the app after disabling tracking, it will not initialize Sentry, thus disabling *all* calls to Sentry
5462
*/
5563

64+
// Clear the per-install user id before closing. Sentry.close may still
65+
// report (anonymized) until the next refresh; without this, those reports
66+
// would keep the user id attached and defeat the anonymization.
67+
Sentry.setUser(null);
5668
Sentry.close(500);
5769
}
5870

0 commit comments

Comments
 (0)