diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 00000000..65e2cc34 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1 @@ +// intentionally blank diff --git a/src-electron/main-process/electron-main.js b/src-electron/main-process/electron-main.js index 1fb7d16c..fbcc8cab 100644 --- a/src-electron/main-process/electron-main.js +++ b/src-electron/main-process/electron-main.js @@ -26,7 +26,11 @@ let installUpdate = false; const title = `${productName} v${version}`; -const selectionMenu = Menu.buildFromTemplate([{ role: "copy" }, { type: "separator" }, { role: "selectall" }]); +const selectionMenu = Menu.buildFromTemplate([ + { role: "copy" }, + { type: "separator" }, + { role: "selectall" } +]); const inputMenu = Menu.buildFromTemplate([ { role: "cut" }, @@ -54,7 +58,13 @@ function createWindow() { minWidth: 640, minHeight: 480, icon: require("path").join(__statics, "icon_512x512.png"), - title + title, + webPreferences: { + nodeIntegration: true, + nodeIntegrationInWorker: true, + // anything we want preloaded, e.g. global vars + preload: path.resolve(__dirname, "electron-preload.js") + } }); mainWindow.on("close", e => { diff --git a/src-electron/main-process/electron-preload.js b/src-electron/main-process/electron-preload.js new file mode 100644 index 00000000..33beefd3 --- /dev/null +++ b/src-electron/main-process/electron-preload.js @@ -0,0 +1,3 @@ +const path = require("upath"); + +require(path.resolve(__dirname, "logging.js")); diff --git a/src-electron/main-process/logging.js b/src-electron/main-process/logging.js new file mode 100644 index 00000000..6f63a69e --- /dev/null +++ b/src-electron/main-process/logging.js @@ -0,0 +1,78 @@ +// This allows for logging (to a file) from the frontend by creating global logging functions +// That send ipc calls (from renderer) to the electron main process +// create global logging functions for the frontend. It sends messages to the main +// process which then log to file + +const electron = require("electron"); + +const _ = require("lodash"); + +const ipc = electron.ipcRenderer; + +function log(...args) { + logAtLevel("info", "INFO ", ...args); +} + +if (window.console) { + console._log = console.log; + console.log = log; + console._trace = console.trace; + console._debug = console.debug; + console._info = console.info; + console._warn = console.warn; + console._error = console.error; + console._fatal = console.error; +} + +// To avoid [Object object] in our log since console.log handles non-strings +// smoothly +function cleanArgsForIPC(args) { + const str = args.map(item => { + if (typeof item !== "string") { + try { + return JSON.stringify(item); + } catch (error) { + return item; + } + } + + return item; + }); + + return str.join(" "); +} + +// Backwards-compatible logging, simple strings and no level (defaulted to INFO) +function now() { + const date = new Date(); + return date.toJSON(); +} + +// The Bunyan API: https://github.com/trentm/node-bunyan#log-method-api +function logAtLevel(level, prefix, ...args) { + const fn = `_${level}`; + console[fn](prefix, now(), ...args); + + const logText = cleanArgsForIPC(args); + ipc.send(`log-${level}`, logText); +} + +window.log = { + fatal: _.partial(logAtLevel, "fatal", "FATAL"), + error: _.partial(logAtLevel, "error", "ERROR"), + warn: _.partial(logAtLevel, "warn", "WARN "), + info: _.partial(logAtLevel, "info", "INFO "), + debug: _.partial(logAtLevel, "debug", "DEBUG"), + trace: _.partial(logAtLevel, "trace", "TRACE") +}; + +window.onerror = (message, script, line, col, error) => { + const errorInfo = error && error.stack ? error.stack : JSON.stringify(error); + window.log.error(`Top-level unhandled error: ${errorInfo}`); +}; + +window.addEventListener("unhandledrejection", rejectionEvent => { + const error = rejectionEvent.reason; + const errorInfo = error && error.stack ? error.stack : error; + window.log.error("Top-level unhandled promise rejection:", errorInfo); +}); diff --git a/src-electron/main-process/modules/backend.js b/src-electron/main-process/modules/backend.js index 5c12c2eb..1212bd64 100644 --- a/src-electron/main-process/modules/backend.js +++ b/src-electron/main-process/modules/backend.js @@ -8,11 +8,16 @@ import { version } from "../../../package.json"; const bunyan = require("bunyan"); const WebSocket = require("ws"); +const electron = require("electron"); const os = require("os"); const fs = require("fs-extra"); const path = require("upath"); const objectAssignDeep = require("object-assign-deep"); +const { ipcMain: ipc } = electron; + +const LOG_LEVELS = ["fatal", "error", "warn", "info", "debug", "trace"]; + export class Backend { constructor(mainWindow) { this.mainWindow = mainWindow; @@ -355,12 +360,20 @@ export class Backend { name: "log", streams: [ { - level: "debug", - path: path.join(logPath, "electron.log") + type: "rotating-file", + path: path.join(logPath, "electron.log"), + period: "1d", // daily rotation + count: 4 // keep 4 days of logs } ] }); + LOG_LEVELS.forEach(level => { + ipc.on(`log-${level}`, (first, ...rest) => { + log[level](...rest); + }); + }); + this.log = log; process.on("uncaughtException", error => {