@overture-stack/lyric
Version:
Data Submission system
66 lines (65 loc) • 2.87 kB
JavaScript
import { createLogger, format, transports } from 'winston';
/**
* Splits variadic logger arguments into a text message and an optional metadata object.
* If the last argument is a plain object it is treated as structured metadata; all preceding
* arguments are joined with ' - ' to form the message string.
*/
const splitArgs = (messages) => {
const last = messages[messages.length - 1];
const isMetaObject = last !== null && last !== undefined && typeof last === 'object' && !Array.isArray(last) && !(last instanceof Error);
if (isMetaObject && messages.length > 1) {
return {
message: messages.slice(0, -1).join(' - '),
meta: last,
};
}
return { message: messages.join(' - ') };
};
/** Renders a metadata object as space-separated key=value pairs for human-readable output. */
const metaToString = (meta) => Object.entries(meta)
.map(([k, v]) => `${k}=${typeof v === 'string' ? v : JSON.stringify(v)}`)
.join(' ');
/**
* Get a Logger instance for log messages.
*
* Supports two transports:
* - Console: human-readable `timestamp [level]: message key=value ...` format, always active.
* - JSON file (`logs/app.json`): full structured JSON for log aggregators; enabled via `config.json`.
*/
export const getLogger = (config) => {
const transportList = [];
const { combine, timestamp, colorize, printf, json, errors } = format;
// Console transport: human-readable with metadata rendered as key=value pairs.
transportList.push(new transports.Console({
format: combine(timestamp(), colorize(), errors({ stack: true }), printf(({ timestamp, level, message, ...meta }) => {
const metaKeys = Object.keys(meta).filter((k) => k !== 'stack');
const metaStr = metaKeys.length ? ' ' + metaToString(meta) : '';
return `${timestamp} [${level}]: ${message}${metaStr}`;
})),
}));
// Plain-text file transport for pre-Kubernetes deployments that relied on file-based log shipping.
if (config.file) {
transportList.push(new transports.File({ filename: 'logs.log' }));
}
// JSON file transport for log aggregators (Kibana via Filebeat, etc.).
if (config.json) {
transportList.push(new transports.File({
filename: 'logs/app.json',
format: combine(timestamp(), errors({ stack: true }), json()),
}));
}
const logger = createLogger({
level: config.level || 'info',
transports: transportList,
});
const emit = (fn) => (...messages) => {
const { message, meta } = splitArgs(messages);
fn(message, meta);
};
return {
debug: emit(logger.debug.bind(logger)),
warn: emit(logger.warn.bind(logger)),
info: emit(logger.info.bind(logger)),
error: emit(logger.error.bind(logger)),
};
};