Skip to content

Commit ad43d25

Browse files
committed
add enabled property to logger methods for performance checks
1 parent b856491 commit ad43d25

File tree

2 files changed

+78
-12
lines changed

2 files changed

+78
-12
lines changed

doc/api/logger.md

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,30 @@ requestLogger.info('Processing request');
260260
// Log includes: service: 'my-app', requestId: 'abc123'
261261
```
262262

263+
### `logger.<level>.enabled`
264+
265+
<!-- YAML
266+
added: REPLACEME
267+
-->
268+
269+
* {boolean} `true` if the level is enabled, `false` otherwise.
270+
271+
Each log method (`trace`, `debug`, `info`, `warn`, `error`, `fatal`) has an
272+
`enabled` property that indicates whether that level is enabled for this logger.
273+
274+
This is the recommended way to check if a level is enabled before performing
275+
expensive computations:
276+
277+
```js
278+
if (logger.debug.enabled) {
279+
// Perform expensive debug computation only if debug is enabled
280+
logger.debug('Debug info', { expensiveData: computeDebugData() });
281+
}
282+
283+
// Typos will throw a TypeError (safer than silent failure)
284+
// logger.debg.enabled → TypeError: Cannot read properties of undefined
285+
```
286+
263287
### `logger.enabled(level)`
264288

265289
<!-- YAML
@@ -269,12 +293,19 @@ added: REPLACEME
269293
* `level` {string} The log level to check.
270294
* Returns: {boolean} `true` if the level is enabled, `false` otherwise.
271295

272-
Checks if a specific log level is enabled for this logger.
296+
Checks if a specific log level is enabled for this logger. For most use cases,
297+
prefer using [`logger.<level>.enabled`][] instead, as it catches typos at
298+
runtime.
273299

274300
```js
301+
// Prefer this:
302+
if (logger.debug.enabled) {
303+
logger.debug('message');
304+
}
305+
306+
// Over this (typos won't throw):
275307
if (logger.enabled('debug')) {
276-
// Perform expensive debug computation
277-
logger.debug('Debug info', { expensiveData: computeDebugData() });
308+
logger.debug('message');
278309
}
279310
```
280311

@@ -747,5 +778,4 @@ class ConsoleColorConsumer extends LogConsumer {
747778
const consumer = new ConsoleColorConsumer({ level: 'debug' });
748779
consumer.attach();
749780
```
750-
751-
[RFC 5424]: https://www.rfc-editor.org/rfc/rfc5424.html
781+
[`logger.<level>.enabled`]: #loggerlevelenabled

lib/logger.js

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const {
33
DateNow,
44
JSONStringify,
55
ObjectAssign,
6+
ObjectDefineProperty,
67
ObjectHasOwn,
78
ObjectKeys,
89
SafeSet,
@@ -303,18 +304,53 @@ class Logger {
303304
}
304305

305306
/**
306-
* Replace disabled log methods with noop for performance
307+
* Setup log methods with enabled getters
307308
* @private
308309
*/
309310
#setLogMethods() {
310311
const levelValue = this.#levelValue;
311312

312-
if (levelValue > LEVELS.trace) this.trace = noop;
313-
if (levelValue > LEVELS.debug) this.debug = noop;
314-
if (levelValue > LEVELS.info) this.info = noop;
315-
if (levelValue > LEVELS.warn) this.warn = noop;
316-
if (levelValue > LEVELS.error) this.error = noop;
317-
if (levelValue > LEVELS.fatal) this.fatal = noop;
313+
// Setup each log level method with .enabled getter
314+
this.trace = this.#createLogMethod('trace', LEVELS.trace, levelValue);
315+
this.debug = this.#createLogMethod('debug', LEVELS.debug, levelValue);
316+
this.info = this.#createLogMethod('info', LEVELS.info, levelValue);
317+
this.warn = this.#createLogMethod('warn', LEVELS.warn, levelValue);
318+
this.error = this.#createLogMethod('error', LEVELS.error, levelValue);
319+
this.fatal = this.#createLogMethod('fatal', LEVELS.fatal, levelValue);
320+
}
321+
322+
/**
323+
* Create a log method with .enabled getter
324+
* @param {string} level - Log level name
325+
* @param {number} methodLevelValue - Numeric value of this level
326+
* @param {number} loggerLevelValue - Logger's minimum level value
327+
* @returns {Function} Log method with .enabled property
328+
* @private
329+
*/
330+
#createLogMethod(level, methodLevelValue, loggerLevelValue) {
331+
const enabled = methodLevelValue >= loggerLevelValue;
332+
333+
let fn;
334+
if (enabled) {
335+
// Create bound log function
336+
fn = (msgOrObj, fields) => {
337+
this.#log(level, methodLevelValue, msgOrObj, fields);
338+
};
339+
} else {
340+
// Use noop for disabled levels
341+
fn = noop;
342+
}
343+
344+
// Add .enabled getter
345+
ObjectDefineProperty(fn, 'enabled', {
346+
__proto__: null,
347+
value: enabled,
348+
writable: false,
349+
enumerable: true,
350+
configurable: false,
351+
});
352+
353+
return fn;
318354
}
319355

320356
/**

0 commit comments

Comments
 (0)