1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Merge pull request #5560 from MetaMask/sentry-enhancements2

Sentry - various enhancements to help debugging (alternate)
This commit is contained in:
kumavis 2018-10-29 21:57:51 -04:00 committed by GitHub
commit 73eeeda215
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 186 additions and 81 deletions

View File

@ -23,7 +23,7 @@ const createStreamSink = require('./lib/createStreamSink')
const NotificationManager = require('./lib/notification-manager.js') const NotificationManager = require('./lib/notification-manager.js')
const MetamaskController = require('./metamask-controller') const MetamaskController = require('./metamask-controller')
const rawFirstTimeState = require('./first-time-state') const rawFirstTimeState = require('./first-time-state')
const setupRaven = require('./lib/setupRaven') const setupSentry = require('./lib/setupSentry')
const reportFailedTxToSentry = require('./lib/reportFailedTxToSentry') const reportFailedTxToSentry = require('./lib/reportFailedTxToSentry')
const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics') const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics')
const EdgeEncryptor = require('./edge-encryptor') const EdgeEncryptor = require('./edge-encryptor')
@ -50,7 +50,7 @@ global.METAMASK_NOTIFIER = notificationManager
// setup sentry error reporting // setup sentry error reporting
const release = platform.getVersion() const release = platform.getVersion()
const raven = setupRaven({ release }) const sentry = setupSentry({ release })
// browser check if it is Edge - https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser // browser check if it is Edge - https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
// Internet Explorer 6-11 // Internet Explorer 6-11
@ -195,14 +195,14 @@ async function loadStateFromPersistence () {
// we were able to recover (though it might be old) // we were able to recover (though it might be old)
versionedData = diskStoreState versionedData = diskStoreState
const vaultStructure = getObjStructure(versionedData) const vaultStructure = getObjStructure(versionedData)
raven.captureMessage('MetaMask - Empty vault found - recovered from diskStore', { sentry.captureMessage('MetaMask - Empty vault found - recovered from diskStore', {
// "extra" key is required by Sentry // "extra" key is required by Sentry
extra: { vaultStructure }, extra: { vaultStructure },
}) })
} else { } else {
// unable to recover, clear state // unable to recover, clear state
versionedData = migrator.generateInitialState(firstTimeState) versionedData = migrator.generateInitialState(firstTimeState)
raven.captureMessage('MetaMask - Empty vault found - unable to recover') sentry.captureMessage('MetaMask - Empty vault found - unable to recover')
} }
} }
@ -210,7 +210,7 @@ async function loadStateFromPersistence () {
migrator.on('error', (err) => { migrator.on('error', (err) => {
// get vault structure without secrets // get vault structure without secrets
const vaultStructure = getObjStructure(versionedData) const vaultStructure = getObjStructure(versionedData)
raven.captureException(err, { sentry.captureException(err, {
// "extra" key is required by Sentry // "extra" key is required by Sentry
extra: { vaultStructure }, extra: { vaultStructure },
}) })
@ -275,7 +275,7 @@ function setupController (initState, initLangCode) {
if (status !== 'failed') return if (status !== 'failed') return
const txMeta = controller.txController.txStateManager.getTx(txId) const txMeta = controller.txController.txStateManager.getTx(txId)
try { try {
reportFailedTxToSentry({ raven, txMeta }) reportFailedTxToSentry({ sentry, txMeta })
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }

View File

@ -83,8 +83,25 @@ class BlacklistController {
* *
*/ */
async updatePhishingList () { async updatePhishingList () {
const response = await fetch('https://api.infura.io/v2/blacklist') // make request
const phishing = await response.json() let response
try {
response = await fetch('https://api.infura.io/v2/blacklist')
} catch (err) {
log.error(new Error(`BlacklistController - failed to fetch blacklist:\n${err.stack}`))
return
}
// parse response
let rawResponse
let phishing
try {
const rawResponse = await response.text()
phishing = JSON.parse(rawResponse)
} catch (err) {
log.error(new Error(`BlacklistController - failed to parse blacklist:\n${rawResponse}`))
return
}
// update current blacklist
this.store.updateState({ phishing }) this.store.updateState({ phishing })
this._setupPhishingDetector(phishing) this._setupPhishingDetector(phishing)
return phishing return phishing
@ -97,9 +114,9 @@ class BlacklistController {
*/ */
scheduleUpdates () { scheduleUpdates () {
if (this._phishingUpdateIntervalRef) return if (this._phishingUpdateIntervalRef) return
this.updatePhishingList().catch(log.warn) this.updatePhishingList()
this._phishingUpdateIntervalRef = setInterval(() => { this._phishingUpdateIntervalRef = setInterval(() => {
this.updatePhishingList().catch(log.warn) this.updatePhishingList()
}, POLLING_INTERVAL) }, POLLING_INTERVAL)
} }

View File

@ -133,18 +133,40 @@ class CurrencyController {
try { try {
currentCurrency = this.getCurrentCurrency() currentCurrency = this.getCurrentCurrency()
nativeCurrency = this.getNativeCurrency() nativeCurrency = this.getNativeCurrency()
// select api
let apiUrl let apiUrl
if (nativeCurrency === 'ETH') { if (nativeCurrency === 'ETH') {
// ETH
apiUrl = `https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}` apiUrl = `https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}`
} else { } else {
// ETC
apiUrl = `https://min-api.cryptocompare.com/data/price?fsym=${nativeCurrency.toUpperCase()}&tsyms=${currentCurrency.toUpperCase()}` apiUrl = `https://min-api.cryptocompare.com/data/price?fsym=${nativeCurrency.toUpperCase()}&tsyms=${currentCurrency.toUpperCase()}`
} }
const response = await fetch(apiUrl) // attempt request
const parsedResponse = await response.json() let response
try {
response = await fetch(apiUrl)
} catch (err) {
log.error(new Error(`CurrencyController - Failed to request currency from Infura:\n${err.stack}`))
return
}
// parse response
let rawResponse
let parsedResponse
try {
rawResponse = await response.text()
parsedResponse = JSON.parse(rawResponse)
} catch (err) {
log.error(new Error(`CurrencyController - Failed to parse response "${rawResponse}"`))
return
}
// set conversion rate
if (nativeCurrency === 'ETH') { if (nativeCurrency === 'ETH') {
// ETH
this.setConversionRate(Number(parsedResponse.bid)) this.setConversionRate(Number(parsedResponse.bid))
this.setConversionDate(Number(parsedResponse.timestamp)) this.setConversionDate(Number(parsedResponse.timestamp))
} else { } else {
// ETC
if (parsedResponse[currentCurrency.toUpperCase()]) { if (parsedResponse[currentCurrency.toUpperCase()]) {
this.setConversionRate(Number(parsedResponse[currentCurrency.toUpperCase()])) this.setConversionRate(Number(parsedResponse[currentCurrency.toUpperCase()]))
this.setConversionDate(parseInt((new Date()).getTime() / 1000)) this.setConversionDate(parseInt((new Date()).getTime() / 1000))
@ -154,9 +176,13 @@ class CurrencyController {
} }
} }
} catch (err) { } catch (err) {
// reset current conversion rate
log.warn(`MetaMask - Failed to query currency conversion:`, nativeCurrency, currentCurrency, err) log.warn(`MetaMask - Failed to query currency conversion:`, nativeCurrency, currentCurrency, err)
this.setConversionRate(0) this.setConversionRate(0)
this.setConversionDate('N/A') this.setConversionDate('N/A')
// throw error
log.error(new Error(`CurrencyController - Failed to query rate for currency "${currentCurrency}":\n${err.stack}`))
return
} }
} }

View File

@ -7,10 +7,10 @@ module.exports = reportFailedTxToSentry
// for sending to sentry // for sending to sentry
// //
function reportFailedTxToSentry ({ raven, txMeta }) { function reportFailedTxToSentry ({ sentry, txMeta }) {
const errorMessage = 'Transaction Failed: ' + extractEthjsErrorMessage(txMeta.err.message) const errorMessage = 'Transaction Failed: ' + extractEthjsErrorMessage(txMeta.err.message)
raven.captureMessage(errorMessage, { sentry.captureMessage(errorMessage, {
// "extra" key is required by Sentry // "extra" key is required by Sentry
extra: txMeta, extra: { txMeta },
}) })
} }

View File

@ -17,9 +17,11 @@ function setupFetchDebugging() {
try { try {
return await originalFetch.call(window, ...args) return await originalFetch.call(window, ...args)
} catch (err) { } catch (err) {
console.warn('FetchDebugger - fetch encountered an Error', err) if (!err.stack) {
console.warn('FetchDebugger - fetch encountered an Error without a stack', err)
console.warn('FetchDebugger - overriding stack to point of original call') console.warn('FetchDebugger - overriding stack to point of original call')
err.stack = initialStack err.stack = initialStack
}
throw err throw err
} }
} }

View File

@ -1,58 +1,55 @@
const Raven = require('raven-js') const Sentry = require('@sentry/browser')
const METAMASK_DEBUG = process.env.METAMASK_DEBUG const METAMASK_DEBUG = process.env.METAMASK_DEBUG
const extractEthjsErrorMessage = require('./extractEthjsErrorMessage') const extractEthjsErrorMessage = require('./extractEthjsErrorMessage')
const PROD = 'https://3567c198f8a8412082d32655da2961d0@sentry.io/273505' const SENTRY_DSN_PROD = 'https://3567c198f8a8412082d32655da2961d0@sentry.io/273505'
const DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496' const SENTRY_DSN_DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496'
module.exports = setupRaven module.exports = setupSentry
// Setup raven / sentry remote error reporting // Setup sentry remote error reporting
function setupRaven (opts) { function setupSentry (opts) {
const { release } = opts const { release, getState } = opts
let ravenTarget let sentryTarget
// detect brave // detect brave
const isBrave = Boolean(window.chrome.ipcRenderer) const isBrave = Boolean(window.chrome.ipcRenderer)
if (METAMASK_DEBUG) { if (METAMASK_DEBUG) {
console.log('Setting up Sentry Remote Error Reporting: DEV') console.log('Setting up Sentry Remote Error Reporting: SENTRY_DSN_DEV')
ravenTarget = DEV sentryTarget = SENTRY_DSN_DEV
} else { } else {
console.log('Setting up Sentry Remote Error Reporting: PROD') console.log('Setting up Sentry Remote Error Reporting: SENTRY_DSN_PROD')
ravenTarget = PROD sentryTarget = SENTRY_DSN_PROD
} }
const client = Raven.config(ravenTarget, { Sentry.init({
dsn: sentryTarget,
debug: METAMASK_DEBUG,
release, release,
transport: function (opts) { beforeSend: (report) => rewriteReport(report),
opts.data.extra.isBrave = isBrave })
const report = opts.data
Sentry.configureScope(scope => {
scope.setExtra('isBrave', isBrave)
})
function rewriteReport(report) {
try { try {
// handle error-like non-error exceptions
rewriteErrorLikeExceptions(report)
// simplify certain complex error messages (e.g. Ethjs) // simplify certain complex error messages (e.g. Ethjs)
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)
} }
// make request normally return report
client._makeRequest(opts) }
},
})
client.install()
return Raven return Sentry
}
function rewriteErrorLikeExceptions (report) {
// handle errors that lost their error-ness in serialization (e.g. dnode)
rewriteErrorMessages(report, (errorMessage) => {
if (!errorMessage.includes('Non-Error exception captured with keys:')) return errorMessage
if (!(report.extra && report.extra.__serialized__ && report.extra.__serialized__.message)) return errorMessage
return `Non-Error Exception: ${report.extra.__serialized__.message}`
})
} }
function simplifyErrorMessages (report) { function simplifyErrorMessages (report) {

View File

@ -9,7 +9,7 @@ const extension = require('extensionizer')
const ExtensionPlatform = require('./platforms/extension') const ExtensionPlatform = require('./platforms/extension')
const NotificationManager = require('./lib/notification-manager') const NotificationManager = require('./lib/notification-manager')
const notificationManager = new NotificationManager() const notificationManager = new NotificationManager()
const setupRaven = require('./lib/setupRaven') const setupSentry = require('./lib/setupSentry')
const log = require('loglevel') const log = require('loglevel')
start().catch(log.error) start().catch(log.error)
@ -21,7 +21,17 @@ async function start () {
// setup sentry error reporting // setup sentry error reporting
const release = global.platform.getVersion() const release = global.platform.getVersion()
setupRaven({ release }) setupSentry({ release, getState })
// provide app state to append to error logs
function getState() {
// get app state
const state = window.getCleanAppState()
// remove unnecessary data
delete state.localeMessages
delete state.metamask.recentBlocks
// return state to be added to request
return state
}
// inject css // inject css
// const css = MetaMaskUiCss() // const css = MetaMaskUiCss()

63
package-lock.json generated
View File

@ -489,6 +489,16 @@
} }
} }
}, },
"@sentry/browser": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-4.2.3.tgz",
"integrity": "sha512-XvuIc1aclz4zuP2LamDuSy62/gl1mmNxzF+Ni5L8mcghBDq0urnOdkblVQgzqGoH8mc2QfjEctGa5djWxg3Bpg==",
"requires": {
"@sentry/core": "4.2.3",
"@sentry/types": "4.2.3",
"@sentry/utils": "4.2.3"
}
},
"@sentry/cli": { "@sentry/cli": {
"version": "1.30.3", "version": "1.30.3",
"resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.30.3.tgz", "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.30.3.tgz",
@ -531,6 +541,48 @@
} }
} }
}, },
"@sentry/core": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-4.2.3.tgz",
"integrity": "sha512-xo5rvksftnaEdnKbdokyfuqgMnuqw1DFp0lDboIFHlEBcQde/AdThEgLujJWmbVNI3Cg7g8/HvP65f7QBjKfOw==",
"requires": {
"@sentry/hub": "4.2.3",
"@sentry/minimal": "4.2.3",
"@sentry/types": "4.2.3",
"@sentry/utils": "4.2.3"
}
},
"@sentry/hub": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-4.2.3.tgz",
"integrity": "sha512-7Jc/wz3vybYm1RX2wk/4zAQS8fo3uxvXYBkRfpm3OmnGgTlwDEmJwtegeGWFqufxLl85brZ19V1KAdulmO206A==",
"requires": {
"@sentry/types": "4.2.3",
"@sentry/utils": "4.2.3"
}
},
"@sentry/minimal": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-4.2.3.tgz",
"integrity": "sha512-Hus7LUeJDGsYpT2RTe6bUjG7mHG9uQoyDmW6pYUMN2dhD+cP5cPoTIXO4yxokwgAeDa+GH2/UXoASWc4s2eA2w==",
"requires": {
"@sentry/hub": "4.2.3",
"@sentry/types": "4.2.3"
}
},
"@sentry/types": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-4.2.3.tgz",
"integrity": "sha512-Z7laXlLtLSEXcKzgcD2caWPMTM8sAvR86rssYM5uYb3azC5PO0aAvuhjokkdv1+Ke1Bg7lkaNZamiCSyaH/9xg=="
},
"@sentry/utils": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-4.2.3.tgz",
"integrity": "sha512-D6+M1081wCwGp8adWV3KFOxrxFmVyjMJ45x6/TnYvXdgDyc+t28oil21FHeKhwjld9eMqgQ5Tf1OOvos1MNBQg==",
"requires": {
"@sentry/types": "4.2.3"
}
},
"@sinonjs/formatio": { "@sinonjs/formatio": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz",
@ -6131,9 +6183,9 @@
} }
}, },
"clone": { "clone": {
"version": "2.1.1", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
"integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=" "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18="
}, },
"clone-buffer": { "clone-buffer": {
"version": "1.0.0", "version": "1.0.0",
@ -27458,11 +27510,6 @@
"eve-raphael": "0.5.0" "eve-raphael": "0.5.0"
} }
}, },
"raven-js": {
"version": "3.24.2",
"resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.24.2.tgz",
"integrity": "sha512-Dy/FHDxuo5pXywVf8Nrs5utB2juMATpkxWGqHjVbpFD3m8CaWYLvkB5SEXjWFUZ5GvUsrBVVQ+Dfcp0x6Z7SOg=="
},
"raw-body": { "raw-body": {
"version": "2.3.2", "version": "2.3.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",

View File

@ -82,6 +82,7 @@
}, },
"dependencies": { "dependencies": {
"@material-ui/core": "1.0.0", "@material-ui/core": "1.0.0",
"@sentry/browser": "^4.1.1",
"@zxing/library": "^0.8.0", "@zxing/library": "^0.8.0",
"abi-decoder": "^1.0.9", "abi-decoder": "^1.0.9",
"asmcrypto.js": "0.22.0", "asmcrypto.js": "0.22.0",
@ -97,7 +98,7 @@
"browserify-derequire": "^0.9.4", "browserify-derequire": "^0.9.4",
"browserify-unibabel": "^3.0.0", "browserify-unibabel": "^3.0.0",
"classnames": "^2.2.5", "classnames": "^2.2.5",
"clone": "^2.1.1", "clone": "^2.1.2",
"copy-to-clipboard": "^3.0.8", "copy-to-clipboard": "^3.0.8",
"css-loader": "^0.28.11", "css-loader": "^0.28.11",
"currency-formatter": "^1.4.2", "currency-formatter": "^1.4.2",
@ -186,7 +187,6 @@
"pumpify": "^1.3.4", "pumpify": "^1.3.4",
"qrcode-npm": "0.0.3", "qrcode-npm": "0.0.3",
"ramda": "^0.24.1", "ramda": "^0.24.1",
"raven-js": "^3.24.2",
"react": "^15.6.2", "react": "^15.6.2",
"react-addons-css-transition-group": "^15.6.0", "react-addons-css-transition-group": "^15.6.0",
"react-dom": "^15.6.2", "react-dom": "^15.6.2",

View File

@ -1,3 +1,4 @@
const clone = require('clone')
const extend = require('xtend') const extend = require('xtend')
const copyToClipboard = require('copy-to-clipboard') const copyToClipboard = require('copy-to-clipboard')
@ -52,19 +53,24 @@ function rootReducer (state, action) {
return state return state
} }
window.getCleanAppState = function () {
const state = clone(window.METAMASK_CACHED_LOG_STATE)
// append additional information
state.version = global.platform.getVersion()
state.browser = window.navigator.userAgent
// ensure seedWords are not included
if (state.metamask) delete state.metamask.seedWords
if (state.appState.currentView) delete state.appState.currentView.seedWords
return state
}
window.logStateString = function (cb) { window.logStateString = function (cb) {
const state = window.METAMASK_CACHED_LOG_STATE const state = window.getCleanAppState()
const version = global.platform.getVersion() global.platform.getPlatformInfo((err, platform) => {
const browser = window.navigator.userAgent if (err) return cb(err)
return global.platform.getPlatformInfo((err, platform) => {
if (err) {
return cb(err)
}
state.version = version
state.platform = platform state.platform = platform
state.browser = browser
const stateString = JSON.stringify(state, removeSeedWords, 2) const stateString = JSON.stringify(state, removeSeedWords, 2)
return cb(null, stateString) cb(null, stateString)
}) })
} }