Browse Source

Merge pull request #498 from gre/export-button

Add export logs
master
Meriadec Pillet 7 years ago
committed by GitHub
parent
commit
4ebf69b3e7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 60
      src/components/ExportLogsBtn.js
  2. 4
      src/components/SettingsPage/sections/Profile.js
  3. 6
      src/components/ThrowBlock.js
  4. 66
      src/logger.js
  5. 4
      static/i18n/en/settings.yml

60
src/components/ExportLogsBtn.js

@ -0,0 +1,60 @@
// @flow
import logger from 'logger'
import moment from 'moment'
import fs from 'fs'
import { webFrame, remote } from 'electron'
import React, { Component } from 'react'
import { translate } from 'react-i18next'
import { connect } from 'react-redux'
import { createStructuredSelector, createSelector } from 'reselect'
import { accountsSelector, encodeAccountsModel } from 'reducers/accounts'
import { storeSelector as settingsSelector } from 'reducers/settings'
import Button from './base/Button'
const mapStateToProps = createStructuredSelector({
accounts: createSelector(accountsSelector, encodeAccountsModel),
settings: settingsSelector,
})
class ExportLogsBtn extends Component<{
t: *,
settings: *,
accounts: *,
}> {
handleExportLogs = () => {
const { accounts, settings } = this.props
const logs = logger.exportLogs()
const resourceUsage = webFrame.getResourceUsage()
const report = { resourceUsage, logs, accounts, settings, date: new Date() }
console.log(report) // eslint-disable-line no-console
const reportJSON = JSON.stringify(report)
const path = remote.dialog.showSaveDialog({
title: 'Export logs',
defaultPath: `ledger_export_${moment().format('YYYY-MM-DD_HHmmss')}.json`,
filters: [
{
name: 'All Files',
extensions: ['json'],
},
],
})
if (path) {
fs.writeFile(path, reportJSON, err => {
if (err) {
logger.error(err)
}
})
}
}
render() {
const { t } = this.props
return (
<Button primary onClick={this.handleExportLogs}>
{t('settings:exportLogs.btn')}
</Button>
)
}
}
export default translate()(connect(mapStateToProps)(ExportLogsBtn))

4
src/components/SettingsPage/sections/Profile.js

@ -13,6 +13,7 @@ import { delay } from 'helpers/promise'
import type { SettingsState } from 'reducers/settings'
import type { T } from 'types/common'
import ExportLogsBtn from 'components/ExportLogsBtn'
import CheckBox from 'components/base/CheckBox'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
@ -181,6 +182,9 @@ class TabProfile extends PureComponent<Props, State> {
{t('settings:profile.hardReset')}
</Button>
</Row>
<Row title={t('settings:exportLogs.title')} desc={t('settings:exportLogs.desc')}>
<ExportLogsBtn />
</Row>
</Body>
<ConfirmModal

6
src/components/ThrowBlock.js

@ -1,5 +1,6 @@
// @flow
import logger from 'logger'
import React, { PureComponent } from 'react'
import styled from 'styled-components'
import { shell, remote } from 'electron'
@ -8,6 +9,7 @@ import qs from 'querystring'
import { rgba } from 'styles/helpers'
import db from 'helpers/db'
import ExportLogsBtn from 'components/ExportLogsBtn'
import Box from 'components/base/Box'
import Button from 'components/base/Button'
@ -43,6 +45,7 @@ class ThrowBlock extends PureComponent<Props, State> {
}
componentDidCatch(error: Error) {
logger.error(error)
this.setState({ error })
}
@ -83,9 +86,10 @@ ${error.stack}
<Button primary onClick={this.handleRestart}>
{'Restart app'}
</Button>
<Button primary onClick={this.handleReset}>
<Button danger onClick={this.handleReset}>
{'Reset app files'}
</Button>
<ExportLogsBtn />
<Button primary onClick={this.handleCreateIssue}>
{'Create ticket'}
</Button>

66
src/logger.js

@ -9,37 +9,93 @@
* - for analytics in the future
*/
const logs = []
const MAX_LOG_LENGTH = 500
const MAX_LOG_JSON_THRESHOLD = 2000
function addLog(type, ...args) {
logs.push({ type, date: new Date(), args })
if (logs.length > MAX_LOG_LENGTH) {
logs.shift()
}
}
const makeSerializableLog = (o: mixed) => {
if (typeof o === 'string') return o
if (typeof o === 'number') return o
if (typeof o === 'object' && o) {
try {
const json = JSON.stringify(o)
if (json.length < MAX_LOG_JSON_THRESHOLD) {
return o
}
// try to make a one level object on the same principle
const oneLevel = {}
Object.keys(o).forEach(key => {
// $FlowFixMe
oneLevel[key] = makeSerializableLog(o[key])
})
const json2 = JSON.stringify(oneLevel)
if (json2.length < MAX_LOG_JSON_THRESHOLD) {
return oneLevel
}
} catch (e) {
// This is not serializable so we will just stringify it
}
}
return String(o)
}
const logClicks = !__DEV__ || process.env.DEBUG_CLICK_ELEMENT
const logRedux = !__DEV__ || process.env.DEBUG_ACTION
export default {
// tracks the user interactions (click, input focus/blur, what else?)
onClickElement: (role: string, roleData: ?Object) => {
if (!__DEV__ || process.env.DEBUG_CLICK_ELEMENT) {
const label = `👆 ${role}`
if (roleData) {
const label = `👆 ${role}`
if (roleData) {
if (logClicks) {
console.log(label, roleData)
} else {
}
addLog('click', label, roleData)
} else {
if (logClicks) {
console.log(label)
}
addLog('click', label, roleData)
}
},
// tracks Redux actions (NB not all actions are serializable)
onReduxAction: (action: Object) => {
if (!__DEV__ || process.env.DEBUG_ACTION) {
if (logRedux) {
console.log(`⚛️ ${action.type}`, action)
}
addLog('action', `⚛️ ${action.type}`, action)
},
// General functions in case the hooks don't apply
log: (...args: any) => {
console.log(...args)
addLog('log', ...args)
},
warn: (...args: any) => {
console.warn(...args)
addLog('warn', ...args)
},
error: (...args: any) => {
console.error(...args)
addLog('error', ...args)
},
exportLogs: (): Array<{ type: string, date: Date, args: Array<any> }> =>
logs.map(({ type, date, args }) => ({
type,
date,
args: args.map(makeSerializableLog),
})),
}

4
static/i18n/en/settings.yml

@ -70,3 +70,7 @@ softResetModal:
title: Clean application cache
subTitle: Are you sure houston?
desc: Lorem ipsum dolor sit amet
exportLogs:
title: Export Logs
desc: Export Logs
btn: Export Logs

Loading…
Cancel
Save