Restore state snapshot for Sentry errors (#9028)

The state snapshot that was attached to Sentry errors was removed
recently in #8794 because it had become too large. The snapshot has
now been restored and reduced in size.

A utility function has been written to reduce the state object to just
the requested properties. This seemed safer than filtering out state
that is known to be large or to contain identifiable information.
This is not a great solution, as now knowledge about the state shape
resides in this large constant, but it will suffice for now. I am
hopeful that we can decorate our controllers with this metadata in the
future instead, as part of the upcoming background controller refactor.

A separate `getSentryState` global function has been added to get the
reduced state, so that the old `getCleanAppState` function that we used
to use could remain unchanged. It's still useful to get that full state
copy while debugging, and in e2e tests.
feature/default_network_editable
Mark Stacey 4 years ago committed by GitHub
parent f02478e026
commit e8b33fb7c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 68
      app/scripts/lib/setupSentry.js
  2. 5
      app/scripts/ui.js
  3. 37
      ui/index.js

@ -8,7 +8,68 @@ const METAMASK_ENVIRONMENT = process.env.METAMASK_ENVIRONMENT
const SENTRY_DSN_PROD = 'https://3567c198f8a8412082d32655da2961d0@sentry.io/273505' const SENTRY_DSN_PROD = 'https://3567c198f8a8412082d32655da2961d0@sentry.io/273505'
const SENTRY_DSN_DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496' const SENTRY_DSN_DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496'
export default function setupSentry ({ release }) { // This describes the subset of Redux state attached to errors sent to Sentry
// These properties have some potential to be useful for debugging, and they do
// not contain any identifiable information.
export const SENTRY_STATE = {
gas: true,
history: true,
metamask: {
alertEnabledness: true,
completedOnboarding: true,
connectedStatusPopoverHasBeenShown: true,
conversionDate: true,
conversionRate: true,
currentBlockGasLimit: true,
currentCurrency: true,
currentLocale: true,
customNonceValue: true,
defaultHomeActiveTabName: true,
featureFlags: true,
firstTimeFlowType: true,
forgottenPassword: true,
incomingTxLastFetchedBlocksByNetwork: true,
ipfsGateway: true,
isAccountMenuOpen: true,
isInitialized: true,
isUnlocked: true,
metaMetricsId: true,
metaMetricsSendCount: true,
nativeCurrency: true,
network: true,
nextNonce: true,
participateInMetaMetrics: true,
preferences: true,
provider: {
nickname: true,
ticker: true,
type: true,
},
seedPhraseBackedUp: true,
settings: {
chainId: true,
ticker: true,
nickname: true,
},
showRestorePrompt: true,
threeBoxDisabled: true,
threeBoxLastUpdated: true,
threeBoxSynced: true,
threeBoxSyncingAllowed: true,
unapprovedDecryptMsgCount: true,
unapprovedEncryptionPublicKeyMsgCount: true,
unapprovedMsgCount: true,
unapprovedPersonalMsgCount: true,
unapprovedTypedMessagesCount: true,
useBlockie: true,
useNonceField: true,
usePhishDetect: true,
welcomeScreenSeen: true,
},
unconnectedAccount: true,
}
export default function setupSentry ({ release, getState }) {
let sentryTarget let sentryTarget
if (METAMASK_DEBUG || process.env.IN_TEST) { if (METAMASK_DEBUG || process.env.IN_TEST) {
@ -37,6 +98,11 @@ export default function setupSentry ({ release }) {
simplifyErrorMessages(report) simplifyErrorMessages(report)
// modify report urls // modify report urls
rewriteReportUrls(report) rewriteReportUrls(report)
// append app state
if (getState) {
const appState = getState()
report.extra.appState = appState
}
} catch (err) { } catch (err) {
console.warn(err) console.warn(err)
} }

@ -36,7 +36,10 @@ async function start () {
// setup sentry error reporting // setup sentry error reporting
const release = global.platform.getVersion() const release = global.platform.getVersion()
setupSentry({ release }) setupSentry({
release,
getState: () => window.getSentryState?.() || {},
})
// identify window type (popup, notification) // identify window type (popup, notification)
const windowType = getEnvironmentType() const windowType = getEnvironmentType()

@ -9,6 +9,7 @@ import configureStore from './app/store/store'
import txHelper from './lib/tx-helper' import txHelper from './lib/tx-helper'
import { getEnvironmentType } from '../app/scripts/lib/util' import { getEnvironmentType } from '../app/scripts/lib/util'
import { ALERT_TYPES } from '../app/scripts/controllers/alert' import { ALERT_TYPES } from '../app/scripts/controllers/alert'
import { SENTRY_STATE } from '../app/scripts/lib/setupSentry'
import { ENVIRONMENT_TYPE_POPUP } from '../app/scripts/lib/enums' import { ENVIRONMENT_TYPE_POPUP } from '../app/scripts/lib/enums'
import { fetchLocale, loadRelativeTimeFormatLocaleData } from './app/helpers/utils/i18n-helper' import { fetchLocale, loadRelativeTimeFormatLocaleData } from './app/helpers/utils/i18n-helper'
import switchDirection from './app/helpers/utils/switch-direction' import switchDirection from './app/helpers/utils/switch-direction'
@ -138,6 +139,33 @@ async function startApp (metamaskState, backgroundConnection, opts) {
return store return store
} }
/**
* Return a "masked" copy of the given object.
*
* The returned object includes only the properties present in the mask. The
* mask is an object that mirrors the structure of the given object, except
* the only values are `true` or a sub-mask. `true` implies the property
* should be included, and a sub-mask implies the property should be further
* masked according to that sub-mask.
*
* @param {Object} object - The object to mask
* @param {Object<Object|boolean>} mask - The mask to apply to the object
*/
function maskObject (object, mask) {
return Object.keys(object)
.reduce(
(state, key) => {
if (mask[key] === true) {
state[key] = object[key]
} else if (mask[key]) {
state[key] = maskObject(object[key], mask[key])
}
return state
},
{},
)
}
function setupDebuggingHelpers (store) { function setupDebuggingHelpers (store) {
window.getCleanAppState = function () { window.getCleanAppState = function () {
const state = clone(store.getState()) const state = clone(store.getState())
@ -145,6 +173,15 @@ function setupDebuggingHelpers (store) {
state.browser = window.navigator.userAgent state.browser = window.navigator.userAgent
return state return state
} }
window.getSentryState = function () {
const fullState = store.getState()
const debugState = maskObject(fullState, SENTRY_STATE)
return {
browser: window.navigator.userAgent,
store: debugState,
version: global.platform.getVersion(),
}
}
} }
window.logStateString = function (cb) { window.logStateString = function (cb) {

Loading…
Cancel
Save