From 28bec2dda33e9aba8ee15138d0d93a873933c312 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Mon, 13 Jan 2025 20:51:31 +0800 Subject: [PATCH] diagnostics_channel: capture console messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/56292 Reviewed-By: Rafael Gonzaga Reviewed-By: Gerhard Stöbich Reviewed-By: Kohei Ueno Reviewed-By: Chengzhong Wu Reviewed-By: James M Snell Reviewed-By: Chemi Atlow --- doc/api/diagnostics_channel.md | 37 +++++++++ lib/internal/console/constructor.js | 35 ++++++++- .../test-console-diagnostics-channels.js | 77 +++++++++++++++++++ test/parallel/test-repl.js | 6 +- 4 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 test/parallel/test-console-diagnostics-channels.js diff --git a/doc/api/diagnostics_channel.md b/doc/api/diagnostics_channel.md index 9b4a85f3c0bda6..e6531cba8479ed 100644 --- a/doc/api/diagnostics_channel.md +++ b/doc/api/diagnostics_channel.md @@ -1120,6 +1120,43 @@ While the diagnostics\_channel API is now considered stable, the built-in channels currently available are not. Each channel must be declared stable independently. +#### Console + +`console.log` + +* `args` {any\[]} + +Emitted when `console.log()` is called. Receives and array of the arguments +passed to `console.log()`. + +`console.info` + +* `args` {any\[]} + +Emitted when `console.info()` is called. Receives and array of the arguments +passed to `console.info()`. + +`console.debug` + +* `args` {any\[]} + +Emitted when `console.debug()` is called. Receives and array of the arguments +passed to `console.debug()`. + +`console.warn` + +* `args` {any\[]} + +Emitted when `console.warn()` is called. Receives and array of the arguments +passed to `console.warn()`. + +`console.error` + +* `args` {any\[]} + +Emitted when `console.error()` is called. Receives and array of the arguments +passed to `console.error()`. + #### HTTP `http.client.request.created` diff --git a/lib/internal/console/constructor.js b/lib/internal/console/constructor.js index cefb50fb35e32b..26f2e837d74f6f 100644 --- a/lib/internal/console/constructor.js +++ b/lib/internal/console/constructor.js @@ -61,6 +61,13 @@ const { } = require('internal/constants'); const kCounts = Symbol('counts'); const { time, timeLog, timeEnd, kNone } = require('internal/util/debuglog'); +const { channel } = require('diagnostics_channel'); + +const onLog = channel('console.log'); +const onWarn = channel('console.warn'); +const onError = channel('console.error'); +const onInfo = channel('console.info'); +const onDebug = channel('console.debug'); const kTraceConsoleCategory = 'node,node.console'; @@ -371,14 +378,39 @@ function timeLogImpl(consoleRef, label, formatted, args) { const consoleMethods = { log(...args) { + if (onLog.hasSubscribers) { + onLog.publish(args); + } this[kWriteToConsole](kUseStdout, this[kFormatForStdout](args)); }, + info(...args) { + if (onInfo.hasSubscribers) { + onInfo.publish(args); + } + this[kWriteToConsole](kUseStdout, this[kFormatForStdout](args)); + }, + + debug(...args) { + if (onDebug.hasSubscribers) { + onDebug.publish(args); + } + this[kWriteToConsole](kUseStdout, this[kFormatForStdout](args)); + }, warn(...args) { + if (onWarn.hasSubscribers) { + onWarn.publish(args); + } this[kWriteToConsole](kUseStderr, this[kFormatForStderr](args)); }, + error(...args) { + if (onError.hasSubscribers) { + onError.publish(args); + } + this[kWriteToConsole](kUseStderr, this[kFormatForStderr](args)); + }, dir(object, options) { this[kWriteToConsole](kUseStdout, inspect(object, { @@ -614,10 +646,7 @@ function noop() {} for (const method of ReflectOwnKeys(consoleMethods)) Console.prototype[method] = consoleMethods[method]; -Console.prototype.debug = Console.prototype.log; -Console.prototype.info = Console.prototype.log; Console.prototype.dirxml = Console.prototype.log; -Console.prototype.error = Console.prototype.warn; Console.prototype.groupCollapsed = Console.prototype.group; function initializeGlobalConsole(globalConsole) { diff --git a/test/parallel/test-console-diagnostics-channels.js b/test/parallel/test-console-diagnostics-channels.js new file mode 100644 index 00000000000000..1a12d7a78f0b71 --- /dev/null +++ b/test/parallel/test-console-diagnostics-channels.js @@ -0,0 +1,77 @@ +'use strict'; + +const { mustCall } = require('../common'); +const { deepStrictEqual, ok, strictEqual } = require('assert'); + +const { channel } = require('diagnostics_channel'); + +const { + hijackStdout, + hijackStderr, + restoreStdout, + restoreStderr +} = require('../common/hijackstdio'); + +const stdoutMethods = [ + 'log', + 'info', + 'debug', +]; + +const stderrMethods = [ + 'warn', + 'error', +]; + +const methods = [ + ...stdoutMethods, + ...stderrMethods, +]; + +const channels = { + log: channel('console.log'), + info: channel('console.info'), + debug: channel('console.debug'), + warn: channel('console.warn'), + error: channel('console.error') +}; + +process.stdout.isTTY = false; +process.stderr.isTTY = false; + +for (const method of methods) { + let intercepted = false; + let formatted = false; + + const isStdout = stdoutMethods.includes(method); + const hijack = isStdout ? hijackStdout : hijackStderr; + const restore = isStdout ? restoreStdout : restoreStderr; + + const foo = 'string'; + const bar = { key: /value/ }; + const baz = [ 1, 2, 3 ]; + + channels[method].subscribe(mustCall((args) => { + // Should not have been formatted yet. + intercepted = true; + ok(!formatted); + + // Should receive expected log message args. + deepStrictEqual(args, [foo, bar, baz]); + + // Should be able to mutate message args and have it reflected in output. + bar.added = true; + })); + + hijack(mustCall((output) => { + // Should have already been intercepted. + formatted = true; + ok(intercepted); + + // Should produce expected formatted output with mutated message args. + strictEqual(output, 'string { key: /value/, added: true } [ 1, 2, 3 ]\n'); + })); + + console[method](foo, bar, baz); + restore(); +} diff --git a/test/parallel/test-repl.js b/test/parallel/test-repl.js index 610c7813e0439c..be389072ce3638 100644 --- a/test/parallel/test-repl.js +++ b/test/parallel/test-repl.js @@ -792,7 +792,10 @@ const errorTests = [ expect: [ 'Object [console] {', ' log: [Function: log],', + ' info: [Function: info],', + ' debug: [Function: debug],', ' warn: [Function: warn],', + ' error: [Function: error],', ' dir: [Function: dir],', ' time: [Function: time],', ' timeEnd: [Function: timeEnd],', @@ -805,10 +808,7 @@ const errorTests = [ ' group: [Function: group],', ' groupEnd: [Function: groupEnd],', ' table: [Function: table],', - / {2}debug: \[Function: (debug|log)],/, - / {2}info: \[Function: (info|log)],/, / {2}dirxml: \[Function: (dirxml|log)],/, - / {2}error: \[Function: (error|warn)],/, / {2}groupCollapsed: \[Function: (groupCollapsed|group)],/, / {2}Console: \[Function: Console],?/, ...process.features.inspector ? [