feat: remove bugsnag (#931)
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
bugsnagApiKey?: string;
|
||||
dashboardUrl?: string;
|
||||
defaultSyncServer: string;
|
||||
devAccountEmail?: string;
|
||||
@@ -22,7 +21,6 @@ import { render } from 'preact';
|
||||
import { ApplicationGroupView } from './components/ApplicationGroupView';
|
||||
import { Bridge } from './services/bridge';
|
||||
import { BrowserBridge } from './services/browserBridge';
|
||||
import { startErrorReporting } from './services/errorReporting';
|
||||
import { StartApplication } from './startApplication';
|
||||
import { ApplicationGroup } from './ui_models/application_group';
|
||||
import { isDev } from './utils';
|
||||
@@ -34,7 +32,7 @@ const startApplication: StartApplication = async function startApplication(
|
||||
webSocketUrl: string
|
||||
) {
|
||||
SNLog.onLog = console.log;
|
||||
startErrorReporting();
|
||||
SNLog.onError = console.error;
|
||||
|
||||
const mainApplicationGroup = new ApplicationGroup(
|
||||
defaultSyncServerHost,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { WebApplication } from '@/ui_models/application';
|
||||
import { AppState } from '@/ui_models/app_state';
|
||||
import { FunctionComponent } from 'preact';
|
||||
import { PreferencesPane } from '../components';
|
||||
import { ErrorReporting, Tools, Defaults, LabsPane } from './general-segments';
|
||||
import { Tools, Defaults, LabsPane } from './general-segments';
|
||||
import { ExtensionsLatestVersions } from '@/preferences/panes/extensions-segments';
|
||||
import { Advanced } from '@/preferences/panes/account';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
@@ -18,7 +18,6 @@ export const General: FunctionComponent<GeneralProps> = observer(
|
||||
<PreferencesPane>
|
||||
<Tools application={application} />
|
||||
<Defaults application={application} />
|
||||
<ErrorReporting appState={appState} />
|
||||
<LabsPane application={application} />
|
||||
<Advanced
|
||||
application={application}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
import { useState } from 'preact/hooks';
|
||||
import { storage, StorageKey } from '@Services/localStorage';
|
||||
import { disableErrorReporting, enableErrorReporting, errorReportingId } from '@Services/errorReporting';
|
||||
import { alertDialog } from '@Services/alertService';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { AppState } from '@/ui_models/app_state';
|
||||
import { FunctionComponent } from 'preact';
|
||||
import { PreferencesGroup, PreferencesSegment, Title, Text } from '@/preferences/components';
|
||||
import { Switch } from '@/components/Switch';
|
||||
|
||||
type Props = {
|
||||
appState: AppState;
|
||||
}
|
||||
|
||||
export const ErrorReporting: FunctionComponent<Props> = observer(({ appState }: Props) => {
|
||||
const [isErrorReportingEnabled] = useState(() => storage.get(StorageKey.DisableErrorReporting) === false);
|
||||
const [errorReportingIdValue] = useState(() => errorReportingId());
|
||||
|
||||
const toggleErrorReportingEnabled = () => {
|
||||
if (isErrorReportingEnabled) {
|
||||
disableErrorReporting();
|
||||
} else {
|
||||
enableErrorReporting();
|
||||
}
|
||||
if (!appState.sync.inProgress) {
|
||||
window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
const openErrorReportingDialog = () => {
|
||||
alertDialog({
|
||||
title: 'Data sent during automatic error reporting',
|
||||
text: `
|
||||
We use <a target="_blank" rel="noreferrer" href="https://www.bugsnag.com/">Bugsnag</a>
|
||||
to automatically report errors that occur while the app is running. See
|
||||
<a target="_blank" rel="noreferrer" href="https://docs.bugsnag.com/platforms/javascript/#sending-diagnostic-data">
|
||||
this article, paragraph 'Browser' under 'Sending diagnostic data',
|
||||
</a>
|
||||
to see what data is included in error reports.
|
||||
<br><br>
|
||||
Error reports never include IP addresses and are fully
|
||||
anonymized. We use error reports to be alerted when something in our
|
||||
code is causing unexpected errors and crashes in your application
|
||||
experience.
|
||||
`
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<PreferencesGroup>
|
||||
<PreferencesSegment>
|
||||
|
||||
<div className="flex flex-row items-center">
|
||||
<div className="flex-grow flex flex-col">
|
||||
<Title>Error Reporting</Title>
|
||||
<Text>
|
||||
Help us improve Standard Notes by automatically submitting
|
||||
anonymized error reports.
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex flex-col justify-center items-center min-w-15">
|
||||
<Switch onChange={toggleErrorReportingEnabled} checked={isErrorReportingEnabled} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-h-2" />
|
||||
|
||||
{errorReportingIdValue && (
|
||||
<>
|
||||
<Text>
|
||||
Your random identifier is <span className="font-bold">{errorReportingIdValue}</span>
|
||||
</Text>
|
||||
<Text>
|
||||
Disabling error reporting will remove that identifier from your
|
||||
local storage, and a new identifier will be created should you
|
||||
decide to enable error reporting again in the future.
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Text>
|
||||
<a className="cursor-pointer" onClick={openErrorReportingDialog}>What data is being sent?</a>
|
||||
</Text>
|
||||
</PreferencesSegment>
|
||||
</PreferencesGroup>
|
||||
);
|
||||
});
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from './ErrorReporting';
|
||||
export * from './Tools';
|
||||
export * from './Defaults';
|
||||
export * from './Labs';
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
import { isNullOrUndefined, SNLog } from '@standardnotes/snjs';
|
||||
import { getDesktopVersion, isDesktopApplication, isDev } from '@/utils';
|
||||
import { storage, StorageKey } from './localStorage';
|
||||
import Bugsnag from '@bugsnag/js';
|
||||
import { WebCrypto } from '../crypto';
|
||||
import { WebAppVersion } from '@/version';
|
||||
|
||||
function redactFilePath(line: string): string {
|
||||
const fileName = line.match(/\w+\.(html|js)/)?.[0];
|
||||
const redacted = '<redacted file path>';
|
||||
if (fileName) {
|
||||
return redacted + '/' + fileName;
|
||||
} else {
|
||||
return redacted;
|
||||
}
|
||||
}
|
||||
|
||||
export function startErrorReporting(): void {
|
||||
const disableErrorReporting = storage.get(StorageKey.DisableErrorReporting);
|
||||
if (
|
||||
/**
|
||||
* Error reporting used to be opt-out, but is now opt-in, so
|
||||
* treat the absence of an error reporting preference as an indication
|
||||
* to disable error reporting.
|
||||
*/
|
||||
isNullOrUndefined(disableErrorReporting) ||
|
||||
disableErrorReporting ||
|
||||
!window.bugsnagApiKey
|
||||
) {
|
||||
SNLog.onError = console.error;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const storedUserId = storage.get(StorageKey.AnonymousUserId);
|
||||
let anonymousUserId: string;
|
||||
if (storedUserId === null) {
|
||||
anonymousUserId = WebCrypto.generateUUID();
|
||||
storage.set(StorageKey.AnonymousUserId, anonymousUserId);
|
||||
} else {
|
||||
anonymousUserId = storedUserId;
|
||||
}
|
||||
|
||||
Bugsnag.start({
|
||||
apiKey: window.bugsnagApiKey,
|
||||
appType: isDesktopApplication() ? 'desktop' : 'web',
|
||||
appVersion: getDesktopVersion() || WebAppVersion,
|
||||
collectUserIp: false,
|
||||
autoTrackSessions: false,
|
||||
releaseStage: isDev ? 'development' : undefined,
|
||||
enabledBreadcrumbTypes: ['error', 'log'],
|
||||
onError(event) {
|
||||
event.setUser(anonymousUserId);
|
||||
|
||||
/**
|
||||
* Redact any data that could be used to identify user,
|
||||
* such as file paths.
|
||||
*/
|
||||
if (isDesktopApplication()) {
|
||||
if (event.context) {
|
||||
event.context = `Desktop/${redactFilePath(event.context)}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.request.url?.includes('file:')) {
|
||||
event.request.url = redactFilePath(event.request.url);
|
||||
}
|
||||
|
||||
const originalStack = event.originalError.stack;
|
||||
if (
|
||||
typeof originalStack === 'string' &&
|
||||
originalStack.includes('file:')
|
||||
) {
|
||||
event.originalError.stack = originalStack
|
||||
.split('\n')
|
||||
.map((line) =>
|
||||
line.includes('file:') ? redactFilePath(line) : line
|
||||
)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
for (const error of event.errors) {
|
||||
for (const stackFrame of error.stacktrace) {
|
||||
if (stackFrame.file.includes('file:')) {
|
||||
stackFrame.file = redactFilePath(stackFrame.file);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (isDev) {
|
||||
SNLog.onError = console.error;
|
||||
} else {
|
||||
SNLog.onError = (error) => {
|
||||
Bugsnag.notify(error);
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to start Bugsnag.', error);
|
||||
SNLog.onError = console.error;
|
||||
}
|
||||
}
|
||||
|
||||
export function disableErrorReporting() {
|
||||
storage.remove(StorageKey.AnonymousUserId);
|
||||
storage.set(StorageKey.DisableErrorReporting, true);
|
||||
}
|
||||
|
||||
export function enableErrorReporting() {
|
||||
storage.set(StorageKey.DisableErrorReporting, false);
|
||||
}
|
||||
|
||||
export function errorReportingId() {
|
||||
return storage.get(StorageKey.AnonymousUserId);
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
export enum StorageKey {
|
||||
DisableErrorReporting = 'DisableErrorReporting',
|
||||
AnonymousUserId = 'AnonymousUserId',
|
||||
ShowBetaWarning = 'ShowBetaWarning',
|
||||
ShowNoAccountWarning = 'ShowNoAccountWarning',
|
||||
}
|
||||
|
||||
export type StorageValue = {
|
||||
[StorageKey.DisableErrorReporting]: boolean;
|
||||
[StorageKey.AnonymousUserId]: string;
|
||||
[StorageKey.ShowBetaWarning]: boolean;
|
||||
[StorageKey.ShowNoAccountWarning]: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export const storage = {
|
||||
get<K extends StorageKey>(key: K): StorageValue[K] | null {
|
||||
|
||||
Reference in New Issue
Block a user