From ea2de65e9ed76b04daca05eeba7adadb20f59fb3 Mon Sep 17 00:00:00 2001 From: Paul Varache Date: Thu, 8 Mar 2018 11:09:17 +0000 Subject: [PATCH] Add Logger. Update menu transform option --- README.md | 52 ++++++++++++++------ app/main.js | 45 +++++++++++------- lib/index.js | 2 + lib/logger.js | 73 ++++++++++++++++++++++++++++ lib/menu/index.js | 32 ++++++++++--- lib/shell/index.js | 10 +++- package-lock.json | 116 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 + 8 files changed, 293 insertions(+), 39 deletions(-) create mode 100644 lib/logger.js diff --git a/README.md b/README.md index 4f03bd9..c281bd0 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ ShellMenu.addItem('my-item', { label: 'My Menu Item', click() { console.log('My // Includes automatic window state managment and UWP style titlebar for windows // Custom preload as module supported const shell = new Shell({ + name: 'My App', root: CONTENT_ROOT, scheme: CONTENT_SCHEME, // Can provide custom width and height @@ -81,29 +82,50 @@ const shell = new Shell({ // Enable UWP style titlebar uwpTitlebar: true, devMode: false, - menuTransform(menu) { - const submenu = [ShellMenu.createMenuItem('my-item')]; - // Can update when dev mode changes - if (shell.isDevMode()) { - submenu.push(ShellMenu.createMenuItem('separator')); - // Can add custom runtime generated menu items - submenu.push({ label: 'Dev option' }); - } - // Inject in list of menus - menu.splice(1, 0, { - label: 'Custom Menu', - submenu, - }); - return menu; + menu: { + transform(menu) { + const submenu = [ShellMenu.createMenuItem('my-item')]; + // Can update when dev mode changes + if (shell.isDevMode()) { + submenu.push(ShellMenu.createMenuItem('separator')); + // Can add custom runtime generated menu items + submenu.push({ label: 'Dev option' }); + } + // Inject in list of menus + menu.splice(1, 0, { + label: 'Custom Menu', + submenu, + }); + return menu; + }, }, - // Set custom window options through this object windowOptions: { icon: path.join(__dirname, 'res/icon_180.png'), }, + // Log options allows to customize logging + log: { + // Default output lod level + level: 'debug', + // File options. Log files are located in the app.getPath('userData') directory + file: { + // Options passed down to bunyan 'rotating-file' + period: '12h', + count: 14, + }, + // options applied once devMode is enabled + devMode: { + level: 'debug', + file: { + level: 'debug', + }, + } + }, }); app.on('ready', () => { shell.createWindow(); + // Logger can be found here + const { log } = shell; // The main window is accessible through properties console.log(shell.window); }); diff --git a/app/main.js b/app/main.js index ce148ab..5e92dce 100644 --- a/app/main.js +++ b/app/main.js @@ -8,12 +8,11 @@ const CONTENT_ROOT = path.join(__dirname, './src'); // Custom menu Item added ShellMenu.addItem('my-item', { label: 'My Menu Item', click() { console.log('My Menu Item'); } }); -app.setName('My app'); - // Create new Shell with custom options // Includes automatic window state managment and UWP style titlebar for windows // Custom preload as module supported const shell = new Shell({ + name: 'My App', root: CONTENT_ROOT, scheme: CONTENT_SCHEME, // Can provide custom width and height @@ -25,28 +24,40 @@ const shell = new Shell({ // Enable UWP style titlebar uwpTitlebar: true, devMode: false, - menuTransform(menu) { - const submenu = [ShellMenu.createMenuItem('my-item')]; - // Can update when dev mode changes - if (shell.isDevMode()) { - submenu.push(ShellMenu.createMenuItem('separator')); - // Can add custom runtime generated menu items - submenu.push({ label: 'Dev option' }); - } - // Inject in list of menus - menu.splice(1, 0, { - label: 'Custom Menu', - submenu, - }); - return menu; + menu: { + transform(menu) { + const submenu = [ShellMenu.createMenuItem('my-item')]; + // Can update when dev mode changes + if (shell.isDevMode()) { + submenu.push(ShellMenu.createMenuItem('separator')); + // Can add custom runtime generated menu items + submenu.push({ label: 'Dev option' }); + } + // Inject in list of menus + menu.splice(1, 0, { + label: 'Custom Menu', + submenu, + }); + return menu; + }, }, windowOptions: { icon: path.join(__dirname, 'res/icon_180.png'), }, + log: { + level: 'info', + file: { + period: '1d', + count: 7, + }, + }, }); app.on('ready', () => { shell.createWindow(); + // Can access logger through the shell object + const { log } = shell; + log.info('My has started'); // The main window is accessible through properties - console.log(shell.window); + // console.log(shell.window); }); diff --git a/lib/index.js b/lib/index.js index efbace4..d0612fc 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,7 +1,9 @@ const Shell = require('./shell'); const ShellMenu = require('./menu'); +const Logger = require('./logger'); module.exports = { Shell, ShellMenu, + Logger, }; diff --git a/lib/logger.js b/lib/logger.js new file mode 100644 index 0000000..103234c --- /dev/null +++ b/lib/logger.js @@ -0,0 +1,73 @@ +const path = require('path'); +const bunyan = require('bunyan'); +const bunyanDebugStream = require('bunyan-debug-stream'); +const { app } = require('electron'); + +const loggers = new Map(); + +const defaultOptions = { + level: 'info', + file: { + level: 'warn', + period: '1d', + count: 7, + }, + devMode: { + level: 'debug', + file: { + level: 'debug', + }, + }, +}; + +const Logger = { + getLogger(opts, name = null) { + const loggerName = name || app.getName(); + const fileOptions = Object.assign({}, defaultOptions.file, opts.file || {}); + const options = Object.assign({}, defaultOptions, opts); + if (!loggers.has(loggerName)) { + const streams = [{ + level: options.level, + type: 'raw', + stream: bunyanDebugStream(), + name: 'stream', + }]; + // Setting the file property to false will disable it + if (opts.file !== false) { + streams.push({ + level: fileOptions.level, + type: 'rotating-file', + path: path.join(app.getPath('userData'), `${loggerName}.log`), + name: 'file', + period: fileOptions.period, + count: fileOptions.count, + }); + } + const logger = bunyan.createLogger({ + name: loggerName, + streams, + serializers: bunyanDebugStream.serializers, + }); + loggers.set(loggerName, logger); + } + return loggers.get(loggerName); + }, + enableDevMode(opts, name = null) { + const loggerName = name || app.getName(); + if (!loggers.has(loggerName)) { + return; + } + const level = opts.devMode + && opts.devMode.level + ? opts.devMode.level : defaultOptions.devMode.level; + const fileLevel = opts.devMode + && opts.devMode.file + && opts.devMode.file.level + ? opts.devMode.file.level : defaultOptions.devMode.file.level; + const logger = loggers.get(loggerName); + logger.levels('stream', level); + logger.levels('file', fileLevel); + }, +}; + +module.exports = Logger; diff --git a/lib/menu/index.js b/lib/menu/index.js index 4540e61..725f063 100644 --- a/lib/menu/index.js +++ b/lib/menu/index.js @@ -1,4 +1,4 @@ -const { app, Menu } = require('electron'); +const { app, shell, Menu } = require('electron'); const ITEMS = { about: { @@ -42,13 +42,15 @@ const ITEMS = { toggledevtools: { role: 'toggledevtools' }, }; +const defaultOptions = { + transform(menu) { + return menu; + }, +}; + class ShellMenu { constructor(window, about, options) { - this.options = Object.assign({ - transform(menu) { - return menu; - }, - }, options); + this.options = Object.assign({}, defaultOptions, options); this.window = window; /* Menus for context menus */ @@ -129,7 +131,25 @@ class ShellMenu { submenu: [], }; + if (debug) { + help.submenu.push({ + label: 'Open logs directory', + click: () => { + let stream; + for (let i = 0; i < this.window.shell.log.streams.length; i += 1) { + stream = this.window.shell.log.streams[i]; + if (stream.name === 'file') { + shell.showItemInFolder(stream.path); + break; + } + } + }, + }); + } if (process.platform !== 'darwin') { + if (help.submenu.length) { + help.submenu.push(ShellMenu.createMenuItem('separator')); + } help.submenu.push(this.aboutItem); } diff --git a/lib/shell/index.js b/lib/shell/index.js index 6f27e9c..2348b6e 100644 --- a/lib/shell/index.js +++ b/lib/shell/index.js @@ -9,6 +9,7 @@ const About = require('../about'); const MenuGenerator = require('../menu'); const path = require('path'); const windowStateKeeper = require('electron-window-state'); +const Logger = require('../logger'); const defaultOptions = { scheme: 'shell', @@ -21,6 +22,7 @@ const defaultOptions = { show: false, autoHideMenuBar: true, }, + name: 'Kano Desktop Shell', }; class Shell { @@ -29,6 +31,8 @@ class Shell { if (!this.options.root) { throw new Error('Missing root option'); } + app.setName(this.options.name); + this.log = Logger.getLogger(this.options.log || {}); protocol.registerStandardSchemes([this.options.scheme], { secure: true }); app.on('ready', () => { appServer.registerProtocol(this.options.scheme, this.options.root); @@ -40,9 +44,13 @@ class Shell { this.about.on('enable-dev-mode', () => { this.options.devMode = true; this.updateMenu(); + Logger.enableDevMode(this.options.log || {}); + this.log.debug('Developer mode enabled'); }); + this.log.level(this.options.logLevel); } createWindow() { + this.log.debug('Creating new window'); this.windowState = windowStateKeeper({ defaultWidth: this.options.width, defaultHeight: this.options.height, @@ -63,7 +71,7 @@ class Shell { this.menuGenerator = new MenuGenerator( this.window, this.about, - { transform: this.options.menuTransform }, + this.options.menu || {}, ); this.updateMenu(); diff --git a/package-lock.json b/package-lock.json index 1f44ae9..ed1db6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -188,6 +188,26 @@ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" }, + "bunyan": { + "version": "1.8.12", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", + "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", + "requires": { + "dtrace-provider": "0.8.6", + "moment": "2.21.0", + "mv": "2.1.1", + "safe-json-stringify": "1.1.0" + } + }, + "bunyan-debug-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/bunyan-debug-stream/-/bunyan-debug-stream-1.0.8.tgz", + "integrity": "sha1-32EoUtXQttbfPzAhTYp+TuklEG0=", + "requires": { + "colors": "1.1.2", + "exception-formatter": "1.0.5" + } + }, "caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", @@ -305,6 +325,11 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=" + }, "combined-stream": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", @@ -514,6 +539,15 @@ "domelementtype": "1.3.0" } }, + "dtrace-provider": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.6.tgz", + "integrity": "sha1-QooiOv4DQl0s1tY0f99AxmkDVj0=", + "optional": true, + "requires": { + "nan": "2.9.2" + } + }, "ecc-jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", @@ -894,6 +928,14 @@ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, + "exception-formatter": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/exception-formatter/-/exception-formatter-1.0.5.tgz", + "integrity": "sha1-valXMZeJy6vfNoSPtSiMWWNLc6U=", + "requires": { + "colors": "1.1.2" + } + }, "extend": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", @@ -1606,6 +1648,12 @@ } } }, + "moment": { + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.21.0.tgz", + "integrity": "sha512-TCZ36BjURTeFTM/CwRcViQlfkMvL1/vFISuNLO5GkcVm1+QHfbSiNqZuWeMFjj1/3+uAjXswgRk30j1kkLYJBQ==", + "optional": true + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1617,12 +1665,74 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, + "mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "optional": true, + "requires": { + "mkdirp": "0.5.1", + "ncp": "2.0.0", + "rimraf": "2.4.5" + }, + "dependencies": { + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "optional": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "optional": true, + "requires": { + "glob": "6.0.4" + } + } + } + }, + "nan": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.9.2.tgz", + "integrity": "sha512-ltW65co7f3PQWBDbqVvaU1WtFJUsNW7sWWm4HINhbMQIyVyzIeyZ8toX5TC5eeooE6piZoaEh4cZkueSKG3KYw==", + "optional": true + }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", + "optional": true + }, "normalize-package-data": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", @@ -5956,6 +6066,12 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, + "safe-json-stringify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.1.0.tgz", + "integrity": "sha512-EzBtUaFH9bHYPc69wqjp0efJI/DPNHdFbGE3uIMn4sVbO0zx8vZ8cG4WKxQfOpUOKsQyGBiT2mTqnCw+6nLswA==", + "optional": true + }, "semver": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", diff --git a/package.json b/package.json index 1dbdd41..68f917f 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ "author": "", "license": "ISC", "dependencies": { + "bunyan": "^1.8.12", + "bunyan-debug-stream": "^1.0.8", "electron": "^1.8.2", "electron-window-state": "^4.1.1", "mime-types": "^2.1.18",