diff --git a/package.json b/package.json index e1702c32..07c4767d 100644 --- a/package.json +++ b/package.json @@ -109,7 +109,6 @@ "uncontrollable": "^6.0.0", "uuid": "^3.2.1", "winston": "^3.0.0", - "winston-daily-rotate-file": "^3.2.3", "winston-transport": "^4.2.0", "write-file-atomic": "^2.3.0", "ws": "^5.1.1", diff --git a/src/components/ExportLogsBtn.js b/src/components/ExportLogsBtn.js index 3e391c14..66ab5ec9 100644 --- a/src/components/ExportLogsBtn.js +++ b/src/components/ExportLogsBtn.js @@ -2,12 +2,20 @@ import logger from 'logger' import moment from 'moment' import fs from 'fs' -import { webFrame, remote } from 'electron' +import { ipcRenderer, webFrame, remote } from 'electron' import React, { Component } from 'react' import { translate } from 'react-i18next' import KeyHandler from 'react-key-handler' import Button from './base/Button' +const queryLogs = () => + new Promise(success => { + ipcRenderer.once('logs', (event: any, { logs }) => { + success(logs) + }) + ipcRenderer.send('queryLogs') + }) + function writeToFile(file, data) { return new Promise((resolve, reject) => { fs.writeFile(file, data, error => { @@ -33,7 +41,6 @@ class ExportLogsBtn extends Component<{ environment: __DEV__ ? 'development' : 'production', userAgent: window.navigator.userAgent, }) - const date = new Date() // we don't want all the logs that happen after the Export was pressed ^^ const path = remote.dialog.showSaveDialog({ title: 'Export logs', defaultPath: `ledgerlive-export-${moment().format( @@ -47,7 +54,7 @@ class ExportLogsBtn extends Component<{ ], }) if (path) { - const logs = await logger.queryAllLogs(date) + const logs = await queryLogs() const json = JSON.stringify(logs) await writeToFile(path, json) } diff --git a/src/components/SettingsPage/sections/Help.js b/src/components/SettingsPage/sections/Help.js index f54301e5..c4577020 100644 --- a/src/components/SettingsPage/sections/Help.js +++ b/src/components/SettingsPage/sections/Help.js @@ -5,7 +5,6 @@ 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/log' import { urls } from 'config/urls' import ExportLogsBtn from 'components/ExportLogsBtn' @@ -52,10 +51,7 @@ class SectionHelp extends PureComponent { > - + { - 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 const cleanUpBeforeClosingSync = () => { - rimraf.sync(resolveLogsDirectory(), { disableGlob: true }) rimraf.sync(path.resolve(resolveUserDataDirectory(), 'sqlite/*.log')) } diff --git a/src/internals/index.js b/src/internals/index.js index a35995b8..cb86c75c 100644 --- a/src/internals/index.js +++ b/src/internals/index.js @@ -2,12 +2,15 @@ import '@babel/polyfill' import commands from 'commands' import logger from 'logger' +import LoggerTransport from 'logger/logger-transport-internal' import uuid from 'uuid/v4' import { setImplementation } from 'api/network' import sentry from 'sentry/node' import { EXPERIMENTAL_HTTP_ON_RENDERER } from 'config/constants' import { serializeError } from 'helpers/errors' +logger.add(new LoggerTransport()) + require('../env') process.title = 'Ledger Live Internal' diff --git a/src/logger/logger-transport-internal.js b/src/logger/logger-transport-internal.js new file mode 100644 index 00000000..79682b96 --- /dev/null +++ b/src/logger/logger-transport-internal.js @@ -0,0 +1,16 @@ +import Transport from 'winston-transport' + +export default class InternalTransport extends Transport { + log(info, callback) { + setImmediate(() => { + this.emit('logged', info) + }) + + process.send({ + type: 'log', + log: info, + }) + + callback() + } +} diff --git a/src/logger/logger-transport-main.js b/src/logger/logger-transport-main.js new file mode 100644 index 00000000..bcf1df84 --- /dev/null +++ b/src/logger/logger-transport-main.js @@ -0,0 +1,17 @@ +import Transport from 'winston-transport' + +export default class MainTransport extends Transport { + logs = [] + capacity = 2000 + + log(info, callback) { + setImmediate(() => { + this.emit('logged', info) + }) + + this.logs.unshift(info) + this.logs.splice(this.capacity) + + callback() + } +} diff --git a/src/logger/logger-transport-renderer.js b/src/logger/logger-transport-renderer.js new file mode 100644 index 00000000..91849842 --- /dev/null +++ b/src/logger/logger-transport-renderer.js @@ -0,0 +1,15 @@ +import Transport from 'winston-transport' + +export default class RendererTransport extends Transport { + ipcRenderer = require('electron').ipcRenderer + + log(info, callback) { + setImmediate(() => { + this.emit('logged', info) + }) + + this.ipcRenderer.send('log', { log: info }) + + callback() + } +} diff --git a/src/logger/logger.js b/src/logger/logger.js index 821f1b90..04e161cb 100644 --- a/src/logger/logger.js +++ b/src/logger/logger.js @@ -2,7 +2,6 @@ import winston from 'winston' import Transport from 'winston-transport' -import { resolveLogsDirectory } from 'helpers/log' import anonymizer from 'helpers/anonymizer' import pname from 'helpers/pname' @@ -18,66 +17,17 @@ import { DEBUG_ANALYTICS, } from 'config/constants' -require('winston-daily-rotate-file') - const { format } = winston const { combine, json, timestamp } = format -let logIndex = 0 - const pinfo = format(info => { - info.pname = pname - info.index = logIndex++ + if (!info.pname) { + info.pname = pname + } return info }) -function createDailyRotateFile(processName) { - return new winston.transports.DailyRotateFile({ - dirname: resolveLogsDirectory(), - json: true, - filename: `ledger-live-${processName}-%DATE%.log`, - datePattern: 'YYYY-MM-DD', - maxSize: '20m', - maxFiles: '7d', - }) -} - -const transports = [createDailyRotateFile(pname)] - -const queryLogs = (processName: string, date: Date) => - new Promise((resolve, reject) => { - const dailyRotateFile = createDailyRotateFile(processName) - const options = { - from: date - 10 * 60 * 1000, - until: date, - limit: 2000, - start: 0, - order: 'desc', - } - dailyRotateFile.query(options, (err, result) => { - if (err) { - reject(err) - return - } - resolve(result) - }) - }) - -const queryAllLogs = async (date: Date = new Date()) => { - const internal = await queryLogs('internal', date) - const main = await queryLogs('main', date) - const renderer = await queryLogs('renderer', date) - const all = internal - .concat(main) - .concat(renderer) - .sort((a, b) => { - if (a.timestamp !== b.timestamp) { - return new Date(b.timestamp) - new Date(a.timestamp) - } - return b.index - a.index - }) - return all -} +const transports = [] if (process.env.NODE_ENV !== 'production' || process.env.DEV_TOOLS) { let consoleT @@ -130,6 +80,10 @@ const logger = winston.createLogger({ transports, }) +const add = (transport: *) => { + logger.add(transport) +} + const captureBreadcrumb = (breadcrumb: any) => { if (!process.env.STORYBOOK_ENV) { try { @@ -404,5 +358,9 @@ export default { } }, - queryAllLogs, + add, + + onLog: (log: *) => { + logger.log(log) + }, } diff --git a/src/main/bridge.js b/src/main/bridge.js index e302d559..8e5bd8be 100644 --- a/src/main/bridge.js +++ b/src/main/bridge.js @@ -7,9 +7,10 @@ import { ipcMain, app } from 'electron' import { ipcMainListenReceiveCommands } from 'helpers/ipc' import path from 'path' import logger from 'logger' +import LoggerTransport from 'logger/logger-transport-main' import sentry, { captureException } from 'sentry/node' import user from 'helpers/user' -import { resolveLogsDirectory, cleanUpBeforeClosingSync } from 'helpers/log' +import { cleanUpBeforeClosingSync } from 'helpers/log' import { deserializeError } from 'helpers/errors' import setupAutoUpdater, { quitAndInstall } from './autoUpdate' @@ -17,9 +18,11 @@ import { setInternalProcessPID } from './terminator' import { getMainWindow } from './app' +const loggerTransport = new LoggerTransport() +logger.add(loggerTransport) + // 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() const LEDGER_CONFIG_DIRECTORY = app.getPath('userData') let internalProcess @@ -55,7 +58,6 @@ const bootInternalProcess = () => { env: { ...process.env, IS_INTERNAL_PROCESS: 1, - LEDGER_LOGS_DIRECTORY, LEDGER_CONFIG_DIRECTORY, LEDGER_LIVE_SQLITE_PATH, INITIAL_SENTRY_ENABLED: sentryEnabled, @@ -76,6 +78,10 @@ ipcMain.on('clean-processes', () => { killInternalProcess() }) +ipcMain.on('log', (e, { log }) => { + logger.onLog(log) +}) + ipcMainListenReceiveCommands({ onUnsubscribe: requestId => { if (!internalProcess) return @@ -118,6 +124,9 @@ function handleGlobalInternalMessage(payload) { captureException(err) break } + case 'log': + logger.onLog(payload.log) + break case 'setLibcoreBusy': case 'setDeviceBusy': case 'executeHttpQueryOnRenderer': { @@ -133,6 +142,10 @@ function handleGlobalInternalMessage(payload) { } } +ipcMain.on('queryLogs', event => { + event.sender.send('logs', { logs: loggerTransport.logs }) +}) + ipcMain.on('executeHttpQueryPayload', (event, payload) => { const p = internalProcess if (!p) return diff --git a/src/renderer/init.js b/src/renderer/init.js index 0cd10074..d5df5010 100644 --- a/src/renderer/init.js +++ b/src/renderer/init.js @@ -1,6 +1,7 @@ // @flow import logger from 'logger' +import LoggerTransport from 'logger/logger-transport-renderer' import React from 'react' import { remote, webFrame } from 'electron' import { render } from 'react-dom' @@ -34,6 +35,8 @@ import AppError from 'components/AppError' import 'styles/global' +logger.add(new LoggerTransport()) + const rootNode = document.getElementById('app') const userDataDirectory = resolveUserDataDirectory() diff --git a/yarn.lock b/yarn.lock index 9afa1d4a..97920ad2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5856,11 +5856,6 @@ 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" - integrity sha1-IegLK+hYD5i0aPN5QwZisEbDStI= - cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" @@ -7807,13 +7802,6 @@ 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" - integrity sha1-DW/qGpp6uiWofP0xtuJp5E6PCvI= - 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" @@ -10740,7 +10728,7 @@ log-update@^1.0.2: ansi-escapes "^1.0.0" cli-cursor "^1.0.2" -logform@^1.6.0, logform@^1.9.0: +logform@^1.9.0: version "1.9.1" resolved "https://registry.yarnpkg.com/logform/-/logform-1.9.1.tgz#58b29d7b11c332456d7a217e17b48a13ad69d60a" integrity sha512-ZHrZE8VSf7K3xKxJiQ1aoTBp2yK+cEbFcgarsjzI3nt3nE/3O0heNSppoOQMUJVMZo/xiVwCxiXIabaZApsKNQ== @@ -11218,7 +11206,7 @@ modify-filename@^1.1.0: resolved "https://registry.yarnpkg.com/modify-filename/-/modify-filename-1.1.0.tgz#9a2dec83806fbb2d975f22beec859ca26b393aa1" integrity sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE= -moment@^2.11.2, moment@^2.21.0, moment@^2.22.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" integrity sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y= @@ -16643,26 +16631,6 @@ window-size@^0.2.0: resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" integrity sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU= -winston-compat@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/winston-compat/-/winston-compat-0.1.4.tgz#599b4ce807ffe728713ecc25ede3f6b89425b739" - integrity sha512-mMEfFsSm6GmkFF+f4/0UJtG4N1vSaczGmXLVJYmS/+u2zUaIPcw2ZRuwUg2TvVBjswgiraN+vNnAG8z4fRUZ4w== - 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" - integrity sha512-BOvmvQH2WaiexOjzj14YNHSc18IDyZJ9t4pMsbTERjpjMltoBVijM8DDJJPr2jSqELSNnbgGPBk3kDQSRgOAtQ== - 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"