Initial import of verae-middleware from zapier monorepo
This commit is contained in:
commit
87cd4896c2
67 changed files with 6630 additions and 0 deletions
121
src/debug/logger.js
Normal file
121
src/debug/logger.js
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/**
|
||||
* @fileoverview Namespaced debug logger with runtime enable/disable.
|
||||
* @module debug/logger
|
||||
*/
|
||||
|
||||
import { appendFileSync } from 'node:fs';
|
||||
import { loadDebugConfig, shouldLog } from './config.js';
|
||||
import { redact } from './redact.js';
|
||||
import { getTraceId } from './trace-context.js';
|
||||
|
||||
/**
|
||||
* @typedef {Object} Debugger
|
||||
* @property {(msg: string, meta?: Record<string, unknown>) => void} debug
|
||||
* @property {(msg: string, meta?: Record<string, unknown>) => void} info
|
||||
* @property {(msg: string, meta?: Record<string, unknown>) => void} warn
|
||||
* @property {(msg: string, meta?: Record<string, unknown>) => void} error
|
||||
* @property {string} namespace
|
||||
*/
|
||||
|
||||
/**
|
||||
* Optional sink for tests — when set, lines go here instead of/in addition to stderr.
|
||||
* @type {null|((line: string) => void)}
|
||||
*/
|
||||
let testSink = null;
|
||||
|
||||
/**
|
||||
* Install a test sink that captures formatted log lines.
|
||||
* Used only by unit tests; do not use in production code.
|
||||
*
|
||||
* @param {null|((line: string) => void)} sink - Callback or null to clear.
|
||||
* @returns {void}
|
||||
*/
|
||||
export function setDebugTestSink(sink) {
|
||||
testSink = sink;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a single log line.
|
||||
*
|
||||
* @param {object} parts
|
||||
* @param {string} parts.level
|
||||
* @param {string} parts.namespace
|
||||
* @param {string} parts.message
|
||||
* @param {Record<string, unknown>|undefined} parts.meta
|
||||
* @param {string|null} parts.traceId
|
||||
* @returns {string}
|
||||
*/
|
||||
export function formatLogLine({ level, namespace, message, meta, traceId }) {
|
||||
const ts = new Date().toISOString();
|
||||
const tracePart = traceId ? ` [trace=${traceId}]` : '';
|
||||
const metaPart =
|
||||
meta && Object.keys(meta).length > 0 ? ` ${JSON.stringify(redact(meta))}` : '';
|
||||
return `${ts} ${level.toUpperCase()} ${namespace}${tracePart} ${message}${metaPart}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a log line to stderr, optional file, and optional test sink.
|
||||
*
|
||||
* @param {string} line - Full formatted line.
|
||||
* @param {string|null} filePath - Optional file from config.
|
||||
* @returns {void}
|
||||
*/
|
||||
function emit(line, filePath) {
|
||||
if (testSink) {
|
||||
testSink(line);
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(line);
|
||||
}
|
||||
if (filePath) {
|
||||
try {
|
||||
appendFileSync(filePath, `${line}\n`, 'utf8');
|
||||
} catch {
|
||||
// ignore file errors in debug path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a namespaced debugger bound to current env configuration.
|
||||
*
|
||||
* Configuration is re-read on every log call so tests can toggle `DEBUG_VERAE`
|
||||
* without reloading modules. Slight cost is acceptable for debug-only paths.
|
||||
*
|
||||
* @param {string} namespace - Namespace such as `auth`, `nats`, `jobs`.
|
||||
* @returns {Debugger} Logger with debug/info/warn/error methods.
|
||||
*
|
||||
* @example
|
||||
* const log = createDebugger('jobs');
|
||||
* log.debug('poll start', { jobId: 'abc' });
|
||||
*/
|
||||
export function createDebugger(namespace) {
|
||||
const ns = String(namespace || 'app').toLowerCase();
|
||||
|
||||
/**
|
||||
* @param {'debug'|'info'|'warn'|'error'} level
|
||||
* @param {string} message
|
||||
* @param {Record<string, unknown>} [meta]
|
||||
*/
|
||||
function write(level, message, meta) {
|
||||
const config = loadDebugConfig();
|
||||
if (!shouldLog(config, ns, level)) return;
|
||||
|
||||
const line = formatLogLine({
|
||||
level,
|
||||
namespace: ns,
|
||||
message: String(message),
|
||||
meta,
|
||||
traceId: getTraceId(),
|
||||
});
|
||||
emit(line, config.filePath);
|
||||
}
|
||||
|
||||
return {
|
||||
namespace: ns,
|
||||
debug: (message, meta) => write('debug', message, meta),
|
||||
info: (message, meta) => write('info', message, meta),
|
||||
warn: (message, meta) => write('warn', message, meta),
|
||||
error: (message, meta) => write('error', message, meta),
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue