diff --git a/.eslintrc b/.eslintrc
index 0cffcf1a..916571b7 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -51,6 +51,7 @@
"no-restricted-syntax": 0,
"no-prototype-builtins": 0,
"no-void": 0,
+ "no-lonely-if": 0,
"react/forbid-prop-types": 0,
"react/jsx-curly-brace-presence": 0,
"react/jsx-filename-extension": 0,
diff --git a/package.json b/package.json
index 9bd7f360..0dc5266c 100644
--- a/package.json
+++ b/package.json
@@ -106,6 +106,9 @@
"tippy.js": "^2.5.2",
"uncontrollable": "^6.0.0",
"uuid": "^3.2.1",
+ "winston": "^3.0.0",
+ "winston-daily-rotate-file": "^3.2.3",
+ "winston-transport": "^4.2.0",
"ws": "^5.1.1",
"zxcvbn": "^4.4.2"
},
diff --git a/src/components/ExportLogsBtn.js b/src/components/ExportLogsBtn.js
index d509c266..389538ea 100644
--- a/src/components/ExportLogsBtn.js
+++ b/src/components/ExportLogsBtn.js
@@ -5,58 +5,37 @@ 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 KeyHandler from 'react-key-handler'
-import { createStructuredSelector, createSelector } from 'reselect'
-import { accountsSelector, encodeAccountsModel } from 'reducers/accounts'
-import { storeSelector as settingsSelector } from 'reducers/settings'
+import { getCurrentLogFile } from 'helpers/resolveLogsDirectory'
import Button from './base/Button'
-const mapStateToProps = createStructuredSelector({
- accounts: createSelector(accountsSelector, encodeAccountsModel),
- settings: settingsSelector,
-})
-
class ExportLogsBtn extends Component<{
t: *,
- settings: ?*,
- accounts: ?*,
hookToShortcut?: boolean,
}> {
handleExportLogs = () => {
- const { accounts, settings } = this.props
- const logs = logger.exportLogs()
+ const srcLogFile = getCurrentLogFile()
const resourceUsage = webFrame.getResourceUsage()
- const report = {
+ logger.log('exportLogsMeta', {
resourceUsage,
- logs,
- accounts,
- settings,
- date: new Date(),
release: __APP_VERSION__,
git_commit: __GIT_REVISION__,
environment: __DEV__ ? 'development' : 'production',
- }
- console.log(report) // eslint-disable-line no-console
- const reportJSON = JSON.stringify(report)
+ })
const path = remote.dialog.showSaveDialog({
title: 'Export logs',
defaultPath: `ledgerlive-export-${moment().format(
'YYYY.MM.DD-HH.mm.ss',
- )}-${__GIT_REVISION__ || 'unversionned'}.json`,
+ )}-${__GIT_REVISION__ || 'unversionned'}.log`,
filters: [
{
name: 'All Files',
- extensions: ['json'],
+ extensions: ['log'],
},
],
})
if (path) {
- fs.writeFile(path, reportJSON, err => {
- if (err) {
- logger.error(err)
- }
- })
+ fs.createReadStream(srcLogFile).pipe(fs.createWriteStream(path))
}
}
@@ -78,10 +57,4 @@ class ExportLogsBtn extends Component<{
}
}
-const WithAppData = connect(mapStateToProps)(ExportLogsBtn)
-const WithoutAppData = ExportLogsBtn
-
-const ExportLogsBtnDispatcher = ({ withAppData, ...rest }: *) =>
- withAppData ? :
-
-export default translate()(ExportLogsBtnDispatcher)
+export default translate()(ExportLogsBtn)
diff --git a/src/components/Onboarding/index.js b/src/components/Onboarding/index.js
index 9c6932bc..19d5790a 100644
--- a/src/components/Onboarding/index.js
+++ b/src/components/Onboarding/index.js
@@ -25,6 +25,7 @@ import { getCurrentDevice } from 'reducers/devices'
import { unlock } from 'reducers/application'
+import ExportLogsBtn from 'components/ExportLogsBtn'
import Box from 'components/base/Box'
import TriggerAppReady from '../TriggerAppReady'
@@ -158,6 +159,8 @@ class Onboarding extends PureComponent {
return (
+
+
{step.options.showBreadcrumb && }
diff --git a/src/components/SettingsPage/sections/About.js b/src/components/SettingsPage/sections/About.js
index 53042723..460d56eb 100644
--- a/src/components/SettingsPage/sections/About.js
+++ b/src/components/SettingsPage/sections/About.js
@@ -5,6 +5,7 @@ import { translate } from 'react-i18next'
import type { T } from 'types/common'
import TrackPage from 'analytics/TrackPage'
import IconHelp from 'icons/Help'
+import resolveLogsDirectory from 'helpers/resolveLogsDirectory'
import ExportLogsBtn from 'components/ExportLogsBtn'
import CleanButton from '../CleanButton'
@@ -55,7 +56,10 @@ class SectionAbout extends PureComponent {
>
-
+
diff --git a/src/helpers/resolveLogsDirectory.js b/src/helpers/resolveLogsDirectory.js
new file mode 100644
index 00000000..380fcb8e
--- /dev/null
+++ b/src/helpers/resolveLogsDirectory.js
@@ -0,0 +1,30 @@
+// @flow
+
+import path from 'path'
+import moment from 'moment'
+
+const resolveLogsDirectory = () => {
+ const { LEDGER_LOGS_DIRECTORY } = process.env
+ if (LEDGER_LOGS_DIRECTORY) return LEDGER_LOGS_DIRECTORY
+ const electron = require('electron')
+ return path.resolve((electron.app || electron.remote.app).getPath('userData'), 'logs')
+}
+
+export default resolveLogsDirectory
+
+export const RotatingLogFileParameters = {
+ filename: 'application-%DATE%.log',
+ datePattern: 'YYYY-MM-DD',
+ zippedArchive: true,
+ maxSize: '20m',
+ maxFiles: '14d',
+}
+
+export const getCurrentLogFile = () =>
+ path.resolve(
+ resolveLogsDirectory(),
+ RotatingLogFileParameters.filename.replace(
+ '%DATE%',
+ moment().format(RotatingLogFileParameters.datePattern),
+ ),
+ )
diff --git a/src/internals/index.js b/src/internals/index.js
index 0db57625..1eb49a16 100644
--- a/src/internals/index.js
+++ b/src/internals/index.js
@@ -7,6 +7,8 @@ import sentry from 'sentry/node'
import { EXPERIMENTAL_HTTP_ON_RENDERER } from 'config/constants'
import { serializeError } from 'helpers/errors'
+logger.setProcessShortName('internal')
+
require('../env')
process.title = 'Internal'
diff --git a/src/logger.js b/src/logger.js
index 552fc071..a26beb6f 100644
--- a/src/logger.js
+++ b/src/logger.js
@@ -1,13 +1,8 @@
// @flow
-/* eslint-disable no-console */
-/**
- * IDEA:
- * logger is an alternative to use for console.log that will be used for many purposes:
- * - provide useful data for debugging during dev (idea is to have opt-in env var)
- * - enabled in prod to provide useful data to debug when sending to Sentry
- * - for analytics in the future
- */
+import winston from 'winston'
+import Transport from 'winston-transport'
+import resolveLogsDirectory, { RotatingLogFileParameters } from 'helpers/resolveLogsDirectory'
import {
DEBUG_NETWORK,
@@ -20,51 +15,83 @@ import {
DEBUG_ANALYTICS,
} from 'config/constants'
-const logs = []
+require('winston-daily-rotate-file')
-const MAX_LOG_LENGTH = 500
-const MAX_LOG_JSON_THRESHOLD = 2000
+let pname = '?'
-const anonymousMode = !__DEV__
+const { format } = winston
+const { combine, json, timestamp } = format
+
+const pinfo = format(info => {
+ info.pname = pname
+ return info
+})
+
+const transports = [
+ new winston.transports.DailyRotateFile({
+ dirname: resolveLogsDirectory(),
+ ...RotatingLogFileParameters,
+ }),
+]
-function addLog(type, ...args) {
- logs.push({ type, date: new Date(), args })
- if (logs.length > MAX_LOG_LENGTH) {
- logs.shift()
+if (process.env.NODE_ENV !== 'production' || process.env.LOGS_IN_CONSOLE) {
+ let consoleT
+ if (typeof window === 'undefined') {
+ // on Node we want a concise logger
+ consoleT = new winston.transports.Console({
+ format: format.simple(),
+ })
+ } else {
+ // On Browser we want to preserve direct usage of console with the "expandable" objects
+ const SPLAT = Symbol.for('splat')
+ class CustomConsole extends Transport {
+ log(info, callback) {
+ setImmediate(() => {
+ this.emit('logged', info)
+ })
+ const rest = info[SPLAT]
+ /* eslint-disable no-console */
+ if (info.level === 'error') {
+ if (rest) {
+ console.error(info.message, ...rest)
+ } else {
+ console.error(info.message)
+ }
+ } else if (info.level === 'warn') {
+ if (rest) {
+ console.warn(info.message, ...rest)
+ } else {
+ console.warn(info.message)
+ }
+ } else {
+ if (rest) {
+ console.log(info.message, ...rest)
+ } else {
+ console.log(info.message)
+ }
+ }
+ /* eslint-enable no-console */
+ callback()
+ }
+ }
+ consoleT = new CustomConsole()
}
+ transports.push(consoleT)
}
+const logger = winston.createLogger({
+ level: 'info',
+ format: combine(pinfo(), timestamp(), json()),
+ transports,
+})
+
+const anonymousMode = !__DEV__
+
function anonymizeURL(url) {
if (!anonymousMode) return url
return url.replace(/\/addresses\/[^/]+/g, '/addresses/')
}
-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 logCmds = !__DEV__ || DEBUG_COMMANDS
const logDb = !__DEV__ || DEBUG_DB
const logRedux = !__DEV__ || DEBUG_ACTION
@@ -75,42 +102,43 @@ const logNetwork = !__DEV__ || DEBUG_NETWORK
const logAnalytics = !__DEV__ || DEBUG_ANALYTICS
export default {
+ setProcessShortName: (processShortName: string) => {
+ pname = processShortName
+ },
+
onCmd: (type: string, id: string, spentTime: number, data?: any) => {
if (logCmds) {
switch (type) {
case 'cmd.START':
- console.log(`CMD ${id}.send(`, data, ')')
+ logger.log('info', 'info', `CMD ${id}.send()`, { type, data })
break
case 'cmd.NEXT':
- console.log(`● CMD ${id}`, data)
+ logger.log('info', `● CMD ${id}`, { type, data })
break
case 'cmd.COMPLETE':
- console.log(`✔ CMD ${id} finished in ${spentTime.toFixed(0)}ms`)
+ logger.log('info', `✔ CMD ${id} finished in ${spentTime.toFixed(0)}ms`, { type })
break
case 'cmd.ERROR':
- console.warn(`✖ CMD ${id} error`, data)
+ logger.log('warn', `✖ CMD ${id} error`, { type, data })
break
default:
}
}
- addLog('cmd', type, id, spentTime, data)
},
onDB: (way: 'read' | 'write' | 'clear', name: string) => {
const msg = `📁 ${way} ${name}`
if (logDb) {
- console.log(msg)
+ logger.log('info', msg, { type: 'db' })
}
- addLog('db', msg)
},
// tracks Redux actions (NB not all actions are serializable)
onReduxAction: (action: Object) => {
if (logRedux) {
- console.log(`⚛️ ${action.type}`, action)
+ logger.log('info', `⚛️ ${action.type}`, { type: 'action', action })
}
- addLog('action', `⚛️ ${action.type}`, action)
},
// tracks keyboard events
@@ -120,31 +148,27 @@ export default {
const displayEl = `${tagName.toLowerCase()}${classList.length ? ` ${classList.item(0)}` : ''}`
const msg = `⇓ - active element ${displayEl}`
if (logTabkey) {
- console.log(msg)
+ logger.log('info', msg, { type: 'keydown' })
}
- addLog('keydown', msg)
},
websocket: (type: string, msg: *) => {
if (logWS) {
- console.log(`~ ${type}:`, msg)
+ logger.log('info', `~ ${type}:`, msg, { type: 'ws' })
}
- addLog('ws', `~ ${type}`, msg)
},
libcore: (level: string, msg: string) => {
if (logLibcore) {
- console.log(`🛠 ${level}: ${msg}`)
+ logger.log('info', `🛠 ${level}: ${msg}`, { type: 'libcore' })
}
- addLog('action', `🛠 ${level}: ${msg}`)
},
network: ({ method, url }: { method: string, url: string }) => {
const log = `➡📡 ${method} ${anonymizeURL(url)}`
if (logNetwork) {
- console.log(log)
+ logger.log('info', log, { type: 'network' })
}
- addLog('network', log)
},
networkSucceed: ({
@@ -162,9 +186,8 @@ export default {
url,
)} – finished in ${responseTime.toFixed(0)}ms`
if (logNetwork) {
- console.log(log)
+ logger.log('info', log, { type: 'network-response' })
}
- addLog('network-response', log)
},
networkError: ({
@@ -184,9 +207,8 @@ export default {
url,
)} – ${error} – failed after ${responseTime.toFixed(0)}ms`
if (logNetwork) {
- console.log(log)
+ logger.log('info', log, { type: 'network-error', status, method })
}
- addLog('network-error', log)
},
networkDown: ({
@@ -202,59 +224,50 @@ export default {
0,
)}ms`
if (logNetwork) {
- console.log(log)
+ logger.log('info', log, { type: 'network-down' })
}
- addLog('network-down', log)
},
analyticsStart: (id: string) => {
if (logAnalytics) {
- console.log(`△ start() with user id ${id}`)
+ logger.log('info', `△ start() with user id ${id}`, { type: 'anaytics-start', id })
}
- addLog('anaytics-start', id)
},
analyticsStop: () => {
if (logAnalytics) {
- console.log(`△ stop()`)
+ logger.log('info', `△ stop()`, { type: 'anaytics-stop' })
}
- addLog('anaytics-stop')
},
analyticsTrack: (event: string, properties: ?Object) => {
if (logAnalytics) {
- console.log(`△ track ${event}`, properties)
+ logger.log('info', `△ track ${event}`, { type: 'anaytics-track', properties })
}
- addLog('anaytics-track', `${event}`)
},
analyticsPage: (category: string, name: ?string, properties: ?Object) => {
if (logAnalytics) {
- console.log(`△ page ${category} ${name || ''}`, properties)
+ logger.log('info', `△ page ${category} ${name || ''}`, { type: 'anaytics-page', properties })
}
- addLog('anaytics-page', `${category} ${name || ''}`)
},
// General functions in case the hooks don't apply
log: (...args: any) => {
- console.log(...args)
- addLog('log', ...args)
+ logger.log('info', ...args)
},
warn: (...args: any) => {
- console.warn(...args)
- addLog('warn', ...args)
+ logger.log('warn', ...args)
},
error: (...args: any) => {
- console.error(...args)
- addLog('error', ...args)
+ logger.log('error', ...args)
},
critical: (error: Error) => {
- addLog('critical', error)
- console.error(error)
+ logger.log('error', error)
if (!process.env.STORYBOOK_ENV) {
try {
if (typeof window !== 'undefined') {
@@ -263,15 +276,8 @@ export default {
require('sentry/node').captureException(error)
}
} catch (e) {
- console.warn("Can't send to sentry", error, e)
+ logger.log('warn', "Can't send to sentry", error, e)
}
}
},
-
- exportLogs: (): Array<{ type: string, date: Date, args: Array }> =>
- logs.map(({ type, date, args }) => ({
- type,
- date,
- args: args.map(makeSerializableLog),
- })),
}
diff --git a/src/main/bridge.js b/src/main/bridge.js
index 5ed618b2..88cfd4f3 100644
--- a/src/main/bridge.js
+++ b/src/main/bridge.js
@@ -9,14 +9,18 @@ import path from 'path'
import logger from 'logger'
import sentry from 'sentry/node'
import user from 'helpers/user'
+import resolveLogsDirectory from 'helpers/resolveLogsDirectory'
import setupAutoUpdater, { quitAndInstall } from './autoUpdate'
import { setInternalProcessPID } from './terminator'
import { getMainWindow } from './app'
+logger.setProcessShortName('main')
+
// sqlite files will be located in the app local data folder
const LEDGER_LIVE_SQLITE_PATH = path.resolve(app.getPath('userData'), 'sqlite')
+const LEDGER_LOGS_DIRECTORY = process.env.LEDGER_LOGS_DIRECTORY || resolveLogsDirectory()
let internalProcess
@@ -45,6 +49,7 @@ const bootInternalProcess = () => {
internalProcess = fork(forkBundlePath, {
env: {
...process.env,
+ LEDGER_LOGS_DIRECTORY,
LEDGER_LIVE_SQLITE_PATH,
INITIAL_SENTRY_ENABLED: sentryEnabled,
SENTRY_USER_ID: userId,
diff --git a/src/renderer/init.js b/src/renderer/init.js
index 951590c7..3b63d849 100644
--- a/src/renderer/init.js
+++ b/src/renderer/init.js
@@ -31,6 +31,8 @@ import AppError from 'components/AppError'
import 'styles/global'
+logger.setProcessShortName('renderer')
+
const rootNode = document.getElementById('app')
const TAB_KEY = 9
diff --git a/static/i18n/en/app.yml b/static/i18n/en/app.yml
index ce88531b..57f6db72 100644
--- a/static/i18n/en/app.yml
+++ b/static/i18n/en/app.yml
@@ -333,8 +333,6 @@ settings: # Always ensure descriptions carry full stops (.)
changePassword: Change password
sync: Synchronize accounts
syncDesc: Resynchronize your accounts with the network.
- export: Export logs
- exportDesc: Exporting Ledger Live logs may be necessary for troubleshooting purposes.
softResetTitle: Clear cache
softResetDesc: Clear the Ledger Live cache to force resynchronization with the blockchain.
softReset: Clear
@@ -371,7 +369,7 @@ settings: # Always ensure descriptions carry full stops (.)
desc: The account will no longer be included in your portfolio. This operation does not affect your assets. Accounts can always be re-added.
exportLogs:
title: Export logs
- desc: Exporting Ledger Live logs may be necessary for troubleshooting purposes.
+ desc: 'Exporting Ledger Live logs may be necessary for troubleshooting purposes.'
btn: Export
password:
warning_0: Very weak
diff --git a/yarn.lock b/yarn.lock
index e85f98a2..f1f62680 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4438,16 +4438,16 @@ collection-visit@^1.0.0:
map-visit "^1.0.0"
object-visit "^1.0.0"
+color-convert@^0.5.0, color-convert@~0.5.0:
+ version "0.5.3"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd"
+
color-convert@^1.3.0, color-convert@^1.9.0, color-convert@^1.9.1:
version "1.9.2"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147"
dependencies:
color-name "1.1.1"
-color-convert@~0.5.0:
- version "0.5.3"
- resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd"
-
color-name@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689"
@@ -4469,6 +4469,13 @@ color-string@^1.5.2:
color-name "^1.0.0"
simple-swizzle "^0.2.2"
+color@0.8.x:
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/color/-/color-0.8.0.tgz#890c07c3fd4e649537638911cf691e5458b6fca5"
+ dependencies:
+ color-convert "^0.5.0"
+ color-string "^0.3.0"
+
color@^0.11.0:
version "0.11.4"
resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764"
@@ -4492,11 +4499,15 @@ colormin@^1.0.5:
css-color-names "0.0.4"
has "^1.0.1"
+colornames@0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/colornames/-/colornames-0.0.2.tgz#d811fd6c84f59029499a8ac4436202935b92be31"
+
colors@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
-colors@^1.1.2:
+colors@^1.1.2, colors@^1.2.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.0.tgz#5f20c9fef6945cb1134260aab33bfbdc8295e04e"
@@ -4504,6 +4515,13 @@ colors@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
+colorspace@1.0.x:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.0.1.tgz#c99c796ed31128b9876a52e1ee5ee03a4a719749"
+ dependencies:
+ color "0.8.x"
+ text-hex "0.0.x"
+
columnify@~1.5.4:
version "1.5.4"
resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb"
@@ -5011,6 +5029,10 @@ currently-unhandled@^0.4.1:
dependencies:
array-find-index "^1.0.1"
+cycle@~1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2"
+
cyclist@~0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
@@ -5453,6 +5475,14 @@ dezalgo@^1.0.0, dezalgo@~1.0.3:
asap "^2.0.0"
wrappy "1"
+diagnostics@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/diagnostics/-/diagnostics-1.1.0.tgz#e1090900b49523e8527be20f081275205f2ae36a"
+ dependencies:
+ colorspace "1.0.x"
+ enabled "1.0.x"
+ kuler "0.0.x"
+
diff@^3.2.0, diff@^3.3.1, diff@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
@@ -5926,6 +5956,12 @@ emotion@^9.1.2:
babel-plugin-emotion "^9.2.4"
create-emotion "^9.2.4"
+enabled@1.0.x:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/enabled/-/enabled-1.0.2.tgz#965f6513d2c2d1c5f4652b64a2e3396467fc2f93"
+ dependencies:
+ env-variable "0.0.x"
+
encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
@@ -5967,6 +6003,10 @@ env-paths@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0"
+env-variable@0.0.x:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/env-variable/-/env-variable-0.0.4.tgz#0d6280cf507d84242befe35a512b5ae4be77c54e"
+
envinfo@^5.7.0:
version "5.10.0"
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-5.10.0.tgz#503a9774ae15b93ea68bdfae2ccd6306624ea6df"
@@ -6572,6 +6612,10 @@ fast-memoize@^2.2.7:
version "2.5.1"
resolved "https://registry.yarnpkg.com/fast-memoize/-/fast-memoize-2.5.1.tgz#c3519241e80552ce395e1a32dcdde8d1fd680f5d"
+fast-safe-stringify@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.4.tgz#4fe828718aa61dbcf9119c3c24e79cc4dea973b2"
+
fastparse@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
@@ -6612,6 +6656,10 @@ fd-slicer@~1.0.1:
dependencies:
pend "~1.2.0"
+fecha@^2.3.3:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/fecha/-/fecha-2.3.3.tgz#948e74157df1a32fd1b12c3a3c3cdcb6ec9d96cd"
+
figgy-pudding@^3.0.0, figgy-pudding@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.1.0.tgz#a77ed2284175976c424b390b298569e9df86dd1e"
@@ -6643,6 +6691,12 @@ file-loader@^1.1.11:
loader-utils "^1.0.2"
schema-utils "^0.4.5"
+file-stream-rotator@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/file-stream-rotator/-/file-stream-rotator-0.2.1.tgz#0d6fea1a9a7aba25a87cfd31b6e269e44e8f0af2"
+ dependencies:
+ moment "^2.11.2"
+
filename-regex@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
@@ -8915,6 +8969,12 @@ klaw@^1.0.0:
optionalDependencies:
graceful-fs "^4.1.9"
+kuler@0.0.x:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/kuler/-/kuler-0.0.0.tgz#b66bb46b934e550f59d818848e0abba4f7f5553c"
+ dependencies:
+ colornames "0.0.2"
+
latest-version@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15"
@@ -9235,6 +9295,16 @@ log-update@^1.0.2:
ansi-escapes "^1.0.0"
cli-cursor "^1.0.2"
+logform@^1.6.0, logform@^1.9.0:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/logform/-/logform-1.9.1.tgz#58b29d7b11c332456d7a217e17b48a13ad69d60a"
+ dependencies:
+ colors "^1.2.1"
+ fast-safe-stringify "^2.0.4"
+ fecha "^2.3.3"
+ ms "^2.1.1"
+ triple-beam "^1.2.0"
+
loglevel@^1.4.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.1.tgz#e0fc95133b6ef276cdc8887cdaf24aa6f156f8fa"
@@ -9702,7 +9772,7 @@ mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdi
dependencies:
minimist "0.0.8"
-moment@^2.21.0, moment@^2.22.2:
+moment@^2.11.2, moment@^2.21.0, moment@^2.22.2:
version "2.22.2"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66"
@@ -9721,7 +9791,7 @@ ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
-ms@2.1.1, ms@^2.0.0:
+ms@2.1.1, ms@^2.0.0, ms@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
@@ -10364,6 +10434,10 @@ once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0, once@~1.4.0:
dependencies:
wrappy "1"
+one-time@0.0.4:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/one-time/-/one-time-0.0.4.tgz#f8cdf77884826fe4dff93e3a9cc37b1e4480742e"
+
onetime@^1.0.0:
version "1.1.0"
resolved "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
@@ -13190,7 +13264,7 @@ stable@~0.1.6:
version "0.1.8"
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
-stack-trace@0.0.10:
+stack-trace@0.0.10, stack-trace@0.0.x:
version "0.0.10"
resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
@@ -13631,6 +13705,10 @@ test-exclude@^4.2.1:
read-pkg-up "^1.0.1"
require-main-filename "^1.0.1"
+text-hex@0.0.x:
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-0.0.0.tgz#578fbc85a6a92636e42dd17b41d0218cce9eb2b3"
+
text-table@0.2.0, text-table@^0.2.0, text-table@~0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@@ -13798,6 +13876,10 @@ trim@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd"
+triple-beam@^1.2.0, triple-beam@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9"
+
trough@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.2.tgz#7f1663ec55c480139e2de5e486c6aef6cc24a535"
@@ -14657,6 +14739,45 @@ window-size@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075"
+winston-compat@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/winston-compat/-/winston-compat-0.1.4.tgz#599b4ce807ffe728713ecc25ede3f6b89425b739"
+ dependencies:
+ cycle "~1.0.3"
+ logform "^1.6.0"
+ triple-beam "^1.2.0"
+
+winston-daily-rotate-file@^3.2.3:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/winston-daily-rotate-file/-/winston-daily-rotate-file-3.2.3.tgz#9f80e7a421ab32b073c1217bae62e762001197d6"
+ dependencies:
+ file-stream-rotator "^0.2.1"
+ semver "^5.5.0"
+ triple-beam "^1.3.0"
+ winston-compat "^0.1.4"
+ winston-transport "^4.2.0"
+
+winston-transport@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.2.0.tgz#a20be89edf2ea2ca39ba25f3e50344d73e6520e5"
+ dependencies:
+ readable-stream "^2.3.6"
+ triple-beam "^1.2.0"
+
+winston@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/winston/-/winston-3.0.0.tgz#1f0b24a96586798bcf0cd149fb07ed47cb01a1b2"
+ dependencies:
+ async "^2.6.0"
+ diagnostics "^1.0.1"
+ is-stream "^1.1.0"
+ logform "^1.9.0"
+ one-time "0.0.4"
+ readable-stream "^2.3.6"
+ stack-trace "0.0.x"
+ triple-beam "^1.3.0"
+ winston-transport "^4.2.0"
+
wordwrap@0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"