forked from Ivasoft/mattermost-mobile
249 lines
6.7 KiB
TypeScript
249 lines
6.7 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import {Breadcrumb, Severity} from '@sentry/types';
|
|
import {Platform} from 'react-native';
|
|
|
|
import Config from '@assets/config.json';
|
|
|
|
import {ClientError} from './client_error';
|
|
|
|
export const BREADCRUMB_UNCAUGHT_APP_ERROR = 'uncaught-app-error';
|
|
export const BREADCRUMB_UNCAUGHT_NON_ERROR = 'uncaught-non-error';
|
|
export const LOGGER_EXTENSION = 'extension';
|
|
export const LOGGER_JAVASCRIPT = 'javascript';
|
|
export const LOGGER_JAVASCRIPT_WARNING = 'javascript_warning';
|
|
export const LOGGER_NATIVE = 'native';
|
|
|
|
let Sentry: any;
|
|
export function initializeSentry() {
|
|
if (!Config.SentryEnabled) {
|
|
return;
|
|
}
|
|
|
|
if (!Sentry) {
|
|
Sentry = require('@sentry/react-native');
|
|
}
|
|
|
|
const dsn = getDsn();
|
|
|
|
if (!dsn) {
|
|
console.warn('Sentry is enabled, but not configured on this platform'); // eslint-disable-line no-console
|
|
return;
|
|
}
|
|
|
|
Sentry.init({dsn, ...Config.SentryOptions});
|
|
}
|
|
|
|
function getDsn() {
|
|
if (Platform.OS === 'android') {
|
|
return Config.SentryDsnAndroid;
|
|
} else if (Platform.OS === 'ios') {
|
|
return Config.SentryDsnIos;
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
export function captureException(error: Error | string, logger: string) {
|
|
if (!Config.SentryEnabled) {
|
|
return;
|
|
}
|
|
|
|
if (!error || !logger) {
|
|
// eslint-disable-next-line no-console
|
|
console.warn('captureException called with missing arguments', error, logger);
|
|
return;
|
|
}
|
|
|
|
// TODO: Get current server config and other relevant data
|
|
|
|
capture(() => {
|
|
Sentry.captureException(error, {logger});
|
|
});
|
|
}
|
|
|
|
export function captureJSException(error: Error | ClientError, isFatal: boolean) {
|
|
if (!Config.SentryEnabled) {
|
|
return;
|
|
}
|
|
|
|
if (!error) {
|
|
// eslint-disable-next-line no-console
|
|
console.warn('captureJSException called with missing arguments', error);
|
|
return;
|
|
}
|
|
|
|
if (error instanceof ClientError) {
|
|
captureClientErrorAsBreadcrumb(error, isFatal);
|
|
} else {
|
|
captureException(error, LOGGER_JAVASCRIPT);
|
|
}
|
|
}
|
|
|
|
function captureClientErrorAsBreadcrumb(error: ClientError, isFatal: boolean) {
|
|
const isAppError = Boolean(error.server_error_id);
|
|
const breadcrumb: Breadcrumb = {
|
|
category: isAppError ? BREADCRUMB_UNCAUGHT_APP_ERROR : BREADCRUMB_UNCAUGHT_NON_ERROR,
|
|
data: {
|
|
isFatal: String(isFatal),
|
|
},
|
|
level: Severity.Warning,
|
|
};
|
|
|
|
if (error.intl?.defaultMessage) {
|
|
breadcrumb.message = error.intl.defaultMessage;
|
|
} else {
|
|
breadcrumb.message = error.message;
|
|
}
|
|
|
|
if (breadcrumb.data) {
|
|
if (error.server_error_id) {
|
|
breadcrumb.data.server_error_id = error.server_error_id;
|
|
}
|
|
|
|
if (error.status_code) {
|
|
breadcrumb.data.status_code = error.status_code;
|
|
}
|
|
|
|
const match = (/^(?:https?:\/\/)[^/]+(\/.*)$/).exec(error.url);
|
|
|
|
if (match && match.length >= 2) {
|
|
breadcrumb.data.url = match[1];
|
|
}
|
|
}
|
|
|
|
try {
|
|
Sentry.addBreadcrumb(breadcrumb);
|
|
} catch (e) {
|
|
// Do nothing since this is only here to make sure we don't crash when handling an exception
|
|
console.warn('Failed to capture breadcrumb of non-error', e); // eslint-disable-line no-console
|
|
}
|
|
}
|
|
|
|
// Wrapper function to any calls to Sentry so that we can gather any necessary extra data
|
|
// before sending.
|
|
function capture(captureFunc: () => void, config?: ClientConfig) {
|
|
if (config?.EnableDiagnostics !== 'true') {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
let hasUserContext = false;
|
|
const userContext = getUserContext();
|
|
if (userContext) {
|
|
hasUserContext = true;
|
|
Sentry.setUserContext(userContext);
|
|
}
|
|
|
|
const extraContext = getExtraContext();
|
|
if (Object.keys(extraContext).length) {
|
|
Sentry.setExtraContext(extraContext);
|
|
}
|
|
|
|
const buildTags = getBuildTags();
|
|
if (buildTags) {
|
|
Sentry.setTagsContext(buildTags);
|
|
}
|
|
|
|
if (hasUserContext) {
|
|
console.warn('Capturing with Sentry at ' + getDsn() + '...'); // eslint-disable-line no-console
|
|
|
|
captureFunc();
|
|
} else {
|
|
console.warn('No user context, skipping capture'); // eslint-disable-line no-console
|
|
}
|
|
} catch (e) {
|
|
// Don't want this to get into an infinite loop again...
|
|
console.warn('Exception occured while sending to Sentry'); // eslint-disable-line no-console
|
|
console.warn(e); // eslint-disable-line no-console
|
|
}
|
|
}
|
|
|
|
function getUserContext() {
|
|
// TODO: Get current user data from active database
|
|
const currentUser = {
|
|
id: 'currentUserId',
|
|
locale: 'en',
|
|
roles: 'multi-server-test-role',
|
|
};
|
|
|
|
if (!currentUser) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
userID: currentUser.id, // This can be changed to id after we upgrade to Sentry >= 0.14.10,
|
|
email: '',
|
|
username: '',
|
|
extra: {
|
|
locale: currentUser.locale,
|
|
roles: currentUser.roles,
|
|
},
|
|
};
|
|
}
|
|
|
|
function getExtraContext() {
|
|
const context = {};
|
|
|
|
// TODO: Add context based on the active database
|
|
|
|
// const currentTeam = getCurrentTeam(state);
|
|
// if (currentTeam) {
|
|
// context.currentTeam = {
|
|
// id: currentTeam.id,
|
|
// };
|
|
// }
|
|
|
|
// const currentTeamMember = getCurrentTeamMembership(state);
|
|
// if (currentTeamMember) {
|
|
// context.currentTeamMember = {
|
|
// roles: currentTeamMember.roles,
|
|
// };
|
|
// }
|
|
|
|
// const currentChannel = getCurrentChannel(state);
|
|
// if (currentChannel) {
|
|
// context.currentChannel = {
|
|
// id: currentChannel.id,
|
|
// type: currentChannel.type,
|
|
// };
|
|
// }
|
|
|
|
// const currentChannelMember = getMyCurrentChannelMembership(state);
|
|
// if (currentChannelMember) {
|
|
// context.currentChannelMember = {
|
|
// roles: currentChannelMember.roles,
|
|
// };
|
|
// }
|
|
|
|
// const config = getConfig(state);
|
|
// if (config) {
|
|
// context.config = {
|
|
// BuildDate: config.BuildDate,
|
|
// BuildEnterpriseReady: config.BuildEnterpriseReady,
|
|
// BuildHash: config.BuildHash,
|
|
// BuildHashEnterprise: config.BuildHashEnterprise,
|
|
// BuildNumber: config.BuildNumber,
|
|
// };
|
|
// }
|
|
|
|
return context;
|
|
}
|
|
|
|
function getBuildTags() {
|
|
let tags;
|
|
|
|
// TODO: Add context based on the active database
|
|
|
|
// const config = getConfig(state);
|
|
// if (config) {
|
|
// tags = {
|
|
// serverBuildHash: config.BuildHash,
|
|
// serverBuildNumber: config.BuildNumber,
|
|
// };
|
|
// }
|
|
|
|
return tags;
|
|
}
|