From 4d9bc2f121e7d02a636490c7ec86cce2f3ffb38c Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 21 Aug 2018 17:09:27 -0700 Subject: [PATCH] [devUtils/toolingLog] give tooling log configurable writers (#22110) * [devUtils/toolingLog] give tooling log configurable writers * property shorthand * remove redundant parameter * call Error.captureStackTrace when subclassing Error * describe why we skip stack trace logging for CliError * always return true/false from log writers * improve type definitions, writeTo is just an object with write method * get rid of weird dedent for failures --- packages/kbn-dev-utils/index.d.ts | 19 +- packages/kbn-dev-utils/src/index.js | 2 +- .../proc_runner/__tests__/with_proc_runner.js | 4 +- .../kbn-dev-utils/src/proc_runner/proc.js | 2 +- .../__snapshots__/log_levels.test.js.snap | 91 +++++++ .../__snapshots__/tooling_log.test.js.snap | 226 ++++++++++++++++++ .../tooling_log_text_writer.test.js.snap | 178 ++++++++++++++ .../src/tooling_log/__tests__/log.js | 99 -------- .../src/tooling_log/__tests__/log_levels.js | 114 --------- .../kbn-dev-utils/src/tooling_log/index.d.ts | 22 ++ .../kbn-dev-utils/src/tooling_log/index.js | 3 +- .../src/tooling_log/log_levels.d.ts | 29 +++ .../src/tooling_log/log_levels.test.js | 35 +++ .../src/tooling_log/tooling_log.d.ts | 45 ++++ .../src/tooling_log/tooling_log.js | 154 ++++++------ .../src/tooling_log/tooling_log.test.js | 143 +++++++++++ .../tooling_log/tooling_log_text_writer.d.ts | 42 ++++ .../tooling_log/tooling_log_text_writer.js | 92 +++++++ .../tooling_log_text_writer.test.js | 98 ++++++++ packages/kbn-dev-utils/tsconfig.json | 3 +- .../src/integration_tests/cluster.test.js | 6 +- packages/kbn-es/src/utils/log.js | 8 +- .../functional_tests/cli/run_tests/args.js | 9 +- .../cli/start_servers/args.js | 9 +- .../src/functional_tests/lib/run_cli.js | 35 ++- .../src/functional_tests/lib/run_ftr.js | 45 ++-- .../kbn-test/src/functional_tests/tasks.js | 13 +- src/dev/build/cli.js | 8 +- src/dev/build/lib/__tests__/exec.js | 12 +- src/dev/build/lib/__tests__/runner.js | 10 +- .../build/tasks/nodejs/__tests__/download.js | 10 +- src/dev/notice/cli.js | 8 +- src/dev/run/run.js | 8 +- src/dev/tslint/run_tslint_cli.ts | 8 +- src/dev/typescript/run_type_check_cli.ts | 8 +- src/es_archiver/cli.js | 8 +- src/es_archiver/lib/__tests__/stats.js | 79 +++--- src/functional_test_runner/cli.js | 8 +- .../lib/config/__tests__/read_config_file.js | 4 +- .../lib/mocha/reporter/reporter.js | 16 +- .../create_or_upgrade_integration.js | 8 +- .../routes/__tests__/lib/servers.js | 8 +- tasks/functional_test_runner.js | 8 +- x-pack/gulpfile.js | 8 +- 44 files changed, 1271 insertions(+), 474 deletions(-) create mode 100644 packages/kbn-dev-utils/src/tooling_log/__snapshots__/log_levels.test.js.snap create mode 100644 packages/kbn-dev-utils/src/tooling_log/__snapshots__/tooling_log.test.js.snap create mode 100644 packages/kbn-dev-utils/src/tooling_log/__snapshots__/tooling_log_text_writer.test.js.snap delete mode 100644 packages/kbn-dev-utils/src/tooling_log/__tests__/log.js delete mode 100644 packages/kbn-dev-utils/src/tooling_log/__tests__/log_levels.js create mode 100644 packages/kbn-dev-utils/src/tooling_log/index.d.ts create mode 100644 packages/kbn-dev-utils/src/tooling_log/log_levels.d.ts create mode 100644 packages/kbn-dev-utils/src/tooling_log/log_levels.test.js create mode 100644 packages/kbn-dev-utils/src/tooling_log/tooling_log.d.ts create mode 100644 packages/kbn-dev-utils/src/tooling_log/tooling_log.test.js create mode 100644 packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.d.ts create mode 100644 packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.js create mode 100644 packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.test.js diff --git a/packages/kbn-dev-utils/index.d.ts b/packages/kbn-dev-utils/index.d.ts index efd26bfea07b..9d8fd8889b7e 100644 --- a/packages/kbn-dev-utils/index.d.ts +++ b/packages/kbn-dev-utils/index.d.ts @@ -17,21 +17,4 @@ * under the License. */ -import { Readable } from 'stream'; - -type LogLevel = 'silent' | 'error' | 'warning' | 'info' | 'debug' | 'verbose'; - -export class ToolingLog extends Readable { - public verbose(...args: any[]): void; - public debug(...args: any[]): void; - public info(...args: any[]): void; - public success(...args: any[]): void; - public warning(...args: any[]): void; - public error(errOrMsg: string | Error): void; - public write(...args: any[]): void; - public indent(spaces: number): void; - public getLevel(): LogLevel; - public setLevel(level: LogLevel): void; -} - -export function createToolingLog(level?: LogLevel): ToolingLog; +export * from './src/tooling_log'; diff --git a/packages/kbn-dev-utils/src/index.js b/packages/kbn-dev-utils/src/index.js index 56e74e59cef4..c8c3d28aa357 100644 --- a/packages/kbn-dev-utils/src/index.js +++ b/packages/kbn-dev-utils/src/index.js @@ -18,4 +18,4 @@ */ export { withProcRunner } from './proc_runner'; -export { createToolingLog, pickLevelFromFlags } from './tooling_log'; +export { ToolingLog, pickLevelFromFlags } from './tooling_log'; diff --git a/packages/kbn-dev-utils/src/proc_runner/__tests__/with_proc_runner.js b/packages/kbn-dev-utils/src/proc_runner/__tests__/with_proc_runner.js index f65d76035bb8..80029b8d93f2 100644 --- a/packages/kbn-dev-utils/src/proc_runner/__tests__/with_proc_runner.js +++ b/packages/kbn-dev-utils/src/proc_runner/__tests__/with_proc_runner.js @@ -17,7 +17,7 @@ * under the License. */ -import { createToolingLog } from '../../tooling_log'; +import { ToolingLog } from '../../tooling_log'; import { withProcRunner } from '../with_proc_runner'; describe('proc runner', () => { @@ -34,7 +34,7 @@ describe('proc runner', () => { } it('passes procs to a function', async () => { - await withProcRunner(createToolingLog(), async procs => { + await withProcRunner(new ToolingLog(), async procs => { await runProc({ procs }); await procs.stop('proc'); }); diff --git a/packages/kbn-dev-utils/src/proc_runner/proc.js b/packages/kbn-dev-utils/src/proc_runner/proc.js index 4e2d59acf7c1..f47302641810 100644 --- a/packages/kbn-dev-utils/src/proc_runner/proc.js +++ b/packages/kbn-dev-utils/src/proc_runner/proc.js @@ -82,7 +82,7 @@ export function createProc(name, { cmd, args, cwd, env, stdin, log }) { name = name; lines$ = Rx.merge(observeLines(childProcess.stdout), observeLines(childProcess.stderr)).pipe( - tap(line => log.write(` ${gray('proc')} [${gray(name)}] ${line}`)), + tap(line => log.write(` ${gray('proc')} [${gray(name)}] ${line}`)), share() ); diff --git a/packages/kbn-dev-utils/src/tooling_log/__snapshots__/log_levels.test.js.snap b/packages/kbn-dev-utils/src/tooling_log/__snapshots__/log_levels.test.js.snap new file mode 100644 index 000000000000..56ad7de85884 --- /dev/null +++ b/packages/kbn-dev-utils/src/tooling_log/__snapshots__/log_levels.test.js.snap @@ -0,0 +1,91 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`parses valid log levels correctly: debug 1`] = ` +Object { + "flags": Object { + "debug": true, + "error": true, + "info": true, + "silent": true, + "verbose": false, + "warning": true, + }, + "name": "debug", +} +`; + +exports[`parses valid log levels correctly: error 1`] = ` +Object { + "flags": Object { + "debug": false, + "error": true, + "info": false, + "silent": true, + "verbose": false, + "warning": false, + }, + "name": "error", +} +`; + +exports[`parses valid log levels correctly: info 1`] = ` +Object { + "flags": Object { + "debug": false, + "error": true, + "info": true, + "silent": true, + "verbose": false, + "warning": true, + }, + "name": "info", +} +`; + +exports[`parses valid log levels correctly: silent 1`] = ` +Object { + "flags": Object { + "debug": false, + "error": false, + "info": false, + "silent": true, + "verbose": false, + "warning": false, + }, + "name": "silent", +} +`; + +exports[`parses valid log levels correctly: verbose 1`] = ` +Object { + "flags": Object { + "debug": true, + "error": true, + "info": true, + "silent": true, + "verbose": true, + "warning": true, + }, + "name": "verbose", +} +`; + +exports[`parses valid log levels correctly: warning 1`] = ` +Object { + "flags": Object { + "debug": false, + "error": true, + "info": false, + "silent": true, + "verbose": false, + "warning": true, + }, + "name": "warning", +} +`; + +exports[`throws error for invalid levels: bar 1`] = `"Invalid log level \\"bar\\" (expected one of silent,error,warning,info,debug,verbose)"`; + +exports[`throws error for invalid levels: foo 1`] = `"Invalid log level \\"foo\\" (expected one of silent,error,warning,info,debug,verbose)"`; + +exports[`throws error for invalid levels: warn 1`] = `"Invalid log level \\"warn\\" (expected one of silent,error,warning,info,debug,verbose)"`; diff --git a/packages/kbn-dev-utils/src/tooling_log/__snapshots__/tooling_log.test.js.snap b/packages/kbn-dev-utils/src/tooling_log/__snapshots__/tooling_log.test.js.snap new file mode 100644 index 000000000000..059e3d49c368 --- /dev/null +++ b/packages/kbn-dev-utils/src/tooling_log/__snapshots__/tooling_log.test.js.snap @@ -0,0 +1,226 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#debug() sends a msg of type "debug" to each writer with indent and arguments 1`] = ` +Array [ + Array [ + Object { + "args": Array [ + "foo", + "bar", + "baz", + ], + "indent": 0, + "type": "debug", + }, + ], +] +`; + +exports[`#error() sends a msg of type "error" to each writer with indent and arguments 1`] = ` +Array [ + Array [ + Object { + "args": Array [ + [Error: error message], + ], + "indent": 0, + "type": "error", + }, + ], + Array [ + Object { + "args": Array [ + "string message", + ], + "indent": 0, + "type": "error", + }, + ], +] +`; + +exports[`#getWritten$() does not emit msg if all writers return false 1`] = `Array []`; + +exports[`#getWritten$() does not emit msg when no writers 1`] = `Array []`; + +exports[`#getWritten$() emits msg if all writers return true 1`] = ` +Array [ + Object { + "args": Array [ + "foo", + ], + "indent": 0, + "type": "debug", + }, + Object { + "args": Array [ + "bar", + ], + "indent": 0, + "type": "info", + }, + Object { + "args": Array [ + "baz", + ], + "indent": 0, + "type": "verbose", + }, +] +`; + +exports[`#getWritten$() emits msg if some writers return true 1`] = ` +Array [ + Object { + "args": Array [ + "foo", + ], + "indent": 0, + "type": "debug", + }, + Object { + "args": Array [ + "bar", + ], + "indent": 0, + "type": "info", + }, + Object { + "args": Array [ + "baz", + ], + "indent": 0, + "type": "verbose", + }, +] +`; + +exports[`#indent() changes the indent on each written msg 1`] = ` +Array [ + Array [ + Object { + "args": Array [ + "foo", + ], + "indent": 1, + "type": "debug", + }, + ], + Array [ + Object { + "args": Array [ + "bar", + ], + "indent": 3, + "type": "debug", + }, + ], + Array [ + Object { + "args": Array [ + "baz", + ], + "indent": 6, + "type": "debug", + }, + ], + Array [ + Object { + "args": Array [ + "box", + ], + "indent": 4, + "type": "debug", + }, + ], + Array [ + Object { + "args": Array [ + "foo", + ], + "indent": 0, + "type": "debug", + }, + ], +] +`; + +exports[`#info() sends a msg of type "info" to each writer with indent and arguments 1`] = ` +Array [ + Array [ + Object { + "args": Array [ + "foo", + "bar", + "baz", + ], + "indent": 0, + "type": "info", + }, + ], +] +`; + +exports[`#success() sends a msg of type "success" to each writer with indent and arguments 1`] = ` +Array [ + Array [ + Object { + "args": Array [ + "foo", + "bar", + "baz", + ], + "indent": 0, + "type": "success", + }, + ], +] +`; + +exports[`#verbose() sends a msg of type "verbose" to each writer with indent and arguments 1`] = ` +Array [ + Array [ + Object { + "args": Array [ + "foo", + "bar", + "baz", + ], + "indent": 0, + "type": "verbose", + }, + ], +] +`; + +exports[`#warning() sends a msg of type "warning" to each writer with indent and arguments 1`] = ` +Array [ + Array [ + Object { + "args": Array [ + "foo", + "bar", + "baz", + ], + "indent": 0, + "type": "warning", + }, + ], +] +`; + +exports[`#write() sends a msg of type "write" to each writer with indent and arguments 1`] = ` +Array [ + Array [ + Object { + "args": Array [ + "foo", + "bar", + "baz", + ], + "indent": 0, + "type": "write", + }, + ], +] +`; diff --git a/packages/kbn-dev-utils/src/tooling_log/__snapshots__/tooling_log_text_writer.test.js.snap b/packages/kbn-dev-utils/src/tooling_log/__snapshots__/tooling_log_text_writer.test.js.snap new file mode 100644 index 000000000000..76c018fdb366 --- /dev/null +++ b/packages/kbn-dev-utils/src/tooling_log/__snapshots__/tooling_log_text_writer.test.js.snap @@ -0,0 +1,178 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`formats %s patterns and indents multi-line messages correctly 1`] = ` +" │ succ foo bar + │ { foo: { bar: { '1': [Array] } }, + │ bar: { bar: { '1': [Array] } } } + │ + │ Infinity +" +`; + +exports[`level:debug/type:debug snapshots: is written 1`] = `true`; + +exports[`level:debug/type:debug snapshots: output 1`] = ` +" debg foo +" +`; + +exports[`level:debug/type:error snapshots: is written 1`] = `true`; + +exports[`level:debug/type:error snapshots: output 1`] = ` +"ERROR foo +" +`; + +exports[`level:debug/type:info snapshots: is written 1`] = `true`; + +exports[`level:debug/type:info snapshots: output 1`] = ` +" info foo +" +`; + +exports[`level:debug/type:success snapshots: is written 1`] = `true`; + +exports[`level:debug/type:success snapshots: output 1`] = ` +" succ foo +" +`; + +exports[`level:debug/type:verbose snapshots: is written 1`] = `false`; + +exports[`level:debug/type:warning snapshots: is written 1`] = `true`; + +exports[`level:debug/type:warning snapshots: output 1`] = ` +" warn foo +" +`; + +exports[`level:error/type:debug snapshots: is written 1`] = `false`; + +exports[`level:error/type:error snapshots: is written 1`] = `true`; + +exports[`level:error/type:error snapshots: output 1`] = ` +"ERROR foo +" +`; + +exports[`level:error/type:info snapshots: is written 1`] = `false`; + +exports[`level:error/type:success snapshots: is written 1`] = `false`; + +exports[`level:error/type:verbose snapshots: is written 1`] = `false`; + +exports[`level:error/type:warning snapshots: is written 1`] = `false`; + +exports[`level:info/type:debug snapshots: is written 1`] = `false`; + +exports[`level:info/type:error snapshots: is written 1`] = `true`; + +exports[`level:info/type:error snapshots: output 1`] = ` +"ERROR foo +" +`; + +exports[`level:info/type:info snapshots: is written 1`] = `true`; + +exports[`level:info/type:info snapshots: output 1`] = ` +" info foo +" +`; + +exports[`level:info/type:success snapshots: is written 1`] = `true`; + +exports[`level:info/type:success snapshots: output 1`] = ` +" succ foo +" +`; + +exports[`level:info/type:verbose snapshots: is written 1`] = `false`; + +exports[`level:info/type:warning snapshots: is written 1`] = `true`; + +exports[`level:info/type:warning snapshots: output 1`] = ` +" warn foo +" +`; + +exports[`level:silent/type:debug snapshots: is written 1`] = `false`; + +exports[`level:silent/type:error snapshots: is written 1`] = `false`; + +exports[`level:silent/type:info snapshots: is written 1`] = `false`; + +exports[`level:silent/type:success snapshots: is written 1`] = `false`; + +exports[`level:silent/type:verbose snapshots: is written 1`] = `false`; + +exports[`level:silent/type:warning snapshots: is written 1`] = `false`; + +exports[`level:verbose/type:debug snapshots: is written 1`] = `true`; + +exports[`level:verbose/type:debug snapshots: output 1`] = ` +" debg foo +" +`; + +exports[`level:verbose/type:error snapshots: is written 1`] = `true`; + +exports[`level:verbose/type:error snapshots: output 1`] = ` +"ERROR foo +" +`; + +exports[`level:verbose/type:info snapshots: is written 1`] = `true`; + +exports[`level:verbose/type:info snapshots: output 1`] = ` +" info foo +" +`; + +exports[`level:verbose/type:success snapshots: is written 1`] = `true`; + +exports[`level:verbose/type:success snapshots: output 1`] = ` +" succ foo +" +`; + +exports[`level:verbose/type:verbose snapshots: is written 1`] = `true`; + +exports[`level:verbose/type:verbose snapshots: output 1`] = ` +" sill foo +" +`; + +exports[`level:verbose/type:warning snapshots: is written 1`] = `true`; + +exports[`level:verbose/type:warning snapshots: output 1`] = ` +" warn foo +" +`; + +exports[`level:warning/type:debug snapshots: is written 1`] = `false`; + +exports[`level:warning/type:error snapshots: is written 1`] = `true`; + +exports[`level:warning/type:error snapshots: output 1`] = ` +"ERROR foo +" +`; + +exports[`level:warning/type:info snapshots: is written 1`] = `false`; + +exports[`level:warning/type:success snapshots: is written 1`] = `false`; + +exports[`level:warning/type:verbose snapshots: is written 1`] = `false`; + +exports[`level:warning/type:warning snapshots: is written 1`] = `true`; + +exports[`level:warning/type:warning snapshots: output 1`] = ` +" warn foo +" +`; + +exports[`throws error if created with invalid level 1`] = `"Invalid log level \\"foo\\" (expected one of silent,error,warning,info,debug,verbose)"`; + +exports[`throws error if writeTo config is not defined or doesn't have a write method 1`] = `"ToolingLogTextWriter requires the \`writeTo\` option be set to a stream (like process.stdout)"`; + +exports[`throws error if writeTo config is not defined or doesn't have a write method 2`] = `"ToolingLogTextWriter requires the \`writeTo\` option be set to a stream (like process.stdout)"`; diff --git a/packages/kbn-dev-utils/src/tooling_log/__tests__/log.js b/packages/kbn-dev-utils/src/tooling_log/__tests__/log.js deleted file mode 100644 index 071b3c180d0c..000000000000 --- a/packages/kbn-dev-utils/src/tooling_log/__tests__/log.js +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from 'expect.js'; -import Chance from 'chance'; - -import { createConcatStream, createPromiseFromStreams } from '../../streams'; - -import { createToolingLog } from '../tooling_log'; - -const chance = new Chance(); -const capture = (level, block) => { - const log = createToolingLog(level); - block(log); - log.end(); - return createPromiseFromStreams([log, createConcatStream('')]); -}; - -const nothingTest = (logLevel, method) => { - describe(`#${method}(...any)`, () => { - it('logs nothing', async () => { - const output = await capture(logLevel, log => log[method]('foo')); - expect(output).to.be(''); - }); - }); -}; - -const somethingTest = (logLevel, method) => { - describe(`#${method}(...any)`, () => { - it('logs to output stream', async () => { - const output = await capture(logLevel, log => log[method]('foo')); - expect(output).to.contain('foo'); - }); - }); -}; - -describe('utils: createToolingLog(logLevel, output)', () => { - it('is a readable stream', async () => { - const log = createToolingLog('debug'); - log.info('Foo'); - log.info('Bar'); - log.info('Baz'); - log.end(); - - const output = await createPromiseFromStreams([log, createConcatStream('')]); - - expect(output).to.contain('Foo'); - expect(output).to.contain('Bar'); - expect(output).to.contain('Baz'); - }); - - describe('log level', () => { - describe('logLevel=silent', () => { - nothingTest('silent', 'debug'); - nothingTest('silent', 'info'); - nothingTest('silent', 'error'); - }); - describe('logLevel=error', () => { - nothingTest('error', 'debug'); - nothingTest('error', 'info'); - somethingTest('error', 'error'); - }); - describe('logLevel=info', () => { - nothingTest('info', 'debug'); - somethingTest('info', 'info'); - somethingTest('info', 'error'); - }); - describe('logLevel=debug', () => { - somethingTest('debug', 'debug'); - somethingTest('debug', 'info'); - somethingTest('debug', 'error'); - }); - describe('invalid logLevel', () => { - it('throw error', () => { - // avoid the impossibility that a valid level is generated - // by specifying a long length - const level = chance.word({ length: 10 }); - - expect(() => createToolingLog(level)).to.throwError(level); - }); - }); - }); -}); diff --git a/packages/kbn-dev-utils/src/tooling_log/__tests__/log_levels.js b/packages/kbn-dev-utils/src/tooling_log/__tests__/log_levels.js deleted file mode 100644 index cedfde5261a5..000000000000 --- a/packages/kbn-dev-utils/src/tooling_log/__tests__/log_levels.js +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import expect from 'expect.js'; -import Chance from 'chance'; -import { parseLogLevel } from '../log_levels'; - -const chance = new Chance(); - -describe('parseLogLevel(logLevel).flags', () => { - describe('logLevel=silent', () => { - it('produces correct map', () => { - expect(parseLogLevel('silent').flags).to.eql({ - silent: true, - error: false, - warning: false, - info: false, - debug: false, - verbose: false, - }); - }); - }); - - describe('logLevel=error', () => { - it('produces correct map', () => { - expect(parseLogLevel('error').flags).to.eql({ - silent: true, - error: true, - warning: false, - info: false, - debug: false, - verbose: false, - }); - }); - }); - - describe('logLevel=warning', () => { - it('produces correct map', () => { - expect(parseLogLevel('warning').flags).to.eql({ - silent: true, - error: true, - warning: true, - info: false, - debug: false, - verbose: false, - }); - }); - }); - - describe('logLevel=info', () => { - it('produces correct map', () => { - expect(parseLogLevel('info').flags).to.eql({ - silent: true, - error: true, - warning: true, - info: true, - debug: false, - verbose: false, - }); - }); - }); - - describe('logLevel=debug', () => { - it('produces correct map', () => { - expect(parseLogLevel('debug').flags).to.eql({ - silent: true, - error: true, - warning: true, - info: true, - debug: true, - verbose: false, - }); - }); - }); - - describe('logLevel=verbose', () => { - it('produces correct map', () => { - expect(parseLogLevel('verbose').flags).to.eql({ - silent: true, - error: true, - warning: true, - info: true, - debug: true, - verbose: true, - }); - }); - }); - - describe('invalid logLevel', () => { - it('throws error', () => { - // avoid the impossibility that a valid level is generated - // by specifying a long length - const level = chance.word({ length: 10 }); - - expect(() => parseLogLevel(level)).to.throwError(level); - }); - }); -}); diff --git a/packages/kbn-dev-utils/src/tooling_log/index.d.ts b/packages/kbn-dev-utils/src/tooling_log/index.d.ts new file mode 100644 index 000000000000..88efd6cf913b --- /dev/null +++ b/packages/kbn-dev-utils/src/tooling_log/index.d.ts @@ -0,0 +1,22 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { ToolingLog } from './tooling_log'; +export { ToolingLogTextWriter, WriterConfig } from './tooling_log_text_writer'; +export { pickLevelFromFlags, LogLevel } from './log_levels'; diff --git a/packages/kbn-dev-utils/src/tooling_log/index.js b/packages/kbn-dev-utils/src/tooling_log/index.js index 9d1f820e39a0..bd394c9a1315 100644 --- a/packages/kbn-dev-utils/src/tooling_log/index.js +++ b/packages/kbn-dev-utils/src/tooling_log/index.js @@ -17,5 +17,6 @@ * under the License. */ -export { createToolingLog } from './tooling_log'; +export { ToolingLog } from './tooling_log'; +export { ToolingLogTextWriter } from './tooling_log_text_writer'; export { pickLevelFromFlags } from './log_levels'; diff --git a/packages/kbn-dev-utils/src/tooling_log/log_levels.d.ts b/packages/kbn-dev-utils/src/tooling_log/log_levels.d.ts new file mode 100644 index 000000000000..64786f8612dd --- /dev/null +++ b/packages/kbn-dev-utils/src/tooling_log/log_levels.d.ts @@ -0,0 +1,29 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export type LogLevel = 'silent' | 'error' | 'warning' | 'info' | 'debug' | 'verbose'; + +export interface ParsedLogLevel { + name: LogLevel; + flags: { [key in LogLevel]: boolean }; +} + +export function pickLevelFromFlags(flags: { [key: string]: any }): LogLevel; + +export function parseLogLevel(level: LogLevel): ParsedLogLevel; diff --git a/packages/kbn-dev-utils/src/tooling_log/log_levels.test.js b/packages/kbn-dev-utils/src/tooling_log/log_levels.test.js new file mode 100644 index 000000000000..eecc33cc9069 --- /dev/null +++ b/packages/kbn-dev-utils/src/tooling_log/log_levels.test.js @@ -0,0 +1,35 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { parseLogLevel } from './log_levels'; + +it('parses valid log levels correctly', () => { + expect(parseLogLevel('silent')).toMatchSnapshot('silent'); + expect(parseLogLevel('error')).toMatchSnapshot('error'); + expect(parseLogLevel('warning')).toMatchSnapshot('warning'); + expect(parseLogLevel('info')).toMatchSnapshot('info'); + expect(parseLogLevel('debug')).toMatchSnapshot('debug'); + expect(parseLogLevel('verbose')).toMatchSnapshot('verbose'); +}); + +it('throws error for invalid levels', () => { + expect(() => parseLogLevel('warn')).toThrowErrorMatchingSnapshot('warn'); + expect(() => parseLogLevel('foo')).toThrowErrorMatchingSnapshot('foo'); + expect(() => parseLogLevel('bar')).toThrowErrorMatchingSnapshot('bar'); +}); diff --git a/packages/kbn-dev-utils/src/tooling_log/tooling_log.d.ts b/packages/kbn-dev-utils/src/tooling_log/tooling_log.d.ts new file mode 100644 index 000000000000..e59524edbe0d --- /dev/null +++ b/packages/kbn-dev-utils/src/tooling_log/tooling_log.d.ts @@ -0,0 +1,45 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// tslint:disable max-classes-per-file + +import * as Rx from 'rxjs'; + +import { ToolingLogWriter, WriterConfig } from './tooling_log_text_writer'; + +export interface LogMessage { + type: 'verbose' | 'debug' | 'info' | 'success' | 'warning' | 'error' | 'write'; + indent: number; + args: any[]; +} + +export class ToolingLog { + constructor(config?: WriterConfig); + public verbose(...args: any[]): void; + public debug(...args: any[]): void; + public info(...args: any[]): void; + public success(...args: any[]): void; + public warning(...args: any[]): void; + public error(errOrMsg: string | Error): void; + public write(...args: any[]): void; + public indent(spaces: number): void; + public getWriters(): ToolingLogWriter[]; + public setWriters(reporters: ToolingLogWriter[]): void; + public getWritten$(): Rx.Observable; +} diff --git a/packages/kbn-dev-utils/src/tooling_log/tooling_log.js b/packages/kbn-dev-utils/src/tooling_log/tooling_log.js index d306dcfe76a5..1b309ac28fc3 100644 --- a/packages/kbn-dev-utils/src/tooling_log/tooling_log.js +++ b/packages/kbn-dev-utils/src/tooling_log/tooling_log.js @@ -17,87 +17,85 @@ * under the License. */ -import { format } from 'util'; -import { PassThrough } from 'stream'; +import * as Rx from 'rxjs'; +import { EventEmitter } from 'events'; -import { magentaBright, yellow, red, blue, green, dim } from 'chalk'; +import { ToolingLogTextWriter } from './tooling_log_text_writer'; -import { parseLogLevel } from './log_levels'; +export class ToolingLog extends EventEmitter { + /** + * Create a ToolingLog object + * @param {WriterConfig} writerConfig + */ + constructor(writerConfig) { + super(); -export function createToolingLog(initialLogLevelName = 'silent') { - // current log level (see logLevel.name and logLevel.flags) changed - // with ToolingLog#setLevel(newLogLevelName); - let logLevel = parseLogLevel(initialLogLevelName); - - // current indentation level, changed with ToolingLog#indent(delta) - let indentString = ''; - - class ToolingLog extends PassThrough { - constructor() { - super({ objectMode: true }); - } - - verbose(...args) { - if (!logLevel.flags.verbose) return; - this.write(' %s ', magentaBright('sill'), format(...args)); - } - - debug(...args) { - if (!logLevel.flags.debug) return; - this.write(' %s ', dim('debg'), format(...args)); - } - - info(...args) { - if (!logLevel.flags.info) return; - this.write(' %s ', blue('info'), format(...args)); - } - - success(...args) { - if (!logLevel.flags.info) return; - this.write(' %s ', green('succ'), format(...args)); - } - - warning(...args) { - if (!logLevel.flags.warning) return; - this.write(' %s ', yellow('warn'), format(...args)); - } - - error(err) { - if (!logLevel.flags.error) return; - - if (typeof err !== 'string' && !(err instanceof Error)) { - err = new Error(`"${err}" thrown`); - } - - this.write('%s ', red('ERROR'), err.stack || err.message || err); - } - - indent(delta = 0) { - const width = Math.max(0, indentString.length + delta); - indentString = ' '.repeat(width); - return indentString.length; - } - - getLevel() { - return logLevel.name; - } - - setLevel(newLogLevelName) { - logLevel = parseLogLevel(newLogLevelName); - } - - write(...args) { - format(...args) - .split('\n') - .forEach((line, i) => { - const subLineIndent = i === 0 ? '' : ' '; - const indent = !indentString - ? '' - : indentString.slice(0, -1) + (i === 0 && line[0] === '-' ? '└' : '│'); - super.write(`${indent}${subLineIndent}${line}\n`); - }); - } + this._indent = 0; + this._writers = writerConfig ? [new ToolingLogTextWriter(writerConfig)] : []; + this._written$ = new Rx.Subject(); } - return new ToolingLog(); + indent(delta = 0) { + this._indent = Math.max(this._indent + delta, 0); + return this._indent; + } + + verbose(...args) { + this._write('verbose', args); + } + + debug(...args) { + this._write('debug', args); + } + + info(...args) { + this._write('info', args); + } + + success(...args) { + this._write('success', args); + } + + warning(...args) { + this._write('warning', args); + } + + error(error) { + this._write('error', [error]); + } + + write(...args) { + this._write('write', args); + } + + getWriters() { + return this._writers.slice(0); + } + + setWriters(writers) { + this._writers = [...writers]; + } + + getWritten$() { + return this._written$.asObservable(); + } + + _write(type, args) { + const msg = { + type, + indent: this._indent, + args, + }; + + let written = false; + for (const writer of this._writers) { + if (writer.write(msg)) { + written = true; + } + } + + if (written) { + this._written$.next(msg); + } + } } diff --git a/packages/kbn-dev-utils/src/tooling_log/tooling_log.test.js b/packages/kbn-dev-utils/src/tooling_log/tooling_log.test.js new file mode 100644 index 000000000000..fb1095caeefa --- /dev/null +++ b/packages/kbn-dev-utils/src/tooling_log/tooling_log.test.js @@ -0,0 +1,143 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as Rx from 'rxjs'; +import { toArray, takeUntil } from 'rxjs/operators'; + +import { ToolingLog } from './tooling_log'; +import { ToolingLogTextWriter } from './tooling_log_text_writer'; + +it('creates zero writers without a config', () => { + const log = new ToolingLog(); + expect(log.getWriters()).toHaveLength(0); +}); + +it('creates a single writer with a single object', () => { + const log = new ToolingLog({ level: 'warning', writeTo: process.stdout }); + expect(log.getWriters()).toHaveLength(1); + const [writer] = log.getWriters(); + expect(writer.level).toHaveProperty('name', 'warning'); + expect(writer.writeTo).toBe(process.stdout); +}); + +describe('#get/setWriters()', () => { + it('returns/replaces the current writers', () => { + const log = new ToolingLog(); + expect(log.getWriters()).toHaveLength(0); + + log.setWriters([ + new ToolingLogTextWriter({ + level: 'verbose', + writeTo: process.stdout, + }), + new ToolingLogTextWriter({ + level: 'verbose', + writeTo: process.stdout, + }), + ]); + expect(log.getWriters()).toHaveLength(2); + + log.setWriters([]); + expect(log.getWriters()).toHaveLength(0); + }); +}); + +describe('#indent()', () => { + it('changes the indent on each written msg', () => { + const log = new ToolingLog(); + const write = jest.fn(); + log.setWriters([{ write }]); + + log.indent(1); + log.debug('foo'); + log.indent(2); + log.debug('bar'); + log.indent(3); + log.debug('baz'); + log.indent(-2); + log.debug('box'); + log.indent(-Infinity); + log.debug('foo'); + + expect(write.mock.calls).toMatchSnapshot(); + }); +}); + +['verbose', 'debug', 'info', 'success', 'warning', 'error', 'write'].forEach(method => { + describe(`#${method}()`, () => { + it(`sends a msg of type "${method}" to each writer with indent and arguments`, () => { + const log = new ToolingLog(); + const writeA = jest.fn(); + const writeB = jest.fn(); + + log.setWriters([{ write: writeA }, { write: writeB }]); + + if (method === 'error') { + const error = new Error('error message'); + error.stack = '... stack trace ...'; + log.error(error); + log.error('string message'); + } else { + log[method]('foo', 'bar', 'baz'); + } + + expect(writeA.mock.calls).toMatchSnapshot(); + expect(writeA.mock.calls).toEqual(writeB.mock.calls); + }); + }); +}); + +describe('#getWritten$()', () => { + async function testWrittenMsgs(writers) { + const log = new ToolingLog(); + log.setWriters(writers); + + const done$ = new Rx.Subject(); + const promise = log + .getWritten$() + .pipe( + takeUntil(done$), + toArray() + ) + .toPromise(); + + log.debug('foo'); + log.info('bar'); + log.verbose('baz'); + done$.next(); + + expect(await promise).toMatchSnapshot(); + } + + it('does not emit msg when no writers', async () => { + await testWrittenMsgs([]); + }); + + it('emits msg if all writers return true', async () => { + await testWrittenMsgs([{ write: jest.fn(() => true) }, { write: jest.fn(() => true) }]); + }); + + it('emits msg if some writers return true', async () => { + await testWrittenMsgs([{ write: jest.fn(() => true) }, { write: jest.fn(() => false) }]); + }); + + it('does not emit msg if all writers return false', async () => { + await testWrittenMsgs([{ write: jest.fn(() => false) }, { write: jest.fn(() => false) }]); + }); +}); diff --git a/packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.d.ts b/packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.d.ts new file mode 100644 index 000000000000..67311c5ceeee --- /dev/null +++ b/packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.d.ts @@ -0,0 +1,42 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { LogLevel, ParsedLogLevel } from './log_levels'; +import { LogMessage } from './tooling_log'; + +export interface ToolingLogWriter { + write(msg: LogMessage): boolean; +} + +export interface WriteTarget { + write(chunk: string): void; +} + +export interface WriterConfig { + level: LogLevel; + writeTo: WriteTarget; +} + +export class ToolingLogTextWriter implements ToolingLogTextWriter { + public level: ParsedLogLevel; + public writeTo: WriteTarget; + + constructor(config: WriterConfig); + public write(msg: LogMessage): boolean; +} diff --git a/packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.js b/packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.js new file mode 100644 index 000000000000..fa0db2868743 --- /dev/null +++ b/packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.js @@ -0,0 +1,92 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { format } from 'util'; + +import { magentaBright, yellow, red, blue, green, dim } from 'chalk'; + +import { parseLogLevel } from './log_levels'; + +const PREFIX_INDENT = ' '.repeat(6); +const MSG_PREFIXES = { + verbose: ` ${magentaBright('sill')} `, + debug: ` ${dim('debg')} `, + info: ` ${blue('info')} `, + success: ` ${green('succ')} `, + warning: ` ${yellow('warn')} `, + error: `${red('ERROR')} `, +}; + +function shouldWriteType(level, type) { + if (type === 'write') { + return true; + } + + return Boolean(level.flags[type === 'success' ? 'info' : type]); +} + +function stringifyError(error) { + if (typeof error !== 'string' && !(error instanceof Error)) { + error = new Error(`"${error}" thrown`); + } + + return error.stack || error.message || error; +} + +export class ToolingLogTextWriter { + constructor(config) { + this.level = parseLogLevel(config.level); + this.writeTo = config.writeTo; + + if (!this.writeTo || typeof this.writeTo.write !== 'function') { + throw new Error( + 'ToolingLogTextWriter requires the `writeTo` option be set to a stream (like process.stdout)' + ); + } + } + + write({ type, indent, args }) { + if (!shouldWriteType(this.level, type)) { + return false; + } + + const txt = type === 'error' ? stringifyError(args[0]) : format(...args); + const prefix = MSG_PREFIXES[type] || ''; + + (prefix + txt).split('\n').forEach((line, i) => { + let lineIndent = ''; + + if (indent > 0) { + // if we are indenting write some spaces followed by a symbol + lineIndent += ' '.repeat(indent - 1); + lineIndent += line.startsWith('-') ? '└' : '│'; + } + + if (line && prefix && i > 0) { + // apply additional indentation to lines after + // the first if this message gets a prefix + lineIndent += PREFIX_INDENT; + } + + this.writeTo.write(`${lineIndent}${line}\n`); + }); + + return true; + } +} diff --git a/packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.test.js b/packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.test.js new file mode 100644 index 000000000000..ed859757c0e7 --- /dev/null +++ b/packages/kbn-dev-utils/src/tooling_log/tooling_log_text_writer.test.js @@ -0,0 +1,98 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ToolingLogTextWriter } from './tooling_log_text_writer'; + +it('throws error if created with invalid level', () => { + expect( + () => + new ToolingLogTextWriter({ + level: 'foo', + }) + ).toThrowErrorMatchingSnapshot(); +}); + +it("throws error if writeTo config is not defined or doesn't have a write method", () => { + expect(() => { + new ToolingLogTextWriter({ + level: 'verbose', + writeTo: null, + }); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + new ToolingLogTextWriter({ + level: 'verbose', + writeTo: 'foo', + }); + }).toThrowErrorMatchingSnapshot(); +}); + +const levels = ['silent', 'verbose', 'debug', 'info', 'warning', 'error']; +const types = ['verbose', 'debug', 'info', 'warning', 'error', 'success']; +for (const level of levels) { + for (const type of types) { + it(`level:${level}/type:${type} snapshots`, () => { + const write = jest.fn(); + const writer = new ToolingLogTextWriter({ + level, + writeTo: { + write, + }, + }); + + const written = writer.write({ + type: type, + indent: 0, + args: ['foo'], + }); + + expect(written).toMatchSnapshot('is written'); + + if (written) { + const output = write.mock.calls.reduce((acc, chunk) => `${acc}${chunk}`, ''); + expect(output).toMatchSnapshot('output'); + } + }); + } +} + +it('formats %s patterns and indents multi-line messages correctly', () => { + const write = jest.fn(); + const writer = new ToolingLogTextWriter({ + level: 'debug', + writeTo: { + write, + }, + }); + + writer.write({ + type: 'success', + indent: 10, + args: [ + '%s\n%O\n\n%d', + 'foo bar', + { foo: { bar: { 1: [1, 2, 3] } }, bar: { bar: { 1: [1, 2, 3] } } }, + Infinity, + ], + }); + + const output = write.mock.calls.reduce((acc, chunk) => `${acc}${chunk}`, ''); + expect(output).toMatchSnapshot(); +}); diff --git a/packages/kbn-dev-utils/tsconfig.json b/packages/kbn-dev-utils/tsconfig.json index 3604f1004cf6..cb0d38840fff 100644 --- a/packages/kbn-dev-utils/tsconfig.json +++ b/packages/kbn-dev-utils/tsconfig.json @@ -1,6 +1,7 @@ { "extends": "../../tsconfig.json", "include": [ - "index.d.ts" + "index.d.ts", + "src/**/*.d.ts" ], } diff --git a/packages/kbn-es/src/integration_tests/cluster.test.js b/packages/kbn-es/src/integration_tests/cluster.test.js index f13bf1954175..e74f94d92bee 100644 --- a/packages/kbn-es/src/integration_tests/cluster.test.js +++ b/packages/kbn-es/src/integration_tests/cluster.test.js @@ -17,7 +17,7 @@ * under the License. */ -const { createToolingLog } = require('@kbn/dev-utils'); +const { ToolingLog } = require('@kbn/dev-utils'); const execa = require('execa'); const { Cluster } = require('../cluster'); const { installSource, installSnapshot, installArchive } = require('../install'); @@ -30,9 +30,7 @@ jest.mock('../install', () => ({ jest.mock('execa', () => jest.fn()); -const log = createToolingLog('verbose'); -log.onData = jest.fn(); -log.on('data', log.onData); +const log = new ToolingLog(); function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); diff --git a/packages/kbn-es/src/utils/log.js b/packages/kbn-es/src/utils/log.js index bb2e96b8c089..5c4bdbcb6c0d 100644 --- a/packages/kbn-es/src/utils/log.js +++ b/packages/kbn-es/src/utils/log.js @@ -17,9 +17,11 @@ * under the License. */ -const { createToolingLog } = require('@kbn/dev-utils'); +const { ToolingLog } = require('@kbn/dev-utils'); -const log = createToolingLog('verbose'); -log.pipe(process.stdout); +const log = new ToolingLog({ + level: 'verbose', + writeTo: process.stdout, +}); exports.log = log; diff --git a/packages/kbn-test/src/functional_tests/cli/run_tests/args.js b/packages/kbn-test/src/functional_tests/cli/run_tests/args.js index 5fdc738c4517..c03649a1167e 100644 --- a/packages/kbn-test/src/functional_tests/cli/run_tests/args.js +++ b/packages/kbn-test/src/functional_tests/cli/run_tests/args.js @@ -18,7 +18,7 @@ */ import dedent from 'dedent'; -import { createToolingLog, pickLevelFromFlags } from '@kbn/dev-utils'; +import { ToolingLog, pickLevelFromFlags } from '@kbn/dev-utils'; const options = { help: { desc: 'Display this menu and exit.' }, @@ -99,9 +99,10 @@ export function processOptions(userOptions, defaultConfigPaths) { } function createLogger() { - const log = createToolingLog(pickLevelFromFlags(userOptions)); - log.pipe(process.stdout); - return log; + return new ToolingLog({ + level: pickLevelFromFlags(userOptions), + writeTo: process.stdout, + }); } return { diff --git a/packages/kbn-test/src/functional_tests/cli/start_servers/args.js b/packages/kbn-test/src/functional_tests/cli/start_servers/args.js index bc3d97ab0641..ca62aff2649f 100644 --- a/packages/kbn-test/src/functional_tests/cli/start_servers/args.js +++ b/packages/kbn-test/src/functional_tests/cli/start_servers/args.js @@ -18,7 +18,7 @@ */ import dedent from 'dedent'; -import { createToolingLog, pickLevelFromFlags } from '@kbn/dev-utils'; +import { ToolingLog, pickLevelFromFlags } from '@kbn/dev-utils'; const options = { help: { desc: 'Display this menu and exit.' }, @@ -86,9 +86,10 @@ export function processOptions(userOptions, defaultConfigPath) { } function createLogger() { - const log = createToolingLog(pickLevelFromFlags(userOptions)); - log.pipe(process.stdout); - return log; + return new ToolingLog({ + level: pickLevelFromFlags(userOptions), + writeTo: process.stdout, + }); } return { diff --git a/packages/kbn-test/src/functional_tests/lib/run_cli.js b/packages/kbn-test/src/functional_tests/lib/run_cli.js index 9bb2b7c05bb2..56f6f36f5388 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_cli.js +++ b/packages/kbn-test/src/functional_tests/lib/run_cli.js @@ -22,6 +22,14 @@ import { inspect } from 'util'; import chalk from 'chalk'; import getopts from 'getopts'; +export class CliError extends Error { + constructor(message, exitCode = 1) { + super(message); + this.exitCode = exitCode; + Error.captureStackTrace(this, CliError); + } +} + export async function runCli(getHelpText, run) { try { const userOptions = getopts(process.argv.slice(2)) || {}; @@ -39,20 +47,25 @@ export async function runCli(getHelpText, run) { console.log(); console.log(chalk.red(error.message)); - // first line in the stack trace is the message, skip it as we log it directly and color it red - if (error.stack) { - console.log( - error.stack - .split('\n') - .slice(1) - .join('\n') - ); - } else { - console.log(' (no stack trace)'); + // CliError is a special error class that indicates that the error is produced as a part + // of using the CLI, and does not need a stack trace to make sense, so we skip the stack + // trace logging if the error thrown is an instance of this class + if (!(error instanceof CliError)) { + // first line in the stack trace is the message, skip it as we log it directly and color it red + if (error.stack) { + console.log( + error.stack + .split('\n') + .slice(1) + .join('\n') + ); + } else { + console.log(' (no stack trace)'); + } } console.log(); - process.exit(1); + process.exit(error.exitCode || 1); } } diff --git a/packages/kbn-test/src/functional_tests/lib/run_ftr.js b/packages/kbn-test/src/functional_tests/lib/run_ftr.js index 90a3289f7202..6fe02ec39d9a 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_ftr.js +++ b/packages/kbn-test/src/functional_tests/lib/run_ftr.js @@ -17,33 +17,26 @@ * under the License. */ -import { KIBANA_FTR_SCRIPT, PROJECT_ROOT } from './paths'; +import { createFunctionalTestRunner } from '../../../../../src/functional_test_runner'; +import { CliError } from './run_cli'; -export async function runFtr({ - procs, - configPath, - cwd = PROJECT_ROOT, - options: { log, bail, grep, updateBaselines }, -}) { - const args = [KIBANA_FTR_SCRIPT]; - - if (getLogFlag(log)) args.push(`--${getLogFlag(log)}`); - if (bail) args.push('--bail'); - if (configPath) args.push('--config', configPath); - if (grep) args.push('--grep', grep); - if (updateBaselines) args.push('--updateBaselines'); - - await procs.run('ftr', { - cmd: 'node', - args, - cwd, - wait: true, +export async function runFtr({ configPath, options: { log, bail, grep, updateBaselines } }) { + const ftr = createFunctionalTestRunner({ + log, + configFile: configPath, + configOverrides: { + mochaOpts: { + bail: !!bail, + grep, + }, + updateBaselines, + }, }); -} -function getLogFlag(log) { - const level = log.getLevel(); - - if (level === 'info') return null; - return level === 'error' ? 'quiet' : level; + const failureCount = await ftr.run(); + if (failureCount > 0) { + throw new CliError( + `${failureCount} functional test ${failureCount === 1 ? 'failure' : 'failures'}` + ); + } } diff --git a/packages/kbn-test/src/functional_tests/tasks.js b/packages/kbn-test/src/functional_tests/tasks.js index b6f44ff7e4cd..0cdcc77161a6 100644 --- a/packages/kbn-test/src/functional_tests/tasks.js +++ b/packages/kbn-test/src/functional_tests/tasks.js @@ -91,7 +91,8 @@ export async function startServers(options) { } async function silence(milliseconds, { log }) { - await Rx.fromEvent(log, 'data') + await log + .getWritten$() .pipe( startWith(null), switchMap(() => Rx.timer(milliseconds)), @@ -115,15 +116,7 @@ async function runSingleConfig(configPath, options) { const es = await runElasticsearch({ config, options: opts }); await runKibanaServer({ procs, config, options: opts }); - - // Note: When solving how to incorporate functional_test_runner - // clean this up - await runFtr({ - procs, - configPath, - cwd: process.cwd(), - options: opts, - }); + await runFtr({ configPath, options: opts }); await procs.stop('kibana'); await es.cleanup(); diff --git a/src/dev/build/cli.js b/src/dev/build/cli.js index 2c42dec6d82d..8107a7b35b88 100644 --- a/src/dev/build/cli.js +++ b/src/dev/build/cli.js @@ -23,7 +23,7 @@ import getopts from 'getopts'; import dedent from 'dedent'; import chalk from 'chalk'; -import { createToolingLog, pickLevelFromFlags } from '@kbn/dev-utils'; +import { ToolingLog, pickLevelFromFlags } from '@kbn/dev-utils'; import { buildDistributables } from './build_distributables'; import { isErrorLogged } from './lib'; @@ -82,8 +82,10 @@ if (flags.help) { process.exit(1); } -const log = createToolingLog(pickLevelFromFlags(flags)); -log.pipe(process.stdout); +const log = new ToolingLog({ + level: pickLevelFromFlags(flags), + writeTo: process.stdout +}); function isOsPackageDesired(name) { if (flags['skip-os-packages']) { diff --git a/src/dev/build/lib/__tests__/exec.js b/src/dev/build/lib/__tests__/exec.js index 33ade585bd3a..9d1ead0c3f22 100644 --- a/src/dev/build/lib/__tests__/exec.js +++ b/src/dev/build/lib/__tests__/exec.js @@ -20,16 +20,22 @@ import sinon from 'sinon'; import stripAnsi from 'strip-ansi'; -import { createToolingLog } from '@kbn/dev-utils'; +import { ToolingLog } from '@kbn/dev-utils'; import { exec } from '../exec'; describe('dev/build/lib/exec', () => { const sandbox = sinon.createSandbox(); afterEach(() => sandbox.reset()); - const log = createToolingLog('verbose'); const onLogLine = sandbox.stub(); - log.on('data', line => onLogLine(stripAnsi(line))); + const log = new ToolingLog({ + level: 'verbose', + writeTo: { + write: chunk => { + onLogLine(stripAnsi(chunk)); + } + } + }); it('executes a command, logs the command, and logs the output', async () => { await exec(log, process.execPath, ['-e', 'console.log("hi")']); diff --git a/src/dev/build/lib/__tests__/runner.js b/src/dev/build/lib/__tests__/runner.js index b5a02bef9117..9b51a724f06c 100644 --- a/src/dev/build/lib/__tests__/runner.js +++ b/src/dev/build/lib/__tests__/runner.js @@ -20,7 +20,7 @@ import sinon from 'sinon'; import expect from 'expect.js'; -import { createToolingLog } from '@kbn/dev-utils'; +import { ToolingLog } from '@kbn/dev-utils'; import { createRunner } from '../runner'; import { isErrorLogged, markErrorLogged } from '../errors'; @@ -29,9 +29,13 @@ describe('dev/build/lib/runner', () => { const config = {}; - const log = createToolingLog('verbose'); const onLogLine = sandbox.stub(); - log.on('data', onLogLine); + const log = new ToolingLog({ + level: 'verbose', + writeTo: { + write: onLogLine + } + }); const buildMatcher = sinon.match({ isOss: sinon.match.func, diff --git a/src/dev/build/tasks/nodejs/__tests__/download.js b/src/dev/build/tasks/nodejs/__tests__/download.js index 5041ed052176..a3514d3254cc 100644 --- a/src/dev/build/tasks/nodejs/__tests__/download.js +++ b/src/dev/build/tasks/nodejs/__tests__/download.js @@ -26,7 +26,7 @@ import sinon from 'sinon'; import expect from 'expect.js'; import Wreck from 'wreck'; -import { createToolingLog } from '@kbn/dev-utils'; +import { ToolingLog } from '@kbn/dev-utils'; import { download } from '../download'; const TMP_DESTINATION = resolve(__dirname, '__tmp__'); @@ -41,9 +41,13 @@ describe('src/dev/build/tasks/nodejs/download', () => { const sandbox = sinon.createSandbox(); afterEach(() => sandbox.reset()); - const log = createToolingLog('verbose'); const onLogLine = sandbox.stub(); - log.on('data', onLogLine); + const log = new ToolingLog({ + level: 'verbose', + writeTo: { + write: onLogLine + } + }); const FOO_SHA256 = '2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae'; const createSendHandler = (send) => (req, res) => { diff --git a/src/dev/notice/cli.js b/src/dev/notice/cli.js index ed621a753ec3..7c1ab57445b6 100644 --- a/src/dev/notice/cli.js +++ b/src/dev/notice/cli.js @@ -22,7 +22,7 @@ import { resolve } from 'path'; import getopts from 'getopts'; import dedent from 'dedent'; -import { createToolingLog, pickLevelFromFlags } from '@kbn/dev-utils'; +import { ToolingLog, pickLevelFromFlags } from '@kbn/dev-utils'; import { REPO_ROOT } from '../constants'; import { generateNoticeFromSource } from './generate_notice_from_source'; @@ -40,8 +40,10 @@ const opts = getopts(process.argv.slice(2), { } }); -const log = createToolingLog(pickLevelFromFlags(opts)); -log.pipe(process.stdout); +const log = new ToolingLog({ + level: pickLevelFromFlags(opts), + writeTo: process.stdout +}); if (unknownFlags.length) { log.error(`Unknown flags ${unknownFlags.map(f => `"${f}"`).join(',')}`); diff --git a/src/dev/run/run.js b/src/dev/run/run.js index e37cd3d7661f..d7d0b4ec30c7 100644 --- a/src/dev/run/run.js +++ b/src/dev/run/run.js @@ -17,7 +17,7 @@ * under the License. */ -import { createToolingLog, pickLevelFromFlags } from '@kbn/dev-utils'; +import { ToolingLog, pickLevelFromFlags } from '@kbn/dev-utils'; import { isFailError } from './fail'; import { getFlags, getHelp } from './flags'; @@ -29,8 +29,10 @@ export async function run(body) { process.exit(1); } - const log = createToolingLog(pickLevelFromFlags(flags)); - log.pipe(process.stdout); + const log = new ToolingLog({ + level: pickLevelFromFlags(flags), + writeTo: process.stdout + }); try { await body({ log, flags }); diff --git a/src/dev/tslint/run_tslint_cli.ts b/src/dev/tslint/run_tslint_cli.ts index 4da2c7bca789..28840e45d54a 100644 --- a/src/dev/tslint/run_tslint_cli.ts +++ b/src/dev/tslint/run_tslint_cli.ts @@ -17,7 +17,7 @@ * under the License. */ -import { createToolingLog } from '@kbn/dev-utils'; +import { ToolingLog } from '@kbn/dev-utils'; import getopts from 'getopts'; import { execInProjects, filterProjectsByFlag, Project } from '../typescript'; @@ -27,8 +27,10 @@ export function runTslintCliOnTsConfigPaths(tsConfigPaths: string[]) { } export function runTslintCli(projects?: Project[]) { - const log = createToolingLog('info'); - log.pipe(process.stdout); + const log = new ToolingLog({ + level: 'info', + writeTo: process.stdout, + }); const opts = getopts(process.argv.slice(2)); projects = projects || filterProjectsByFlag(opts.project); diff --git a/src/dev/typescript/run_type_check_cli.ts b/src/dev/typescript/run_type_check_cli.ts index 91f072dbba72..f0ddf6f56e35 100644 --- a/src/dev/typescript/run_type_check_cli.ts +++ b/src/dev/typescript/run_type_check_cli.ts @@ -17,7 +17,7 @@ * under the License. */ -import { createToolingLog } from '@kbn/dev-utils'; +import { ToolingLog } from '@kbn/dev-utils'; import chalk from 'chalk'; import dedent from 'dedent'; import getopts from 'getopts'; @@ -38,8 +38,10 @@ export function runTypeCheckCli() { }, }); - const log = createToolingLog('info'); - log.pipe(process.stdout); + const log = new ToolingLog({ + level: 'info', + writeTo: process.stdout, + }); if (extraFlags.length) { for (const flag of extraFlags) { diff --git a/src/es_archiver/cli.js b/src/es_archiver/cli.js index 456e43b4f5e9..902da0fcbefc 100644 --- a/src/es_archiver/cli.js +++ b/src/es_archiver/cli.js @@ -31,7 +31,7 @@ import { Command } from 'commander'; import elasticsearch from 'elasticsearch'; import { EsArchiver } from './es_archiver'; -import { createToolingLog } from '@kbn/dev-utils'; +import { ToolingLog } from '@kbn/dev-utils'; import { readConfigFile } from '../functional_test_runner'; const cmd = new Command('node scripts/es_archiver'); @@ -76,8 +76,10 @@ if (missingCommand) { async function execute(fn) { try { - const log = createToolingLog(cmd.verbose ? 'debug' : 'info'); - log.pipe(process.stdout); + const log = new ToolingLog({ + level: cmd.verbose ? 'debug' : 'info', + writeTo: process.stdout + }); if (cmd.config) { // load default values from the specified config file diff --git a/src/es_archiver/lib/__tests__/stats.js b/src/es_archiver/lib/__tests__/stats.js index eeb4f18766d6..eb7d9f09f858 100644 --- a/src/es_archiver/lib/__tests__/stats.js +++ b/src/es_archiver/lib/__tests__/stats.js @@ -22,18 +22,17 @@ import { uniq } from 'lodash'; import sinon from 'sinon'; import { createStats } from '../'; -import { createToolingLog } from '@kbn/dev-utils'; -import { - createConcatStream, - createPromiseFromStreams -} from '../../../utils'; +import { ToolingLog } from '@kbn/dev-utils'; -function drain(log) { - log.end(); - return createPromiseFromStreams([ - log, - createConcatStream('') - ]); +function createBufferedLog() { + const log = new ToolingLog({ + level: 'debug', + writeTo: { + write: (chunk) => log.buffer += chunk + } + }); + log.buffer = ''; + return log; } function assertDeepClones(a, b) { @@ -66,110 +65,106 @@ function assertDeepClones(a, b) { describe('esArchiver: Stats', () => { describe('#skippedIndex(index)', () => { it('marks the index as skipped', () => { - const stats = createStats('name', createToolingLog()); + const stats = createStats('name', new ToolingLog()); stats.skippedIndex('index-name'); const indexStats = stats.toJSON()['index-name']; expect(indexStats).to.have.property('skipped', true); }); it('logs that the index was skipped', async () => { - const log = createToolingLog('debug'); + const log = createBufferedLog(); const stats = createStats('name', log); stats.skippedIndex('index-name'); - expect(await drain(log)).to.contain('Skipped'); + expect(log.buffer).to.contain('Skipped'); }); }); describe('#deletedIndex(index)', () => { it('marks the index as deleted', () => { - const stats = createStats('name', createToolingLog()); + const stats = createStats('name', new ToolingLog()); stats.deletedIndex('index-name'); const indexStats = stats.toJSON()['index-name']; expect(indexStats).to.have.property('deleted', true); }); it('logs that the index was deleted', async () => { - const log = createToolingLog('debug'); + const log = createBufferedLog(); const stats = createStats('name', log); stats.deletedIndex('index-name'); - expect(await drain(log)).to.contain('Deleted'); + expect(log.buffer).to.contain('Deleted'); }); }); describe('#createdIndex(index, [metadata])', () => { it('marks the index as created', () => { - const stats = createStats('name', createToolingLog()); + const stats = createStats('name', new ToolingLog()); stats.createdIndex('index-name'); const indexStats = stats.toJSON()['index-name']; expect(indexStats).to.have.property('created', true); }); it('logs that the index was created', async () => { - const log = createToolingLog('debug'); + const log = createBufferedLog(); const stats = createStats('name', log); stats.createdIndex('index-name'); - expect(await drain(log)).to.contain('Created'); + expect(log.buffer).to.contain('Created'); }); describe('with metadata', () => { it('debug-logs each key from the metadata', async () => { - const log = createToolingLog('debug'); + const log = createBufferedLog(); const stats = createStats('name', log); stats.createdIndex('index-name', { foo: 'bar' }); - const output = await drain(log); - expect(output).to.contain('debg'); - expect(output).to.contain('foo "bar"'); + expect(log.buffer).to.contain('debg'); + expect(log.buffer).to.contain('foo "bar"'); }); }); describe('without metadata', () => { it('no debug logging', async () => { - const log = createToolingLog('debug'); + const log = createBufferedLog(); const stats = createStats('name', log); stats.createdIndex('index-name'); - const output = await drain(log); - expect(output).to.not.contain('debg'); + expect(log.buffer).to.not.contain('debg'); }); }); }); describe('#archivedIndex(index, [metadata])', () => { it('marks the index as archived', () => { - const stats = createStats('name', createToolingLog()); + const stats = createStats('name', new ToolingLog()); stats.archivedIndex('index-name'); const indexStats = stats.toJSON()['index-name']; expect(indexStats).to.have.property('archived', true); }); it('logs that the index was archived', async () => { - const log = createToolingLog('debug'); + const log = createBufferedLog(); const stats = createStats('name', log); stats.archivedIndex('index-name'); - expect(await drain(log)).to.contain('Archived'); + expect(log.buffer).to.contain('Archived'); }); describe('with metadata', () => { it('debug-logs each key from the metadata', async () => { - const log = createToolingLog('debug'); + const log = createBufferedLog(); const stats = createStats('name', log); stats.archivedIndex('index-name', { foo: 'bar' }); - const output = await drain(log); - expect(output).to.contain('debg'); - expect(output).to.contain('foo "bar"'); + expect(log.buffer).to.contain('debg'); + expect(log.buffer).to.contain('foo "bar"'); }); }); describe('without metadata', () => { it('no debug logging', async () => { - const log = createToolingLog('debug'); + const log = createBufferedLog(); const stats = createStats('name', log); stats.archivedIndex('index-name'); - const output = await drain(log); - expect(output).to.not.contain('debg'); + expect(log.buffer).to.not.contain('debg'); }); }); }); describe('#indexedDoc(index)', () => { it('increases the docs.indexed count for the index', () => { - const stats = createStats('name', createToolingLog()); + const stats = createStats('name', new ToolingLog()); stats.indexedDoc('index-name'); expect(stats.toJSON()['index-name'].docs.indexed).to.be(1); stats.indexedDoc('index-name'); @@ -180,7 +175,7 @@ describe('esArchiver: Stats', () => { describe('#archivedDoc(index)', () => { it('increases the docs.archived count for the index', () => { - const stats = createStats('name', createToolingLog()); + const stats = createStats('name', new ToolingLog()); stats.archivedDoc('index-name'); expect(stats.toJSON()['index-name'].docs.archived).to.be(1); stats.archivedDoc('index-name'); @@ -191,13 +186,13 @@ describe('esArchiver: Stats', () => { describe('#toJSON()', () => { it('returns the stats for all indexes', () => { - const stats = createStats('name', createToolingLog()); + const stats = createStats('name', new ToolingLog()); stats.archivedIndex('index1'); stats.archivedIndex('index2'); expect(Object.keys(stats.toJSON())).to.eql(['index1', 'index2']); }); it('returns a deep clone of the stats', () => { - const stats = createStats('name', createToolingLog()); + const stats = createStats('name', new ToolingLog()); stats.archivedIndex('index1'); stats.archivedIndex('index2'); stats.deletedIndex('index3'); @@ -208,7 +203,7 @@ describe('esArchiver: Stats', () => { describe('#forEachIndex(fn)', () => { it('iterates a clone of the index stats', () => { - const stats = createStats('name', createToolingLog()); + const stats = createStats('name', new ToolingLog()); stats.archivedIndex('index1'); stats.archivedIndex('index2'); stats.deletedIndex('index3'); diff --git a/src/functional_test_runner/cli.js b/src/functional_test_runner/cli.js index 3a2bd3747882..f784245c5716 100644 --- a/src/functional_test_runner/cli.js +++ b/src/functional_test_runner/cli.js @@ -21,7 +21,7 @@ import { resolve } from 'path'; import { Command } from 'commander'; -import { createToolingLog } from '@kbn/dev-utils'; +import { ToolingLog } from '@kbn/dev-utils'; import { createFunctionalTestRunner } from './functional_test_runner'; const cmd = new Command('node scripts/functional_test_runner'); @@ -46,8 +46,10 @@ if (cmd.quiet) logLevel = 'error'; if (cmd.debug) logLevel = 'debug'; if (cmd.verbose) logLevel = 'verbose'; -const log = createToolingLog(logLevel); -log.pipe(process.stdout); +const log = new ToolingLog({ + level: logLevel, + writeTo: process.stdout +}); const functionalTestRunner = createFunctionalTestRunner({ log, diff --git a/src/functional_test_runner/lib/config/__tests__/read_config_file.js b/src/functional_test_runner/lib/config/__tests__/read_config_file.js index 20bcb25c6c7b..d9da00d9b3c8 100644 --- a/src/functional_test_runner/lib/config/__tests__/read_config_file.js +++ b/src/functional_test_runner/lib/config/__tests__/read_config_file.js @@ -19,11 +19,11 @@ import expect from 'expect.js'; -import { createToolingLog } from '@kbn/dev-utils'; +import { ToolingLog } from '@kbn/dev-utils'; import { readConfigFile } from '../read_config_file'; import { Config } from '../config'; -const log = createToolingLog().resume(); +const log = new ToolingLog(); describe('readConfigFile()', () => { it('reads config from a file, returns an instance of Config class', async () => { diff --git a/src/functional_test_runner/lib/mocha/reporter/reporter.js b/src/functional_test_runner/lib/mocha/reporter/reporter.js index efced410ab93..96061a4b3846 100644 --- a/src/functional_test_runner/lib/mocha/reporter/reporter.js +++ b/src/functional_test_runner/lib/mocha/reporter/reporter.js @@ -123,25 +123,19 @@ export function MochaReporterProvider({ getService }) { console.log = realLog; } - log.indent(-2); log.write( `- ${symbols.err} ` + colors.fail(`fail: "${test.fullTitle()}"`) + '\n' + output .split('\n') - .slice(2) // drop the first two lines, (empty + test title) - .map(line => { - // move leading colors behind leading spaces - return line.replace(/^((?:\[.+m)+)(\s+)/, '$2$1'); - }) - .map(line => { - // shrink mocha's indentation - return line.replace(/^\s{5,5}/, ' '); - }) + // drop the first two lines, (empty + test title) + .slice(2) + // move leading colors behind leading spaces + .map(line => line.replace(/^((?:\[.+m)+)(\s+)/, '$2$1')) + .map(line => ` ${line}`) .join('\n') ); - log.indent(2); } onEnd = () => { diff --git a/src/ui/ui_settings/create_or_upgrade_saved_config/__tests__/create_or_upgrade_integration.js b/src/ui/ui_settings/create_or_upgrade_saved_config/__tests__/create_or_upgrade_integration.js index 2fb7a325d61c..f83c4ba8805d 100644 --- a/src/ui/ui_settings/create_or_upgrade_saved_config/__tests__/create_or_upgrade_integration.js +++ b/src/ui/ui_settings/create_or_upgrade_saved_config/__tests__/create_or_upgrade_integration.js @@ -22,7 +22,7 @@ import expect from 'expect.js'; import { createEsTestCluster } from '@kbn/test'; import { createServerWithCorePlugins } from '../../../../test_utils/kbn_server'; -import { createToolingLog } from '@kbn/dev-utils'; +import { ToolingLog } from '@kbn/dev-utils'; import { createOrUpgradeSavedConfig } from '../create_or_upgrade_saved_config'; describe('createOrUpgradeSavedConfig()', () => { @@ -31,8 +31,10 @@ describe('createOrUpgradeSavedConfig()', () => { const cleanup = []; before(async function () { - const log = createToolingLog('debug'); - log.pipe(process.stdout); + const log = new ToolingLog({ + level: 'debug', + writeTo: process.stdout + }); log.indent(6); log.info('starting elasticsearch'); diff --git a/src/ui/ui_settings/routes/__tests__/lib/servers.js b/src/ui/ui_settings/routes/__tests__/lib/servers.js index 825c5c85ca7d..f27afb54c808 100644 --- a/src/ui/ui_settings/routes/__tests__/lib/servers.js +++ b/src/ui/ui_settings/routes/__tests__/lib/servers.js @@ -18,7 +18,7 @@ */ import { createEsTestCluster } from '@kbn/test'; -import { createToolingLog } from '@kbn/dev-utils'; +import { ToolingLog } from '@kbn/dev-utils'; import * as kbnTestServer from '../../../../../test_utils/kbn_server'; let kbnServer; @@ -26,8 +26,10 @@ let services; let es; export async function startServers() { - const log = createToolingLog('debug'); - log.pipe(process.stdout); + const log = new ToolingLog({ + level: 'debug', + writeTo: process.stdout + }); log.indent(6); log.info('starting elasticsearch'); diff --git a/tasks/functional_test_runner.js b/tasks/functional_test_runner.js index 1c72b50d849f..91c49e090ed5 100644 --- a/tasks/functional_test_runner.js +++ b/tasks/functional_test_runner.js @@ -18,7 +18,7 @@ */ import { createFunctionalTestRunner } from '../src/functional_test_runner'; -import { createToolingLog } from '@kbn/dev-utils'; +import { ToolingLog } from '@kbn/dev-utils'; export default function (grunt) { grunt.registerMultiTask('functional_test_runner', 'run tests with the functional test runner', function () { @@ -28,8 +28,10 @@ export default function (grunt) { configOverrides } = this.options(); - const log = createToolingLog(logLevel); - log.pipe(process.stdout); + const log = new ToolingLog({ + level: logLevel, + writeTo: process.stdout, + }); const functionalTestRunner = createFunctionalTestRunner({ log, diff --git a/x-pack/gulpfile.js b/x-pack/gulpfile.js index 503adf91cb47..9d45efa40f0d 100644 --- a/x-pack/gulpfile.js +++ b/x-pack/gulpfile.js @@ -15,7 +15,7 @@ const path = require('path'); const del = require('del'); const runSequence = require('run-sequence'); const pluginHelpers = require('@kbn/plugin-helpers'); -const { createToolingLog } = require('@kbn/dev-utils'); +const { ToolingLog } = require('@kbn/dev-utils'); const logger = require('./gulp_helpers/logger'); const buildVersion = require('./gulp_helpers/build_version')(); @@ -76,8 +76,10 @@ gulp.task('build', ['clean', 'report', 'prepare'], async () => { }); const buildRoot = path.resolve(buildTarget, 'kibana/x-pack'); - const log = createToolingLog('info'); - log.pipe(process.stdout); + const log = new ToolingLog({ + level: 'info', + writeTo: process.stdout + }); writeFileSync( path.resolve(buildRoot, 'NOTICE.txt'),