// Copyright Abridged, Inc. 2022,2024. All Rights Reserved. // Node module: @collabland/common // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT import debug, {Debugger, log} from 'debug'; import {InspectOptions} from 'util'; import {getPinoDebugger, getPinoLogger} from './debug-pino.js'; import { LogLevel, isDebugEnabled, isLogEnabled, redactData, stringify, } from './debug.js'; import {getEnvVarAsNumber} from './env.js'; /** * Debug namespaces */ export const debugNamespaces: Set = new Set(); const pinoLogger = getPinoLogger(); /** * Create a debug instance for the given namespace * @param namespace - Namespace */ export function debugFactory(namespace: string): Debugger { debugNamespaces.add(namespace); if (pinoLogger != null) { const debugFn = getPinoDebugger(pinoLogger, debugFactory, namespace); return debugFn; } const fn = debug(namespace); // Add log reaction const defaultLog = fn.log ?? log; fn.log = (...args) => { if (!isLogEnabled(namespace)) return; const redacted = redactData(args); return defaultLog(...redacted); }; // Set depth for object dump const depth = getEnvVarAsNumber('DEBUG_DEPTH', 8); const options = fn as unknown as {inspectOpts: InspectOptions}; options.inspectOpts = options.inspectOpts ?? {}; options.inspectOpts.depth = depth; return fn; } /** * Loggers for the given namespace * @param namespace - Namespace for logging * @returns */ export function loggers(namespace: string): Record { return { [LogLevel.ERROR]: debugFactory(`${namespace}:${LogLevel.ERROR}`), [LogLevel.WARN]: debugFactory(`${namespace}:${LogLevel.WARN}`), [LogLevel.INFO]: debugFactory(`${namespace}:${LogLevel.INFO}`), [LogLevel.DEBUG]: debugFactory(`${namespace}:${LogLevel.DEBUG}`), [LogLevel.TRACE]: debugFactory(`${namespace}:${LogLevel.TRACE}`), }; } /** * Alias for loggers */ export const debuggers = loggers; /** * Get an object of debug settings */ export function getDebugSettings() { const settings: Record = {}; for (const ns of debugNamespaces) { settings[ns] = isDebugEnabled(ns); } return settings; } const {info} = loggers('collabland:diagnostic'); export class DiagnosticTrace extends Array { /** * Create a new span that's embedded in the current trace * @param key - key for the new span * @returns */ span(key: string) { const spanned = new DiagnosticTrace(); this.push({[key]: spanned}); return spanned; } /** * Remove all items in this diagnostic trace * @returns */ reset() { return this.splice(0, this.length); } log(format = '%s', fn: (format: string, ...args: unknown[]) => void = info) { if (fn === info && info.enabled) { info(format, this); } else { fn(format, this); } } toString() { return stringify(this); } }