The previous GitHub history was lost with the account (the local .git was missing its refs and most objects), so this repository starts from the existing code. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
78 lines
2.3 KiB
JavaScript
78 lines
2.3 KiB
JavaScript
// Lightweight structured logger. No dependencies — writes one record per line
|
|
// to a stream (stdout by default). Level is controlled by LOG_LEVEL
|
|
// (error < warn < info < debug); anything above the threshold is dropped.
|
|
// Format is controlled by LOG_FORMAT (json | text, default: text).
|
|
|
|
const LEVELS = { error: 0, warn: 1, info: 2, debug: 3 };
|
|
|
|
function serializeMeta(meta) {
|
|
if (meta == null) return undefined;
|
|
if (meta instanceof Error) {
|
|
return { error: meta.message, stack: meta.stack, cause: meta.cause };
|
|
}
|
|
if (typeof meta !== 'object') {
|
|
return { value: meta };
|
|
}
|
|
const out = {};
|
|
for (const [key, value] of Object.entries(meta)) {
|
|
if (value instanceof Error) {
|
|
out[key] = { error: value.message, stack: value.stack, cause: value.cause };
|
|
} else {
|
|
out[key] = value;
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function formatTextRecord(record) {
|
|
const lvlStr = record.level.toUpperCase().padEnd(5);
|
|
const metaStr = record.meta ? ` ${JSON.stringify(record.meta)}` : '';
|
|
return `[${record.ts}] [${lvlStr}] [${record.component}] ${record.msg}${metaStr}\n`;
|
|
}
|
|
|
|
export function createLogger(component, {
|
|
level = process.env.LOG_LEVEL || 'info',
|
|
format = process.env.LOG_FORMAT || 'text',
|
|
stream = process.stdout,
|
|
} = {}) {
|
|
const threshold = LEVELS[level] ?? LEVELS.info;
|
|
const isJson = format === 'json';
|
|
|
|
function log(lvl, msg, meta) {
|
|
if (LEVELS[lvl] > threshold) return;
|
|
const record = {
|
|
ts: new Date().toISOString(),
|
|
level: lvl,
|
|
component,
|
|
msg,
|
|
};
|
|
const m = serializeMeta(meta);
|
|
if (m !== undefined) record.meta = m;
|
|
|
|
if (isJson) {
|
|
stream.write(JSON.stringify(record) + '\n');
|
|
} else {
|
|
stream.write(formatTextRecord(record));
|
|
}
|
|
}
|
|
|
|
return {
|
|
error: (msg, meta) => log('error', msg, meta),
|
|
warn: (msg, meta) => log('warn', msg, meta),
|
|
info: (msg, meta) => log('info', msg, meta),
|
|
debug: (msg, meta) => log('debug', msg, meta),
|
|
time: (label, lvl = 'info') => {
|
|
const start = Date.now();
|
|
return (meta = {}) => {
|
|
const durationMs = Date.now() - start;
|
|
log(lvl, `${label} took ${durationMs}ms`, { ...meta, durationMs });
|
|
};
|
|
},
|
|
child: (sub) => createLogger(`${component}:${sub}`, { level, format, stream }),
|
|
};
|
|
}
|
|
|
|
// Default application logger.
|
|
export const logger = createLogger('craftdock');
|
|
|