Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Jun 9, 2025. It is now read-only.

feat: support custom raw logger #461

Open
eduardoboucas wants to merge 2 commits into main
base: main
Choose a base branch
Loading
from feat/api-updates
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/internal.ts
View file Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -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'
40 changes: 19 additions & 21 deletions src/lib/system_logger.ts
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { env } from 'process'

const systemLogTag = '__nfSystemLog'

const serializeError = (error: Error): Record<string, unknown> => {
Expand All @@ -18,61 +16,61 @@ export enum LogLevel {
Debug = 1,
Log,
Error,
Silent = Number.POSITIVE_INFINITY,
}

class SystemLogger {
type RawLogger = (...data: unknown[]) => void

export class StructuredLogger {
private readonly fields: Record<string, unknown>
private readonly logLevel: LogLevel
private readonly rawLogger?: RawLogger

constructor(fields: Record<string, unknown> = {}, logLevel = LogLevel.Log) {
constructor(logLevel: LogLevel, rawLogger?: RawLogger, fields: Record<string, unknown> = {}) {
this.fields = fields
this.logLevel = logLevel
this.rawLogger = rawLogger
}

private doLog(logger: typeof console.log, message: string) {
if (env.NETLIFY_DEV && !env.NETLIFY_ENABLE_SYSTEM_LOGGING) {
return
}
private doLog(message: string, level: string, defaultLogger: RawLogger) {
const logger = this.rawLogger ?? defaultLogger

logger(systemLogTag, JSON.stringify({ msg: message, fields: this.fields }))
logger(systemLogTag, JSON.stringify({ msg: message, fields: this.fields, level }))
}

log(message: string) {
if (this.logLevel > LogLevel.Log) {
return
}

this.doLog(console.log, message)
this.doLog(message, 'log', console.log)
}

debug(message: string) {
if (this.logLevel > LogLevel.Debug) {
return
}

this.doLog(console.debug, message)
this.doLog(message, 'debug', console.debug)
}

error(message: string) {
if (this.logLevel > LogLevel.Error) {
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<string, unknown>) {
return new SystemLogger(
{
...this.fields,
...fields,
},
this.logLevel,
)
return new StructuredLogger(this.logLevel, this.rawLogger, {
...this.fields,
...fields,
})
}

withError(error: unknown) {
Expand All @@ -82,4 +80,4 @@ class SystemLogger {
}
}

export const systemLogger = new SystemLogger()
export const systemLogger = new StructuredLogger(LogLevel.Log)
102 changes: 70 additions & 32 deletions test/unit/system_logger.js
View file Open in desktop
Original file line number Diff line number Diff line change
@@ -1,29 +1,44 @@
const process = require("process")

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
const consoleLog = console.log

test.afterEach(() => {
console.debug = consoleDebug
console.error = consoleError
console.log = consoleLog
})

test('Log Level', (t) => {
const originalDebug = console.debug
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.Silent).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!')
Expand All @@ -34,26 +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)

console.log = originalLog
t.is(log.level, 'log')
})

test('Local Dev', (t) => {
const originalLog = console.log
const logs = []
console.log = (...message) => logs.push(message)
systemLogger.log('hello!')
t.is(logs.length, 1)
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')

process.env.NETLIFY_DEV= "true"
systemLogger.log('hello!')
t.is(logs.length, 1)
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)

process.env.NETLIFY_ENABLE_SYSTEM_LOGGING= "true"
systemLogger.log('hello!')
t.is(logs.length, 2)
logger.withLogLevel(LogLevel.Debug).error('error 1')
t.is(logs.error.length, 1)

delete process.env.NETLIFY_DEV
delete process.env.NETLIFY_ENABLE_SYSTEM_LOGGING
console.log = originalLog
logger.withLogLevel(LogLevel.Silent).error('error 2')
t.is(logs.error.length, 1)
})

AltStyle によって変換されたページ (->オリジナル) /