cui-server
Version:
Web UI Agent Platform based on Claude Code
77 lines • 3.11 kB
JavaScript
import { Transform } from 'stream';
const RESET = '\x1b[0m';
const GRAY = '\x1b[90m';
const BOLD = '\x1b[1m';
const BLUE = '\x1b[34m';
export class LogFormatter extends Transform {
constructor() {
super({
writableObjectMode: true,
transform(chunk, _encoding, callback) {
try {
const logLine = String(chunk).trim();
if (!logLine) {
callback();
return;
}
const log = JSON.parse(logLine);
const formatted = formatLog(log);
callback(null, formatted + '\n');
}
catch (_err) {
// If we can't parse it, pass it through as-is
callback(null, chunk);
}
}
});
}
}
function formatLog(log) {
// Format timestamp in 12-hour format with AM/PM
const time = new Date(typeof log.time === 'string' ? log.time : log.time);
const hours = time.getHours();
const minutes = time.getMinutes().toString().padStart(2, '0');
const seconds = time.getSeconds().toString().padStart(2, '0');
const ampm = hours >= 12 ? 'PM' : 'AM';
const displayHours = (hours % 12 || 12).toString().padStart(2, '0');
const timestamp = `${displayHours}:${minutes}:${seconds} ${ampm}`;
// Build the formatted message
let formatted = `${GRAY}${timestamp}${RESET}`;
// Add component in bold blue brackets if present
if (log.component) {
formatted += ` ${BOLD}${BLUE}[${log.component}]${RESET}`;
}
// Add the main message
formatted += ` ${log.msg}`;
// Add context fields (filter out only pino internals)
const excludedFields = ['level', 'time', 'msg', 'component', 'pid', 'hostname', 'v'];
const contextFields = Object.keys(log)
.filter(key => !excludedFields.includes(key) && log[key] !== undefined && log[key] !== null);
if (contextFields.length > 0) {
const contextPairs = contextFields.map(key => {
const value = log[key];
// Special handling for error objects
if ((key === 'err' || key === 'error') && typeof value === 'object' && value !== null && 'message' in value) {
return `${key}="${value.message}"`;
}
// Format based on value type
if (typeof value === 'string') {
return `${key}="${value}"`;
}
else if (typeof value === 'number' || typeof value === 'boolean') {
return `${key}=${value}`;
}
else {
// For objects and arrays, use JSON.stringify
return `${key}=${JSON.stringify(value)}`;
}
});
formatted += ` ${GRAY}${contextPairs.join(' ')}${RESET}`;
}
// Handle error stack traces
if (log.err && typeof log.err === 'object' && 'stack' in log.err && log.err.stack) {
formatted += `\n${log.err.stack}`;
}
return formatted;
}
//# sourceMappingURL=log-formatter.js.map