From c90765e10d9e37bb7317316c716697e89ef54464 Mon Sep 17 00:00:00 2001 From: Pierre Vanduynslager Date: Sun, 29 Oct 2017 01:50:24 -0400 Subject: [PATCH] fix: Include Error properties in logs --- src/cli.js | 2 +- src/lib/logger.js | 11 ++++++++--- test/fixtures/plugin-error-a.js | 4 +++- test/integration.test.js | 4 ++++ test/logger.test.js | 27 +++++++++++++++++++-------- 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/cli.js b/src/cli.js index e21c8785..cf969b24 100755 --- a/src/cli.js +++ b/src/cli.js @@ -52,7 +52,7 @@ module.exports = async () => { logger.log(`%s ${err.message}`, err.code); } else { process.exitCode = 1; - logger.error(err); + logger.error('An error occurred while running semantic-release: %O', err); } } }; diff --git a/src/lib/logger.js b/src/lib/logger.js index 9116e720..6990398e 100644 --- a/src/lib/logger.js +++ b/src/lib/logger.js @@ -6,13 +6,18 @@ const chalk = require('chalk'); module.exports = { log(...args) { const [format, ...rest] = args; - console.log(`${chalk.grey('[Semantic release]:')} ${format}`, ...rest.map(arg => chalk.magenta(arg))); + console.log( + `${chalk.grey('[Semantic release]:')}${typeof format === 'string' + ? ` ${format.replace(/%[^%]/g, seq => chalk.magenta(seq))}` + : ''}`, + ...(typeof format === 'string' ? [] : [format]).concat(rest) + ); }, error(...args) { const [format, ...rest] = args; console.error( - `${chalk.grey('[Semantic release]:')} ${chalk.red(format instanceof Error ? format.stack : format)}`, - ...rest.map(arg => chalk.red(arg instanceof Error ? arg.stack : arg)) + `${chalk.grey('[Semantic release]:')}${typeof format === 'string' ? ` ${chalk.red(format)}` : ''}`, + ...(typeof format === 'string' ? [] : [format]).concat(rest) ); }, }; diff --git a/test/fixtures/plugin-error-a.js b/test/fixtures/plugin-error-a.js index 27c8eaf7..2a4fe3de 100644 --- a/test/fixtures/plugin-error-a.js +++ b/test/fixtures/plugin-error-a.js @@ -1,3 +1,5 @@ module.exports = function(config, options, cb) { - cb(new Error('a')); + const error = new Error('a'); + error.errorProperty = 'errorProperty'; + cb(error); }; diff --git a/test/integration.test.js b/test/integration.test.js index cbc6a5ff..d541497c 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -634,8 +634,12 @@ test.serial('Log unexpected errors from plugins and exit with 1', async t => { await gitCommits(['feat: Initial commit']); t.log('$ semantic-release'); let {stderr, code} = await execa(cli, [], {env, reject: false}); + // Verify the type and message are logged t.regex(stderr, /Error: a/); + // Verify the the stacktrace is logged t.regex(stderr, new RegExp(pluginError)); + // Verify the Error properties are logged + t.regex(stderr, /errorProperty: 'errorProperty'/); t.is(code, 1); }); diff --git a/test/logger.test.js b/test/logger.test.js index 9b6fbfb1..3ea564cc 100644 --- a/test/logger.test.js +++ b/test/logger.test.js @@ -1,5 +1,5 @@ import test from 'ava'; -import {stub} from 'sinon'; +import {stub, match} from 'sinon'; import logger from '../src/lib/logger'; test.beforeEach(t => { @@ -20,18 +20,29 @@ test.serial('Basic log', t => { t.true(t.context.error.calledWithMatch(/.*test error/)); }); +test.serial('Log object', t => { + const obj = {a: 1, b: '2'}; + logger.log(obj); + logger.error(obj); + + t.true(t.context.log.calledWithMatch(match.string, obj)); + t.true(t.context.error.calledWithMatch(match.string, obj)); +}); + test.serial('Log with string formatting', t => { logger.log('test log %s', 'log value'); logger.error('test error %s', 'error value'); - t.true(t.context.log.calledWithMatch(/.*test log %s/, 'log value')); - t.true(t.context.error.calledWithMatch(/.*test error %s/, 'error value')); + t.true(t.context.log.calledWithMatch(/.*test log/, 'log value')); + t.true(t.context.error.calledWithMatch(/.*test error/, 'error value')); }); -test.serial('Log with error stacktrace', t => { - logger.error(new Error('error message')); - logger.error('test error %s', new Error('other error message')); +test.serial('Log with error stacktrace and properties', t => { + const error = new Error('error message'); + logger.error(error); + const otherError = new Error('other error message'); + logger.error('test error %O', otherError); - t.true(t.context.error.calledWithMatch(/.*test error %s/, /Error: other error message(\s|.)*?logger\.test\.js/)); - t.true(t.context.error.calledWithMatch(/Error: error message(\s|.)*?logger\.test\.js/)); + t.true(t.context.error.calledWithMatch(match.string, error)); + t.true(t.context.error.calledWithMatch(/.*test error/, otherError)); });