Split monolithic chat service into modular structure with Matrix client support, extract web server into per-resource route modules, introduce bootstrap/main-loop separation, and add comprehensive vitest coverage across services, repositories, and utilities. Adds project docs and CLAUDE.md agent instructions.
49 lines
1.4 KiB
TypeScript
49 lines
1.4 KiB
TypeScript
import winston from 'winston';
|
|
|
|
export type Logger = winston.Logger;
|
|
|
|
function safeStringify(value: unknown): string {
|
|
const seen = new WeakSet<object>();
|
|
try {
|
|
return JSON.stringify(value, (_key, val) => {
|
|
if (typeof val === 'object' && val !== null) {
|
|
if (seen.has(val)) return '[Circular]';
|
|
seen.add(val);
|
|
}
|
|
if (typeof val === 'bigint') return val.toString();
|
|
return val;
|
|
});
|
|
} catch (err) {
|
|
return `[Unserializable: ${(err as Error).message}]`;
|
|
}
|
|
}
|
|
|
|
export function createLogger(): Logger {
|
|
return winston.createLogger({
|
|
level: process.env.LOG_LEVEL || 'info',
|
|
format: winston.format.combine(
|
|
winston.format.timestamp(),
|
|
winston.format.errors({ stack: true }),
|
|
winston.format.json()
|
|
),
|
|
transports: [
|
|
new winston.transports.Console({
|
|
format: winston.format.combine(
|
|
winston.format.colorize(),
|
|
winston.format.printf(({ level, message, timestamp, ...meta }) => {
|
|
const metaStr = Object.keys(meta).length ? ` ${safeStringify(meta)}` : '';
|
|
return `${timestamp} [${level}]: ${message}${metaStr}`;
|
|
})
|
|
),
|
|
}),
|
|
new winston.transports.File({
|
|
filename: 'logs/error.log',
|
|
level: 'error',
|
|
}),
|
|
new winston.transports.File({
|
|
filename: 'logs/combined.log',
|
|
}),
|
|
],
|
|
});
|
|
}
|