Skip to content

Commit

Permalink
Merge pull request #5560 from MetaMask/sentry-enhancements2
Browse files Browse the repository at this point in the history
Sentry - various enhancements to help debugging (alternate)
  • Loading branch information
kumavis authored Oct 30, 2018
2 parents 310229d + 7ae5a23 commit 73eeeda
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 80 deletions.
12 changes: 6 additions & 6 deletions app/scripts/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const createStreamSink = require('./lib/createStreamSink')
const NotificationManager = require('./lib/notification-manager.js')
const MetamaskController = require('./metamask-controller')
const rawFirstTimeState = require('./first-time-state')
const setupRaven = require('./lib/setupRaven')
const setupSentry = require('./lib/setupSentry')
const reportFailedTxToSentry = require('./lib/reportFailedTxToSentry')
const setupMetamaskMeshMetrics = require('./lib/setupMetamaskMeshMetrics')
const EdgeEncryptor = require('./edge-encryptor')
Expand All @@ -50,7 +50,7 @@ global.METAMASK_NOTIFIER = notificationManager

// setup sentry error reporting
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
// Internet Explorer 6-11
Expand Down Expand Up @@ -195,22 +195,22 @@ async function loadStateFromPersistence () {
// we were able to recover (though it might be old)
versionedData = diskStoreState
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: { vaultStructure },
})
} else {
// unable to recover, clear state
versionedData = migrator.generateInitialState(firstTimeState)
raven.captureMessage('MetaMask - Empty vault found - unable to recover')
sentry.captureMessage('MetaMask - Empty vault found - unable to recover')
}
}

// report migration errors to sentry
migrator.on('error', (err) => {
// get vault structure without secrets
const vaultStructure = getObjStructure(versionedData)
raven.captureException(err, {
sentry.captureException(err, {
// "extra" key is required by Sentry
extra: { vaultStructure },
})
Expand Down Expand Up @@ -275,7 +275,7 @@ function setupController (initState, initLangCode) {
if (status !== 'failed') return
const txMeta = controller.txController.txStateManager.getTx(txId)
try {
reportFailedTxToSentry({ raven, txMeta })
reportFailedTxToSentry({ sentry, txMeta })
} catch (e) {
console.error(e)
}
Expand Down
25 changes: 21 additions & 4 deletions app/scripts/controllers/blacklist.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,25 @@ class BlacklistController {
*
*/
async updatePhishingList () {
const response = await fetch('https://api.infura.io/v2/blacklist')
const phishing = await response.json()
// make request
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._setupPhishingDetector(phishing)
return phishing
Expand All @@ -97,9 +114,9 @@ class BlacklistController {
*/
scheduleUpdates () {
if (this._phishingUpdateIntervalRef) return
this.updatePhishingList().catch(log.warn)
this.updatePhishingList()
this._phishingUpdateIntervalRef = setInterval(() => {
this.updatePhishingList().catch(log.warn)
this.updatePhishingList()
}, POLLING_INTERVAL)
}

Expand Down
30 changes: 28 additions & 2 deletions app/scripts/controllers/currency.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,18 +133,40 @@ class CurrencyController {
try {
currentCurrency = this.getCurrentCurrency()
nativeCurrency = this.getNativeCurrency()
// select api
let apiUrl
if (nativeCurrency === 'ETH') {
// ETH
apiUrl = `https://api.infura.io/v1/ticker/eth${currentCurrency.toLowerCase()}`
} else {
// ETC
apiUrl = `https://min-api.cryptocompare.com/data/price?fsym=${nativeCurrency.toUpperCase()}&tsyms=${currentCurrency.toUpperCase()}`
}
const response = await fetch(apiUrl)
const parsedResponse = await response.json()
// attempt request
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') {
// ETH
this.setConversionRate(Number(parsedResponse.bid))
this.setConversionDate(Number(parsedResponse.timestamp))
} else {
// ETC
if (parsedResponse[currentCurrency.toUpperCase()]) {
this.setConversionRate(Number(parsedResponse[currentCurrency.toUpperCase()]))
this.setConversionDate(parseInt((new Date()).getTime() / 1000))
Expand All @@ -154,9 +176,13 @@ class CurrencyController {
}
}
} catch (err) {
// reset current conversion rate
log.warn(`MetaMask - Failed to query currency conversion:`, nativeCurrency, currentCurrency, err)
this.setConversionRate(0)
this.setConversionDate('N/A')
// throw error
log.error(new Error(`CurrencyController - Failed to query rate for currency "${currentCurrency}":\n${err.stack}`))
return
}
}

Expand Down
6 changes: 3 additions & 3 deletions app/scripts/lib/reportFailedTxToSentry.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ module.exports = reportFailedTxToSentry
// for sending to sentry
//

function reportFailedTxToSentry ({ raven, txMeta }) {
function reportFailedTxToSentry ({ sentry, txMeta }) {
const errorMessage = 'Transaction Failed: ' + extractEthjsErrorMessage(txMeta.err.message)
raven.captureMessage(errorMessage, {
sentry.captureMessage(errorMessage, {
// "extra" key is required by Sentry
extra: txMeta,
extra: { txMeta },
})
}
10 changes: 6 additions & 4 deletions app/scripts/lib/setupFetchDebugging.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module.exports = setupFetchDebugging

//
// This is a utility to help resolve cases where `window.fetch` throws a
// `TypeError: Failed to Fetch` without any stack or context for the request
// `TypeError: Failed to Fetch` without any stack or context for the request
// https://github.com/getsentry/sentry-javascript/pull/1293
//

Expand All @@ -17,9 +17,11 @@ function setupFetchDebugging() {
try {
return await originalFetch.call(window, ...args)
} catch (err) {
console.warn('FetchDebugger - fetch encountered an Error', err)
console.warn('FetchDebugger - overriding stack to point of original call')
err.stack = initialStack
if (!err.stack) {
console.warn('FetchDebugger - fetch encountered an Error without a stack', err)
console.warn('FetchDebugger - overriding stack to point of original call')
err.stack = initialStack
}
throw err
}
}
Expand Down
75 changes: 36 additions & 39 deletions app/scripts/lib/setupRaven.js → app/scripts/lib/setupSentry.js
Original file line number Diff line number Diff line change
@@ -1,58 +1,55 @@
const Raven = require('raven-js')
const Sentry = require('@sentry/browser')
const METAMASK_DEBUG = process.env.METAMASK_DEBUG
const extractEthjsErrorMessage = require('./extractEthjsErrorMessage')
const PROD = 'https://3567c198f8a8412082d32655da2961d0@sentry.io/273505'
const DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496'
const SENTRY_DSN_PROD = 'https://3567c198f8a8412082d32655da2961d0@sentry.io/273505'
const SENTRY_DSN_DEV = 'https://f59f3dd640d2429d9d0e2445a87ea8e1@sentry.io/273496'

module.exports = setupRaven
module.exports = setupSentry

// Setup raven / sentry remote error reporting
function setupRaven (opts) {
const { release } = opts
let ravenTarget
// Setup sentry remote error reporting
function setupSentry (opts) {
const { release, getState } = opts
let sentryTarget
// detect brave
const isBrave = Boolean(window.chrome.ipcRenderer)

if (METAMASK_DEBUG) {
console.log('Setting up Sentry Remote Error Reporting: DEV')
ravenTarget = DEV
console.log('Setting up Sentry Remote Error Reporting: SENTRY_DSN_DEV')
sentryTarget = SENTRY_DSN_DEV
} else {
console.log('Setting up Sentry Remote Error Reporting: PROD')
ravenTarget = PROD
console.log('Setting up Sentry Remote Error Reporting: SENTRY_DSN_PROD')
sentryTarget = SENTRY_DSN_PROD
}

const client = Raven.config(ravenTarget, {
Sentry.init({
dsn: sentryTarget,
debug: METAMASK_DEBUG,
release,
transport: function (opts) {
opts.data.extra.isBrave = isBrave
const report = opts.data
beforeSend: (report) => rewriteReport(report),
})

try {
// handle error-like non-error exceptions
rewriteErrorLikeExceptions(report)
// simplify certain complex error messages (e.g. Ethjs)
simplifyErrorMessages(report)
// modify report urls
rewriteReportUrls(report)
} catch (err) {
console.warn(err)
}
// make request normally
client._makeRequest(opts)
},
Sentry.configureScope(scope => {
scope.setExtra('isBrave', isBrave)
})
client.install()

return Raven
}
function rewriteReport(report) {
try {
// simplify certain complex error messages (e.g. Ethjs)
simplifyErrorMessages(report)
// modify report urls
rewriteReportUrls(report)
// append app state
if (getState) {
const appState = getState()
report.extra.appState = appState
}
} catch (err) {
console.warn(err)
}
return report
}

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}`
})
return Sentry
}

function simplifyErrorMessages (report) {
Expand Down
14 changes: 12 additions & 2 deletions app/scripts/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const extension = require('extensionizer')
const ExtensionPlatform = require('./platforms/extension')
const NotificationManager = require('./lib/notification-manager')
const notificationManager = new NotificationManager()
const setupRaven = require('./lib/setupRaven')
const setupSentry = require('./lib/setupSentry')
const log = require('loglevel')

start().catch(log.error)
Expand All @@ -21,7 +21,17 @@ async function start () {

// setup sentry error reporting
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
// const css = MetaMaskUiCss()
Expand Down
Loading

0 comments on commit 73eeeda

Please sign in to comment.