From 572d1fba51a83716dd48791b44535db33aad6eb3 Mon Sep 17 00:00:00 2001 From: commenthol Date: Sun, 9 Feb 2025 21:17:32 +0100 Subject: [PATCH 1/3] feat(LogEcs): static methods wrapConsole, wrapDebug on LogEcs --- src/ecs/LogEcs.js | 30 ++++++++++++++++++++------- src/wrapDebug.js | 4 ++-- test/LogEcs.test.js | 47 +++++++++++++++++++++++++++++++++++++++++++ types/ecs/LogEcs.d.ts | 21 ++++++++++++------- types/wrapDebug.d.ts | 2 +- 5 files changed, 87 insertions(+), 17 deletions(-) diff --git a/src/ecs/LogEcs.js b/src/ecs/LogEcs.js index fa68100..eba1590 100644 --- a/src/ecs/LogEcs.js +++ b/src/ecs/LogEcs.js @@ -1,13 +1,11 @@ import { Log, stringify } from '../node.js' import { ecsSerializers } from './serializers.js' +import { wrapConsole } from '../wrapConsole.js' +import { wrapDebug } from '../wrapDebug.js' -/** - * @typedef {import('../serializers/index.js').Serializer} Serializer - */ -/** - * @typedef {import('../node.js').LogOptions & {serializers: Record}} LogOptionsEcs - * @typedef {import('../node.js').LogOptionWrapConsole} LogOptionWrapConsole - */ +/** @typedef {import('../serializers/index.js').Serializer} Serializer */ +/** @typedef {import('../node.js').LogOptions & {serializers: Record}} LogOptionsEcs */ +/** @typedef {import('../node.js').LogOptionWrapConsole} LogOptionWrapConsole */ /** * Elastic Common Schema (ECS) compatible logger; @@ -33,6 +31,24 @@ export class LogEcs extends Log { this.toJson = this._toJson } + /** + * @param {string} [name] + * @param {LogOptionsEcs & LogOptionWrapConsole} [opts] + * @returns {() => void} unwrap function + */ + static wrapConsole(name = 'console', opts) { + const log = new LogEcs(name, opts) + return wrapConsole(log, opts) + } + + /** + * @param {LogOptionsEcs} [opts] + * @returns {() => void} unwrap function + */ + static wrapDebug(opts) { + return wrapDebug(LogEcs, opts) + } + /* c8 ignore next 18 */ _applySerializers(obj) { const ecsObj = {} diff --git a/src/wrapDebug.js b/src/wrapDebug.js index ae7dcc7..e1cb5ba 100644 --- a/src/wrapDebug.js +++ b/src/wrapDebug.js @@ -11,7 +11,7 @@ const unwrap = () => { } } -export function wrapDebug(Log) { +export function wrapDebug(Log, opts) { if (wrapped) return unwrap class Loggers { constructor() { @@ -21,7 +21,7 @@ export function wrapDebug(Log) { get(namespace) { let logger = this.cache[namespace] if (!logger) { - logger = this.cache[namespace] = new Log(namespace) + logger = this.cache[namespace] = new Log(namespace, opts) } return logger } diff --git a/test/LogEcs.test.js b/test/LogEcs.test.js index a2a1479..db11f25 100644 --- a/test/LogEcs.test.js +++ b/test/LogEcs.test.js @@ -1,6 +1,7 @@ import assert from 'assert' import os from 'os' import sinon from 'sinon' +import debug from 'debug' import { startTimeKey } from '../src/serializers/res.js' import { LogEcs, ecsSerializers } from '../src/ecs/index.js' import { httpLogs, logger } from '../src/index.js' @@ -413,4 +414,50 @@ describe('LogEcs', function () { ) }) }) + + describe('wrap console', function () { + let unwrap + before(function () { + unwrap = LogEcs.wrapConsole('test', { + level: 'trace', + namespaces: 'test' + }) + }) + after(function () { + unwrap() + }) + + it('shall wrap console.log', function () { + console.log('log %s', 'log') + console.trace('trace') + console.debug({ debug: true }) + console.info('log %j', { info: 1 }) + console.warn('warn') + console.error(new Error('Baam')) + }) + + it('shall not wrap console twice', function () { + const unwrap1 = LogEcs.wrapConsole('test1') + const unwrap2 = LogEcs.wrapConsole('test2') + assert.strictEqual(unwrap1, unwrap) + assert.strictEqual(unwrap2, unwrap) + }) + }) + + describe('wrap debug', function () { + let unwrap + before(function () { + const options = { level: 'debug', namespaces: '*' } + unwrap = LogEcs.wrapDebug(options) + }) + after(function () { + unwrap() + }) + + it('shall wrap debug', function () { + const log = debug('namespace') + log.enabled = '*' + log('hello %s', 'log') + }) + }) }) diff --git a/types/ecs/LogEcs.d.ts b/types/ecs/LogEcs.d.ts index 49c070c..a927fee 100644 --- a/types/ecs/LogEcs.d.ts +++ b/types/ecs/LogEcs.d.ts @@ -1,10 +1,6 @@ -/** - * @typedef {import('../serializers/index.js').Serializer} Serializer - */ -/** - * @typedef {import('../node.js').LogOptions & {serializers: Record}} LogOptionsEcs - * @typedef {import('../node.js').LogOptionWrapConsole} LogOptionWrapConsole - */ +/** @typedef {import('../serializers/index.js').Serializer} Serializer */ +/** @typedef {import('../node.js').LogOptions & {serializers: Record}} LogOptionsEcs */ +/** @typedef {import('../node.js').LogOptionWrapConsole} LogOptionWrapConsole */ /** * Elastic Common Schema (ECS) compatible logger; * See [field reference](https://www.elastic.co/guide/en/ecs/current/ecs-field-reference.html) @@ -13,6 +9,17 @@ * (response) keys in objects */ export class LogEcs extends Log { + /** + * @param {string} [name] + * @param {LogOptionsEcs & LogOptionWrapConsole} [opts] + * @returns {() => void} unwrap function + */ + static wrapConsole(name?: string, opts?: LogOptionsEcs & LogOptionWrapConsole): () => void; + /** + * @param {LogOptionsEcs} [opts] + * @returns {() => void} unwrap function + */ + static wrapDebug(opts?: LogOptionsEcs): () => void; /** * @param {string} name logger namespace * @param {LogOptionsEcs} [opts] diff --git a/types/wrapDebug.d.ts b/types/wrapDebug.d.ts index 7805f98..6a57ae0 100644 --- a/types/wrapDebug.d.ts +++ b/types/wrapDebug.d.ts @@ -1 +1 @@ -export function wrapDebug(Log: any): () => void; +export function wrapDebug(Log: any, opts: any): () => void; From 1dee762ccdb7447778c5a7a0c690f8aa6fe90ac1 Mon Sep 17 00:00:00 2001 From: commenthol Date: Mon, 10 Feb 2025 18:29:13 +0100 Subject: [PATCH 2/3] fix(node): wrapDebug with options --- src/node.js | 9 +++++---- types/node.d.ts | 5 +++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/node.js b/src/node.js index 1f5b283..d07a1af 100644 --- a/src/node.js +++ b/src/node.js @@ -92,7 +92,7 @@ export class Log extends LogBase { options.serializers, opts ? opts.serializers : {} ) - const _opts = Object.assign({}, options, opts, { serializers }) + const _opts = { ...options, ...opts, serializers } super(name, _opts) const colorFn = (n) => chalk.hex(n) @@ -195,10 +195,11 @@ export class Log extends LogBase { } /** - * @returns {() => void} unwrap functions + * @param {LogOptions} [opts] - see Log.options + * @returns {() => void} unwrap function */ - static wrapDebug() { - return wrapDebug(Log) + static wrapDebug(opts) { + return wrapDebug(Log, opts) } /** diff --git a/types/node.d.ts b/types/node.d.ts index 6ab7ced..9cccd66 100644 --- a/types/node.d.ts +++ b/types/node.d.ts @@ -35,9 +35,10 @@ export class Log extends LogBase { */ static handleExitEvents(name?: string, opts?: LogOptionHandleExitEvents): void; /** - * @returns {() => void} unwrap functions + * @param {LogOptions} [opts] - see Log.options + * @returns {() => void} unwrap function */ - static wrapDebug(): () => void; + static wrapDebug(opts?: LogOptions): () => void; /** * creates a new logger * @param {String} name - namespace of Logger From 4f032a86fb59cc66c337eb7fff933c1e7ce1a934 Mon Sep 17 00:00:00 2001 From: commenthol Date: Mon, 10 Feb 2025 18:29:29 +0100 Subject: [PATCH 3/3] feat(ProcLog): static methods wrapConsole, wrapDebug on ProcLog --- src/ProcLog.js | 26 +++++- test/ProcLog.test.js | 202 +++++++++++++++++++++++++++---------------- types/ProcLog.d.ts | 13 +++ 3 files changed, 164 insertions(+), 77 deletions(-) diff --git a/src/ProcLog.js b/src/ProcLog.js index fb080d8..75d20b4 100644 --- a/src/ProcLog.js +++ b/src/ProcLog.js @@ -1,10 +1,14 @@ import { LogBase } from './LogBase.js' import { Log } from './node.js' import { inspectOpts, inspectNamespaces, INFO } from './utils.js' +import { wrapConsole } from './wrapConsole.js' +import { wrapDebug } from './wrapDebug.js' /** @typedef {import('./utils.js').Level} Level */ /** @typedef {import('./node.js').LogOptions} LogOptions */ +/** @typedef {import('./node.js').LogOptionWrapConsole} LogOptionWrapConsole */ /** @typedef {LogOptions & {Log: typeof Log}} LogOptionsWithCustomLog */ + /** * @typedef {object} ProcLogOptions * @property {Level} [level] log level @@ -13,7 +17,7 @@ import { inspectOpts, inspectNamespaces, INFO } from './utils.js' export const EVENT_PROC_LOG = 'log-level' -const defaultOptions = { +const options = { level: INFO, namespaces: undefined } @@ -57,7 +61,7 @@ export class ProcLog extends LogBase { */ constructor(name, opts) { const _opts = { - ...defaultOptions, + ...options, ...inspectOpts(process.env), ...inspectNamespaces(process.env), ...opts, @@ -73,6 +77,24 @@ export class ProcLog extends LogBase { // @ts-expect-error process.emit(EVENT_PROC_LOG, level, this.name, fmt, args) } + + /** + * @param {string} [name] + * @param {ProcLogOptions & LogOptionWrapConsole} [opts] + * @returns {() => void} unwrap functions + */ + static wrapConsole(name = 'console', opts) { + const log = new ProcLog(name, opts) + return wrapConsole(log, opts) + } + + /** + * @param {ProcLogOptions} [opts] + * @returns {() => void} unwrap functions + */ + static wrapDebug(opts) { + return wrapDebug(ProcLog, opts) + } } /** diff --git a/test/ProcLog.test.js b/test/ProcLog.test.js index cb5ecd7..57223ed 100644 --- a/test/ProcLog.test.js +++ b/test/ProcLog.test.js @@ -1,95 +1,147 @@ import assert from 'node:assert' import { LogEcs } from '../src/index.js' import { ProcLog, initProcLog, EVENT_PROC_LOG } from '../src/ProcLog.js' +import debug from 'debug' describe('ProcLog', function () { - beforeEach(function () { - initProcLog() - }) - - it('should log via process.emit', function () { - const { lines } = myInitProcLog() - const log = new ProcLog('test:1') - log.log('a log line') - assert.deepEqual(lines, ['LOG', 'test:1', 'a log line', []]) - }) + describe('general', function () { + beforeEach(function () { + initProcLog() + }) - it('should log deep object', function () { - const { lines } = myInitProcLog() - const log = new ProcLog('test:2') - log.log({ a: { nested: 'object' } }) - assert.deepEqual(lines, [ - 'LOG', - 'test:2', - { - a: { - nested: 'object' - } - }, - [] - ]) - }) + it('should log via process.emit', function () { + const { lines } = myInitProcLog() + const log = new ProcLog('test:1') + log.log('a log line') + assert.deepEqual(lines, ['LOG', 'test:1', 'a log line', []]) + }) - it('should log deep object with format', function () { - const { lines } = myInitProcLog() - const log = new ProcLog('test:2') - log.info('%j', { a: { nested: 'object' } }) - assert.deepEqual(lines, [ - 'INFO', - 'test:2', - '%j', - [ + it('should log deep object', function () { + const { lines } = myInitProcLog() + const log = new ProcLog('test:2') + log.log({ a: { nested: 'object' } }) + assert.deepEqual(lines, [ + 'LOG', + 'test:2', { a: { nested: 'object' } - } - ] - ]) - }) + }, + [] + ]) + }) - it('should use the Ecs logger', function () { - initProcLog({ Log: LogEcs, json: true, colors: false }) - const { lines } = myInitProcLog() - const log = new ProcLog('test:3') - log.warn('%j', { a: { nested: 'object' } }) - assert.deepEqual(lines, [ - 'WARN', - 'test:3', - '%j', - [ - { - a: { - nested: 'object' + it('should log deep object with format', function () { + const { lines } = myInitProcLog() + const log = new ProcLog('test:2') + log.info('%j', { a: { nested: 'object' } }) + assert.deepEqual(lines, [ + 'INFO', + 'test:2', + '%j', + [ + { + a: { + nested: 'object' + } + } + ] + ]) + }) + + it('should use the Ecs logger', function () { + initProcLog({ Log: LogEcs, json: true, colors: false }) + const { lines } = myInitProcLog() + const log = new ProcLog('test:3') + log.warn('%j', { a: { nested: 'object' } }) + assert.deepEqual(lines, [ + 'WARN', + 'test:3', + '%j', + [ + { + a: { + nested: 'object' + } + } + ] + ]) + }) + + it('should not use number level or serializers', function () { + initProcLog({ + levelNumbers: true, + serializers: { + err: (err) => { + return err?.message } } - ] - ]) + }) + const { lines } = myInitProcLog() + const log = new ProcLog('test:4') + log.error({ err: { name: 'Error', message: 'error' } }) + assert.deepEqual(lines, [ + 'ERROR', + 'test:4', + { + err: { + message: 'error', + name: 'Error' + } + }, + [] + ]) + }) }) - it('should not use number level or serializers', function () { - initProcLog({ - levelNumbers: true, - serializers: { - err: (err) => { - return err?.message - } - } + describe('wrap console', function () { + let unwrap + before(function () { + initProcLog() + + unwrap = ProcLog.wrapConsole('test', { + level: 'trace', + namespaces: 'test' + }) + }) + after(function () { + unwrap() + }) + + it('shall wrap console.log', function () { + console.log('log %s', 'log') + console.trace('trace') + console.debug({ debug: true }) + console.info('log %j', { info: 1 }) + console.warn('warn') + console.error(new Error('Baam')) + }) + + it('shall not wrap console twice', function () { + const unwrap1 = ProcLog.wrapConsole('test1') + const unwrap2 = ProcLog.wrapConsole('test2') + assert.strictEqual(unwrap1, unwrap) + assert.strictEqual(unwrap2, unwrap) + }) + }) + + describe('wrap debug', function () { + let unwrap + before(function () { + const options = { level: 'debug', namespaces: '*' } + initProcLog(options) + unwrap = ProcLog.wrapDebug(options) + }) + after(function () { + unwrap() + }) + + it('shall wrap debug', function () { + const log = debug('namespace') + log.enabled = '*' + log('hello %s', 'log') }) - const { lines } = myInitProcLog() - const log = new ProcLog('test:4') - log.error({ err: { name: 'Error', message: 'error' } }) - assert.deepEqual(lines, [ - 'ERROR', - 'test:4', - { - err: { - message: 'error', - name: 'Error' - } - }, - [] - ]) }) }) diff --git a/types/ProcLog.d.ts b/types/ProcLog.d.ts index fb0b2b4..56aa680 100644 --- a/types/ProcLog.d.ts +++ b/types/ProcLog.d.ts @@ -5,6 +5,7 @@ export function initProcLog(options?: LogOptionsWithCustomLog): void; /** @typedef {import('./utils.js').Level} Level */ /** @typedef {import('./node.js').LogOptions} LogOptions */ +/** @typedef {import('./node.js').LogOptionWrapConsole} LogOptionWrapConsole */ /** @typedef {LogOptions & {Log: typeof Log}} LogOptionsWithCustomLog */ /** * @typedef {object} ProcLogOptions @@ -44,6 +45,17 @@ export const EVENT_PROC_LOG: "log-level"; * ``` */ export class ProcLog extends LogBase { + /** + * @param {string} [name] + * @param {ProcLogOptions & LogOptionWrapConsole} [opts] + * @returns {() => void} unwrap functions + */ + static wrapConsole(name?: string, opts?: ProcLogOptions & LogOptionWrapConsole): () => void; + /** + * @param {ProcLogOptions} [opts] + * @returns {() => void} unwrap functions + */ + static wrapDebug(opts?: ProcLogOptions): () => void; /** * creates a new logger * @param {string} name - namespace of Logger @@ -53,6 +65,7 @@ export class ProcLog extends LogBase { } export type Level = import("./utils.js").Level; export type LogOptions = import("./node.js").LogOptions; +export type LogOptionWrapConsole = import("./node.js").LogOptionWrapConsole; export type LogOptionsWithCustomLog = LogOptions & { Log: typeof Log; };