From 97d2dc912892c7bdc09a8857147fe0784f514ea5 Mon Sep 17 00:00:00 2001 From: Romain Deltour Date: Mon, 9 Oct 2017 10:45:08 +0200 Subject: [PATCH] feat: write logs to a user directory Write the log file to a user-specific directory, depending on the OS: - `~/Library/Logs/DAISY Ace/ace.log` on macOS - `~\AppData\Local\DAISY Ace\Log\ace.log` on Windows - `~/.local/state/DAISY Ace/ace.log` on Linux Also augment Winston with a `logAndExit` method to propertly flush the logs to files before exitting (see winstonjs/winston#228). In the CLI, move the main processing to an `async` function and wait for a timeout when logging and exitting. Closes #50. --- package.json | 3 +- src/cli/cli.js | 85 ++++++++++-------- src/core/logger.js | 88 ++++++++++++++----- .../__tests__/__snapshots__/cli.test.js.snap | 11 ++- yarn.lock | 10 ++- 5 files changed, 131 insertions(+), 66 deletions(-) diff --git a/package.json b/package.json index b5336778..081fdf0d 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ ], "dependencies": { "axe-core": "^2.3.1", + "env-paths": "^1.0.0", "express": "^4.15.5", "express-easy-zip": "^1.1.4", "extract-zip": "^1.6.5", @@ -64,7 +65,7 @@ "tmp": "0.0.31", "unzip": "^0.1.11", "uuidv4": "^0.5.0", - "winston": "^2.3.1", + "winston": "^2.4.0", "xmldom": "^0.1.27", "xpath": "^0.0.24" }, diff --git a/src/cli/cli.js b/src/cli/cli.js index bf9fccda..c1044c5b 100755 --- a/src/cli/cli.js +++ b/src/cli/cli.js @@ -42,50 +42,63 @@ const cli = meow(` string: ['outdir', 'tempdir'], }); -logger.initLogger({verbose: cli.flags.verbose, silent: cli.flags.silent}); - -// Check that an EPUB path is specified -if (cli.input.length === 0) { - winston.error('Input required'); - cli.showHelp(1); +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); } -// Check that output directories can be overridden -let outdir = cli.flags.outdir; -if (outdir) { - if (cli.flags.subdir) { - outdir = path.join(outdir, path.parse(cli.input[0]).name); +(async function processArgs() { + logger.initLogger({ verbose: cli.flags.verbose, silent: cli.flags.silent }); + + // Check that an EPUB path is specified + if (cli.input.length === 0) { + winston.logAndExit('error', 'Input required', () => { + console.log(cli.help); + process.exit(1); + }); + await sleep(5000); + process.exit(1); } - if (!cli.flags.force) { - const overrides = ['ace.json', 'report.html', 'data', 'js'] - .map(file => path.join(outdir, file)) - .filter(fs.existsSync); - if (overrides.length > 0) { - winston.warn(`\ + + // Check that output directories can be overridden + let outdir = cli.flags.outdir; + if (outdir) { + if (cli.flags.subdir) { + outdir = path.join(outdir, path.parse(cli.input[0]).name); + } + if (!cli.flags.force) { + const overrides = ['ace.json', 'report.html', 'data', 'js'] + .map(file => path.join(outdir, file)) + .filter(fs.existsSync); + if (overrides.length > 0) { + winston.logAndExit('warn', `\ Output directory is not empty. -Running Ace would override the following files or directories: + Running Ace would override the following files or directories: ${overrides.map(file => ` - ${file}`).join('\n')} -Use option --force to override. -`); - process.exit(1); + Use option --force to override. + `, 1); + await sleep(5000); + process.exit(1); + } } } -} -// finally, invoke Ace -ace(cli.input[0], { - cwd: cli.flags.cwd || process.cwd(), - outdir, - tmpdir: cli.flags.tempdir, - verbose: cli.flags.verbose, - silent: cli.flags.silent, - jobId: '', -}) -.catch((err) => { - if (err) winston.error(err.message); - console.log(`Re-run Ace using the --verbose option to enable full debug logging.`) - process.exit(1); -}); + // finally, invoke Ace + ace(cli.input[0], { + cwd: cli.flags.cwd || process.cwd(), + outdir, + tmpdir: cli.flags.tempdir, + verbose: cli.flags.verbose, + silent: cli.flags.silent, + jobId: '', + }) + .catch((err) => { + if (err && err.message) winston.error(err.message); + winston.logAndExit('info', 'Closing logs.', () => { + console.log('Re-run Ace using the --verbose option to enable full debug logging.'); + process.exit(1); + }); + }); +}()); diff --git a/src/core/logger.js b/src/core/logger.js index d88addb0..38429bf2 100644 --- a/src/core/logger.js +++ b/src/core/logger.js @@ -1,29 +1,73 @@ 'use strict'; -const winston = require('winston'); +const envPaths = require('env-paths'); const fs = require('fs-extra'); -const LOGFILE = __dirname + "/../ace.log"; +const path = require('path'); +const winston = require('winston'); -module.exports = { - initLogger: function(options) { - // clear old log file - fs.removeSync(LOGFILE); +const acePaths = envPaths('DAISY Ace', { suffix: null }); - // set up logger - var level = 'info'; - if (options.verbose) { - level = 'verbose'; - } - winston.configure({ - level: level, - transports: [ - new (winston.transports.File)({name: "file", filename: LOGFILE}), - new (winston.transports.Console)({name: "console"}) - ] +module.exports.initLogger = function initLogger(options = {}) { + // Check logging directoy exists + if (!fs.existsSync(acePaths.log)) { + fs.mkdirSync(acePaths.log); + } + + // OS-dependant path to log file + const logfile = path.join(acePaths.log, 'ace.log'); + + // clear old log file + if (fs.existsSync(logfile)) { + fs.removeSync(logfile); + } + + // set up logger + const level = (options.verbose) ? 'verbose' : 'info'; + winston.configure({ + level, + transports: [ + new winston.transports.File({ name: 'file', filename: logfile }), + new winston.transports.Console({ name: 'console' }), + ], + }); + if (options.silent) { + winston.remove('console'); + } + winston.cli(); +}; + +/* eslint-disable no-underscore-dangle */ +// Properly wait that loggers write to file before exitting +// See https://github.com/winstonjs/winston/issues/228 +winston.logAndExit = function logAndExit(level, msg, codeOrCallback) { + const self = this; + this.log(level, msg, () => { + let numFlushes = 0; + let numFlushed = 0; + Object.keys(self.default.transports).forEach((k) => { + if (self.default.transports[k]._stream) { + numFlushes += 1; + self.default.transports[k]._stream.once('finish', () => { + numFlushed += 1; + if (numFlushes === numFlushed) { + if (typeof codeOrCallback === 'function') { + codeOrCallback(); + } else { + process.exit(codeOrCallback); + } + } + }); + self.default.transports[k]._stream.end(); + } }); - if (options.silent) { - winston.remove("console"); + if (numFlushes === 0) { + if (typeof codeOrCallback === 'function') { + codeOrCallback(); + } else { + process.exit(codeOrCallback); + } } - winston.cli(); - } -} + }); +}; +/* eslint-enable no-underscore-dangle */ + diff --git a/tests/__tests__/__snapshots__/cli.test.js.snap b/tests/__tests__/__snapshots__/cli.test.js.snap index 62167452..47b161fe 100644 --- a/tests/__tests__/__snapshots__/cli.test.js.snap +++ b/tests/__tests__/__snapshots__/cli.test.js.snap @@ -3,14 +3,14 @@ exports[`test existing output 1`] = ` "warn: Output directory is not empty. -Running Ace would override the following files or directories: + Running Ace would override the following files or directories: - report/ace.json - report/report.html - report/js -Use option --force to override. - + Use option --force to override. + " `; @@ -64,7 +64,10 @@ exports[`test no input 2`] = ` " `; -exports[`test unexisting input 1`] = `"Re-run Ace using the --verbose option to enable full debug logging."`; +exports[`test unexisting input 1`] = ` +"info: Closing logs. +Re-run Ace using the --verbose option to enable full debug logging." +`; exports[`test unexisting input 2`] = ` "error: Couldn’t find EPUB file 'unexisting.epub' diff --git a/yarn.lock b/yarn.lock index 4960bc6a..c2df50fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1377,6 +1377,10 @@ enqueue@^1.0.2: dependencies: sliced "0.0.5" +env-paths@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0" + err-code@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960" @@ -4565,9 +4569,9 @@ window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" -winston@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/winston/-/winston-2.3.1.tgz#0b48420d978c01804cf0230b648861598225a119" +winston@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.0.tgz#808050b93d52661ed9fb6c26b3f0c826708b0aee" dependencies: async "~1.0.0" colors "1.0.x"