const path = require('path'); const fs = require('fs'); const electron = require('electron') const bunyan = require('bunyan'); const mkdirp = require('mkdirp'); const _ = require('lodash'); const app = electron.app; const ipc = electron.ipcMain; const LEVELS = ['fatal', 'error', 'warn', 'info', 'debug', 'trace']; let logger; function dropFirst(args) { return Array.prototype.slice.call(args, 1); } function initialize() { if (logger) { throw new Error('Already called initialize!'); } const basePath = app.getPath('userData'); const logPath = path.join(basePath, 'logs'); mkdirp.sync(logPath); const logFile = path.join(logPath, 'log.log'); logger = bunyan.createLogger({ name: 'log', streams: [{ level: 'debug', stream: process.stdout }, { type: 'rotating-file', path: logFile, period: '1d', count: 3 }] }); LEVELS.forEach(function(level) { ipc.on('log-' + level, function() { // first parameter is the event, rest are provided arguments var args = dropFirst(arguments); logger[level].apply(logger, args); }); }); ipc.on('fetch-log', function(event) { fetch(logPath).then(function(data) { event.sender.send('fetched-log', data); }, function(error) { logger.error('Problem loading log from disk: ' + error.stack); }); }); } function getLogger() { if (!logger) { throw new Error('Logger hasn\'t been initialized yet!'); } return logger; } function fetchLog(logFile) { return new Promise(function(resolve, reject) { fs.readFile(logFile, { encoding: 'utf8' }, function(err, text) { if (err) { return reject(err); } const lines = _.compact(text.split('\n')); const data = _.compact(lines.map(function(line) { try { return _.pick(JSON.parse(line), ['level', 'time', 'msg']); } catch (e) {} })); return resolve(data); }); }); } function fetch(logPath) { const files = fs.readdirSync(logPath); const paths = files.map(function(file) { return path.join(logPath, file) }); return Promise.all(paths.map(fetchLog)).then(function(results) { const data = _.flatten(results); return _.sortBy(data, 'time'); }); } function logAtLevel() { const level = arguments[0]; const args = Array.prototype.slice.call(arguments, 1); if (logger) { // To avoid [Object object] in our log since console.log handles non-strings smoothly const str = args.map(function(item) { if (typeof item !== 'string') { try { return JSON.stringify(item); } catch (e) { return item; } } return item; }); logger[level](str.join(' ')); } else { console._log.apply(console, consoleArgs); } } console._log = console.log; console.log = _.partial(logAtLevel, 'info'); console._error = console.error; console.error = _.partial(logAtLevel, 'error'); console._warn = console.warn; console.warn = _.partial(logAtLevel, 'warn'); module.exports = { initialize, getLogger, };