Gekidou Sentry Installation (#6726)

* Sentry - Clean Install

* Update config.yml

* update CI to install Sentry CLI

* update CI to install Sentry CLI

* Squashed commit of the following:

commit e1996e59de
Merge: 87cc8d6f2 2e8352f3e
Author: Avinash Lingaloo <avinashlng1080@gmail.com>
Date:   Fri Nov 4 20:59:29 2022 +0400

    Merge branch 'android-pr-sentry-clean' of https://github.com/mattermost/mattermost-mobile into android-pr-sentry-clean

commit 2e8352f3e1
Author: Elias Nahum <nahumhbl@gmail.com>
Date:   Fri Nov 4 15:48:24 2022 +0200

    update CI to install Sentry CLI

commit 87cc8d6f2b
Author: Elias Nahum <nahumhbl@gmail.com>
Date:   Fri Nov 4 15:48:24 2022 +0200

    update CI to install Sentry CLI

commit 8dca885a02
Author: Avinash Lingaloo <avinashlng1080@gmail.com>
Date:   Fri Nov 4 16:48:47 2022 +0400

    Update config.yml

commit 684bbb4aef
Author: Mylon Suren <23694620+mylonsuren@users.noreply.github.com>
Date:   Thu Nov 3 11:37:58 2022 -0400

    Remove down arrow next to team name and make team name unclickable (#6715)

commit 88ff8fac30
Author: Jason Frerich <jason.frerich@mattermost.com>
Date:   Wed Nov 2 19:35:23 2022 -0500

    [Bug] Emit boolean with "of" operator (#6729)

commit debcc99480
Author: Jason Frerich <jason.frerich@mattermost.com>
Date:   Wed Nov 2 12:15:54 2022 -0500

    [Gekidou MM-48006] Show keyboard when select a modifier (#6714)

* Delete @sentry+react-native+4.6.1.patch

* correction from PR review

* Add logError to DatabaseManager

* removes sentry context from android build job

* removes team+channel data

* shift active server listener to home/index

* Revert "shift active server listener to home/index"

This reverts commit 75e26faadd.

* refactor after PR Review

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
Avinash Lingaloo
2022-11-11 22:48:38 +04:00
committed by GitHub
parent 25ddc894f2
commit 987d024708
7 changed files with 104 additions and 121 deletions

View File

@@ -111,7 +111,9 @@ commands:
key: v2-npm-{{ checksum "package.json" }}-{{ arch }}
- run:
name: Getting JavaScript dependencies
command: NODE_ENV=development npm ci --ignore-scripts
command: |
NODE_ENV=development npm ci --ignore-scripts
node node_modules/\@sentry/cli/scripts/install.js
- save_cache:
name: Save npm cache
key: v2-npm-{{ checksum "package.json" }}-{{ arch }}

View File

@@ -110,7 +110,7 @@ class DatabaseManager {
return this.appDatabase;
} catch (e) {
// TODO : report to sentry? Show something on the UI ?
logError('Unable to create the App Database!!', e);
}
return undefined;
@@ -164,8 +164,6 @@ class DatabaseManager {
return serverDatabase;
} catch (e) {
// TODO : report to sentry? Show something on the UI ?
logError('Error initializing database', e);
}
}
@@ -249,7 +247,7 @@ class DatabaseManager {
}
}
} catch (e) {
// TODO : report to sentry? Show something on the UI ?
logError('Error adding server to App database', e);
}
};

View File

@@ -12,10 +12,12 @@ import {Edge, SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-cont
import FreezeScreen from '@components/freeze_screen';
import TeamSidebar from '@components/team_sidebar';
import {Navigation as NavigationConstants, Screens} from '@constants';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
import {resetToTeams} from '@screens/navigation';
import NavigationStore from '@store/navigation_store';
import {addSentryContext} from '@utils/sentry';
import AdditionalTabletView from './additional_tablet_view';
import CategoriesList from './categories_list';
@@ -50,6 +52,7 @@ const ChannelListScreen = (props: ChannelProps) => {
const isFocused = useIsFocused();
const navigation = useNavigation();
const insets = useSafeAreaInsets();
const serverUrl = useServerUrl();
const params = route.params as {direction: string};
const canAddOtherServers = managedConfig?.allowOtherServers !== 'false';
@@ -111,6 +114,10 @@ const ChannelListScreen = (props: ChannelProps) => {
return () => back.remove();
}, [handleBackPress]);
useEffect(() => {
addSentryContext(serverUrl);
}, [serverUrl]);
return (
<FreezeScreen freeze={!isFocused}>
{<Animated.View style={top}/>}

View File

@@ -1,22 +1,39 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import keyMirror from '@utils/key_mirror';
const SentryLevels = keyMirror({debug: null, info: null, warning: null, error: null});
export function logError(...args: any[]) {
// eslint-disable-next-line no-console
console.error(...args);
addBreadcrumb(SentryLevels.error, ...args);
}
export function logWarning(...args: any[]) {
// eslint-disable-next-line no-console
console.warn(...args);
addBreadcrumb(SentryLevels.warning, ...args);
}
export function logInfo(...args: any[]) {
// eslint-disable-next-line no-console
console.log(...args);
addBreadcrumb(SentryLevels.info, ...args);
}
export function logDebug(...args: any[]) {
// eslint-disable-next-line no-console
console.debug(...args);
addBreadcrumb(SentryLevels.debug, ...args);
}
const addBreadcrumb = (logLevel: keyof typeof SentryLevels, ...args: any[]) => {
const Sentry = require('@sentry/react-native');
Sentry.addBreadcrumb({
level: logLevel,
message: args.join(','),
type: 'console-log',
});
};

View File

@@ -1,13 +1,18 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Database} from '@nozbe/watermelondb';
import {Breadcrumb} from '@sentry/types';
import {Platform} from 'react-native';
import {Navigation} from 'react-native-navigation';
import Config from '@assets/config.json';
import DatabaseManager from '@database/manager';
import {getConfig} from '@queries/servers/system';
import {getCurrentUser} from '@queries/servers/user';
import {ClientError} from './client_error';
import {logError, logWarning} from './log';
import {logWarning} from './log';
export const BREADCRUMB_UNCAUGHT_APP_ERROR = 'uncaught-app-error';
export const BREADCRUMB_UNCAUGHT_NON_ERROR = 'uncaught-non-error';
@@ -17,6 +22,7 @@ export const LOGGER_JAVASCRIPT_WARNING = 'javascript_warning';
export const LOGGER_NATIVE = 'native';
let Sentry: any;
export function initializeSentry() {
if (!Config.SentryEnabled) {
return;
@@ -33,7 +39,21 @@ export function initializeSentry() {
return;
}
Sentry.init({dsn, ...Config.SentryOptions});
Sentry.init({
dsn,
tracesSampleRate: 0.2,
integrations: [
new Sentry.ReactNativeTracing({
// Pass instrumentation to be used as `routingInstrumentation`
routingInstrumentation: new Sentry.ReactNativeNavigationInstrumentation(
Navigation,
),
}),
],
sendDefaultPii: false,
...Config.SentryOptions,
});
}
function getDsn() {
@@ -55,12 +75,7 @@ export function captureException(error: Error | string, logger: string) {
logWarning('captureException called with missing arguments', error, logger);
return;
}
// TODO: Get current server config and other relevant data
capture(() => {
Sentry.captureException(error, {logger});
});
Sentry.captureException(error, {logger});
}
export function captureJSException(error: Error | ClientError, isFatal: boolean) {
@@ -120,128 +135,70 @@ function captureClientErrorAsBreadcrumb(error: ClientError, isFatal: boolean) {
}
}
// 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) {
logWarning('Capturing with Sentry at ' + getDsn() + '...');
captureFunc();
} else {
logWarning('No user context, skipping capture');
}
} catch (e) {
// Don't want this to get into an infinite loop again...
logError('Exception occurred while sending to Sentry');
logError(e);
}
}
function getUserContext() {
// TODO: Get current user data from active database
const getUserContext = async (database: Database) => {
const currentUser = {
id: 'currentUserId',
locale: 'en',
roles: 'multi-server-test-role',
};
if (!currentUser) {
return null;
}
const user = await getCurrentUser(database);
return {
userID: currentUser.id, // This can be changed to id after we upgrade to Sentry >= 0.14.10,
userID: user?.id ?? currentUser.id,
email: '',
username: '',
extra: {
locale: currentUser.locale,
roles: currentUser.roles,
},
locale: user?.locale ?? currentUser.locale,
roles: user?.roles ?? currentUser.roles,
};
}
};
function getExtraContext() {
const context = {};
const getExtraContext = async (database: Database) => {
const context = {
config: {},
currentChannel: {},
currentTeam: {},
};
// 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,
// };
// }
const config = await getConfig(database);
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;
const getBuildTags = async (database: Database) => {
const tags = {
serverBuildHash: '',
serverBuildNumber: '',
};
// TODO: Add context based on the active database
// const config = getConfig(state);
// if (config) {
// tags = {
// serverBuildHash: config.BuildHash,
// serverBuildNumber: config.BuildNumber,
// };
// }
const config = await getConfig(database);
if (config) {
tags.serverBuildHash = config.BuildHash;
tags.serverBuildNumber = config.BuildNumber;
}
return tags;
}
};
export const addSentryContext = async (serverUrl: string) => {
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
if (database) {
const userContext = await getUserContext(database);
Sentry.setContext('User-Information', userContext);
const buildContext = await getBuildTags(database);
Sentry.setContext('App-Build Information', buildContext);
const extraContext = await getExtraContext(database);
Sentry.setContext('Server-Information', extraContext);
}
};

View File

@@ -12,7 +12,8 @@ if [[ "${SENTRY_ENABLED}" = "true" ]]; then
./makeSentryProperties.sh
export SENTRY_PROPERTIES=sentry.properties
../node_modules/@sentry/cli/bin/sentry-cli react-native xcode ./react-native-xcode.sh
../node_modules/@sentry/cli/bin/sentry-cli react-native xcode \
../node_modules/react-native/scripts/react-native-xcode.sh
else
echo "Sentry native integration is not enabled"
../node_modules/react-native/scripts/react-native-xcode.sh

View File

@@ -6,7 +6,8 @@ if [[ "${SENTRY_ENABLED}" = "true" ]]; then
./makeSentryProperties.sh
export SENTRY_PROPERTIES=sentry.properties
../node_modules/@sentry/cli/bin/sentry-cli upload-dsym
../node_modules/@sentry/cli/bin/sentry-cli upload-dif "$DWARF_DSYM_FOLDER_PATH"
else
echo "Not uploading debugging symbols to Sentry because Sentry is disabled"
fi