From e057d1d231d45695d277d3943149ecd6a60a90da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Fri, 19 Jan 2024 10:29:06 +0000 Subject: [PATCH 1/2] feat: add `Silent` log level --- src/lib/system_logger.ts | 5 +-- test/unit/system_logger.js | 65 +++++++++++++++++--------------------- 2 files changed, 30 insertions(+), 40 deletions(-) diff --git a/src/lib/system_logger.ts b/src/lib/system_logger.ts index 80afab13..c537899f 100644 --- a/src/lib/system_logger.ts +++ b/src/lib/system_logger.ts @@ -18,6 +18,7 @@ export enum LogLevel { Debug = 1, Log, Error, + Silent = Infinity, } class SystemLogger { @@ -30,10 +31,6 @@ class SystemLogger { } private doLog(logger: typeof console.log, message: string) { - if (env.NETLIFY_DEV && !env.NETLIFY_ENABLE_SYSTEM_LOGGING) { - return - } - logger(systemLogTag, JSON.stringify({ msg: message, fields: this.fields })) } diff --git a/test/unit/system_logger.js b/test/unit/system_logger.js index 712dfb28..3e2b52d1 100644 --- a/test/unit/system_logger.js +++ b/test/unit/system_logger.js @@ -1,29 +1,44 @@ -const process = require("process") - const test = require('ava') const { systemLogger, LogLevel } = require('../../dist/internal') -test('Log Level', (t) => { - const originalDebug = console.debug +const consoleDebug = console.debug +const consoleError = console.error +const consoleLog = console.log + +test.afterEach(() => { + console.debug = consoleDebug + console.error = consoleError + console.log = consoleLog +}) + +test('Log levels', (t) => { + const logs = { + debug: [], + error: [], + log: [], + } + console.debug = (...message) => logs.debug.push(message) + console.error = (...message) => logs.error.push(message) + console.log = (...message) => logs.log.push(message) - const debugLogs = [] - console.debug = (...message) => debugLogs.push(message) + systemLogger.debug('debug 1') + t.is(logs.debug.length, 0) - systemLogger.debug('hello!') - t.is(debugLogs.length, 0) + systemLogger.log('log 1') + t.is(logs.log.length, 1) - systemLogger.withLogLevel(LogLevel.Debug).debug('hello!') - t.is(debugLogs.length, 1) + systemLogger.withLogLevel(LogLevel.Debug).debug('debug 2') + t.is(logs.debug.length, 1) - systemLogger.withLogLevel(LogLevel.Log).debug('hello!') - t.is(debugLogs.length, 1) + systemLogger.withLogLevel(LogLevel.Debug).error('error 1') + t.is(logs.error.length, 1) - console.debug = originalDebug + systemLogger.withLogLevel(LogLevel.None).error('error 2') + t.is(logs.error.length, 1) }) test('Fields', (t) => { - const originalLog = console.log const logs = [] console.log = (...message) => logs.push(message) systemLogger.withError(new Error('boom')).withFields({ foo: 'bar' }).log('hello!') @@ -34,26 +49,4 @@ test('Fields', (t) => { t.is(log.fields.foo, 'bar') t.is(log.fields.error, 'boom') t.is(log.fields.error_stack.split('\n').length > 2, true) - - console.log = originalLog -}) - -test('Local Dev', (t) => { - const originalLog = console.log - const logs = [] - console.log = (...message) => logs.push(message) - systemLogger.log('hello!') - t.is(logs.length, 1) - - process.env.NETLIFY_DEV= "true" - systemLogger.log('hello!') - t.is(logs.length, 1) - - process.env.NETLIFY_ENABLE_SYSTEM_LOGGING= "true" - systemLogger.log('hello!') - t.is(logs.length, 2) - - delete process.env.NETLIFY_DEV - delete process.env.NETLIFY_ENABLE_SYSTEM_LOGGING - console.log = originalLog }) From 7298b841472c383cb2880ca6c5ba6bffb5aa35ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Bou=C3=A7as?= Date: Fri, 19 Jan 2024 10:51:43 +0000 Subject: [PATCH 2/2] feat: support custom raw logger --- src/internal.ts | 2 +- src/lib/system_logger.ts | 39 +++++++++++++++--------------- test/unit/system_logger.js | 49 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 68 insertions(+), 22 deletions(-) diff --git a/src/internal.ts b/src/internal.ts index 14ce81c0..c9b47263 100644 --- a/src/internal.ts +++ b/src/internal.ts @@ -2,4 +2,4 @@ // and only meant for consumption by Netlify Teams. // While we try to adhere to semver, this file is not considered part of the public API. -export { systemLogger, LogLevel } from './lib/system_logger.js' +export { systemLogger, LogLevel, StructuredLogger } from './lib/system_logger.js' diff --git a/src/lib/system_logger.ts b/src/lib/system_logger.ts index c537899f..d937afd6 100644 --- a/src/lib/system_logger.ts +++ b/src/lib/system_logger.ts @@ -1,5 +1,3 @@ -import { env } from 'process' - const systemLogTag = '__nfSystemLog' const serializeError = (error: Error): Record => { @@ -18,20 +16,26 @@ export enum LogLevel { Debug = 1, Log, Error, - Silent = Infinity, + Silent = Number.POSITIVE_INFINITY, } -class SystemLogger { +type RawLogger = (...data: unknown[]) => void + +export class StructuredLogger { private readonly fields: Record private readonly logLevel: LogLevel + private readonly rawLogger?: RawLogger - constructor(fields: Record = {}, logLevel = LogLevel.Log) { + constructor(logLevel: LogLevel, rawLogger?: RawLogger, fields: Record = {}) { this.fields = fields this.logLevel = logLevel + this.rawLogger = rawLogger } - private doLog(logger: typeof console.log, message: string) { - logger(systemLogTag, JSON.stringify({ msg: message, fields: this.fields })) + private doLog(message: string, level: string, defaultLogger: RawLogger) { + const logger = this.rawLogger ?? defaultLogger + + logger(systemLogTag, JSON.stringify({ msg: message, fields: this.fields, level })) } log(message: string) { @@ -39,7 +43,7 @@ class SystemLogger { return } - this.doLog(console.log, message) + this.doLog(message, 'log', console.log) } debug(message: string) { @@ -47,7 +51,7 @@ class SystemLogger { return } - this.doLog(console.debug, message) + this.doLog(message, 'debug', console.debug) } error(message: string) { @@ -55,21 +59,18 @@ class SystemLogger { return } - this.doLog(console.error, message) + this.doLog(message, 'error', console.error) } withLogLevel(level: LogLevel) { - return new SystemLogger(this.fields, level) + return new StructuredLogger(level, this.rawLogger, this.fields) } withFields(fields: Record) { - return new SystemLogger( - { - ...this.fields, - ...fields, - }, - this.logLevel, - ) + return new StructuredLogger(this.logLevel, this.rawLogger, { + ...this.fields, + ...fields, + }) } withError(error: unknown) { @@ -79,4 +80,4 @@ class SystemLogger { } } -export const systemLogger = new SystemLogger() +export const systemLogger = new StructuredLogger(LogLevel.Log) diff --git a/test/unit/system_logger.js b/test/unit/system_logger.js index 3e2b52d1..f0f62fb1 100644 --- a/test/unit/system_logger.js +++ b/test/unit/system_logger.js @@ -1,6 +1,6 @@ const test = require('ava') -const { systemLogger, LogLevel } = require('../../dist/internal') +const { systemLogger, LogLevel, StructuredLogger } = require('../../dist/internal') const consoleDebug = console.debug const consoleError = console.error @@ -34,7 +34,7 @@ test('Log levels', (t) => { systemLogger.withLogLevel(LogLevel.Debug).error('error 1') t.is(logs.error.length, 1) - systemLogger.withLogLevel(LogLevel.None).error('error 2') + systemLogger.withLogLevel(LogLevel.Silent).error('error 2') t.is(logs.error.length, 1) }) @@ -49,4 +49,49 @@ test('Fields', (t) => { t.is(log.fields.foo, 'bar') t.is(log.fields.error, 'boom') t.is(log.fields.error_stack.split('\n').length > 2, true) + t.is(log.level, 'log') +}) + +test('Accepts a custom raw logger', (t) => { + const logs = { + debug: [], + error: [], + log: [], + } + console.debug = () => { + throw new Error('Unexpected `console.debug` call') + } + console.error = () => { + throw new Error('Unexpected `console.error` call') + } + console.log = () => { + throw new Error('Unexpected `console.log` call') + } + const rawLogger = (tag, payload) => { + t.is(tag, '__nfSystemLog') + + const { msg, fields, level } = JSON.parse(payload) + const bucket = logs[level] + + t.truthy(bucket) + + bucket.push({ fields, msg }) + } + + const logger = new StructuredLogger(LogLevel.Log, rawLogger, {}) + + logger.debug('debug 1') + t.is(logs.debug.length, 0) + + logger.log('log 1') + t.is(logs.log.length, 1) + + logger.withLogLevel(LogLevel.Debug).debug('debug 2') + t.is(logs.debug.length, 1) + + logger.withLogLevel(LogLevel.Debug).error('error 1') + t.is(logs.error.length, 1) + + logger.withLogLevel(LogLevel.Silent).error('error 2') + t.is(logs.error.length, 1) })