From d9ff02c0af506e4991315ae07a06eb6a47f2f089 Mon Sep 17 00:00:00 2001 From: Mert Can Altin Date: Sat, 4 Oct 2025 02:46:46 +0300 Subject: [PATCH 1/2] lib: create util.log function --- benchmark/util/log.js | 43 ++++++++++++ doc/api/util.md | 81 ++++++++++++++++++++++ lib/internal/util/log.js | 111 ++++++++++++++++++++++++++++++ lib/util.js | 10 +++ src/node_util.cc | 34 +++++++++ test/parallel/test-util-log.js | 122 +++++++++++++++++++++++++++++++++ 6 files changed, 401 insertions(+) create mode 100644 benchmark/util/log.js create mode 100644 lib/internal/util/log.js create mode 100644 test/parallel/test-util-log.js diff --git a/benchmark/util/log.js b/benchmark/util/log.js new file mode 100644 index 00000000000000..ef3d250d4e4b0a --- /dev/null +++ b/benchmark/util/log.js @@ -0,0 +1,43 @@ +'use strict'; + +const common = require('../common.js'); +const util = require('util'); + +const bench = common.createBenchmark(main, { + method: ['simple_string', 'object_simple', 'object_complex'], + n: [1e4], +}); + +function main({ n, method }) { + const complexObject = { + name: 'test', + value: 123, + nested: { + array: [1, 2, 3, 4, 5], + boolean: true, + null: null, + }, + }; + + bench.start(); + + switch (method) { + case 'simple_string': + for (let i = 0; i < n; i++) { + util.log('Hello World'); + } + break; + case 'object_simple': + for (let i = 0; i < n; i++) { + util.log('Number:', i, 'Boolean:', true); + } + break; + case 'object_complex': + for (let i = 0; i < n; i++) { + util.log('Object:', complexObject); + } + break; + } + + bench.end(n); +} diff --git a/doc/api/util.md b/doc/api/util.md index 0bb5c5c91958d5..7affaa74eb29f5 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -1574,6 +1574,87 @@ inspect.defaultOptions.maxArrayLength = null; console.log(arr); // logs the full array ``` +## `util.log([...args])` + + + +* `...args` {any} + +A high-performance logging function optimized for scenarios where output is +redirected to files or pipes. The `util.log()` method formats its arguments +using [`util.format()`][] and writes the result to `stdout` with a trailing +newline. + +```mjs +import { log } from 'node:util'; + +log('Hello', 'World'); +// Prints: Hello World + +log('User %s logged in', 'alice'); +// Prints: User alice logged in +``` + +```cjs +const { log } = require('node:util'); + +log('Hello', 'World'); +// Prints: Hello World +``` + +### Performance characteristics + +The behavior differs based on the output destination: + +* **TTY (interactive terminal)**: Synchronous writes for immediate visibility +* **Pipe/File (redirected output)**: Asynchronous buffering for performance + * 16KB buffer size + * 10ms flush interval + * Automatic flush on `process.beforeExit` + +This makes `util.log()` ideal for high-frequency logging when output is +redirected, while maintaining the same behavior as `console.log()` for +interactive use. + +```mjs +import { log } from 'node:util'; + +// High-frequency logging example +// When redirected (node script.js > output.log): +// - Buffered and batched for better performance +// When run in terminal: +// - Immediate output, same as console.log() + +for (let i = 0; i < 10000; i++) { + log('Processing item', i); +} +``` + +### When to use `util.log()` vs `console.log()` + +Use `util.log()` when: + +* You have high-frequency logging (e.g., logging every request in a server) +* Output is redirected to files or pipes +* You want to reduce CPU usage from logging overhead + +Use `console.log()` when: + +* You need guaranteed immediate output before `process.exit()` +* You're debugging and need synchronous behavior +* You want consistency with browser JavaScript + +**Performance impact**: In high-frequency logging scenarios with redirected +output, `util.log()` can significantly reduce logging overhead compared to +`console.log()` through asynchronous buffering and batched writes. + +**Important**: Messages logged immediately before `process.exit()` may not be +written when output is buffered. Use `console.log()` for critical final +messages, or avoid calling `process.exit()` directly (let the process exit +naturally). + ## `util.isDeepStrictEqual(val1, val2[, options])`