mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 20:03:37 +00:00
Previously, colors were only enabled when stdout was a TTY, which caused colored output to be stripped when the server ran as a subprocess. Now colors are enabled by default in Node.js and can be disabled with LOG_COLORS=false if needed. Also removed the unused isTTY() function.
248 lines
6.7 KiB
TypeScript
248 lines
6.7 KiB
TypeScript
/**
|
|
* Enhanced logger with colors and timestamps
|
|
*
|
|
* Environment Variables:
|
|
* - LOG_LEVEL: error, warn, info, debug (default: info)
|
|
* - LOG_COLORS: true/false (default: true in Node.js)
|
|
* - LOG_TIMESTAMPS: true/false (default: false)
|
|
*/
|
|
|
|
export enum LogLevel {
|
|
ERROR = 0,
|
|
WARN = 1,
|
|
INFO = 2,
|
|
DEBUG = 3,
|
|
}
|
|
|
|
const LOG_LEVEL_NAMES: Record<string, LogLevel> = {
|
|
error: LogLevel.ERROR,
|
|
warn: LogLevel.WARN,
|
|
info: LogLevel.INFO,
|
|
debug: LogLevel.DEBUG,
|
|
};
|
|
|
|
// ANSI color codes for terminal output
|
|
const ANSI = {
|
|
reset: '\x1b[0m',
|
|
bold: '\x1b[1m',
|
|
dim: '\x1b[2m',
|
|
// Foreground colors
|
|
red: '\x1b[31m',
|
|
green: '\x1b[32m',
|
|
yellow: '\x1b[33m',
|
|
blue: '\x1b[34m',
|
|
magenta: '\x1b[35m',
|
|
cyan: '\x1b[36m',
|
|
white: '\x1b[37m',
|
|
gray: '\x1b[90m',
|
|
};
|
|
|
|
// Browser CSS styles for console output
|
|
const BROWSER_STYLES = {
|
|
timestamp: 'color: #6b7280; font-size: 11px;',
|
|
context: 'color: #3b82f6; font-weight: 600;',
|
|
reset: 'color: inherit; font-weight: inherit;',
|
|
levels: {
|
|
ERROR:
|
|
'background: #ef4444; color: white; font-weight: bold; padding: 1px 6px; border-radius: 3px;',
|
|
WARN: 'background: #f59e0b; color: white; font-weight: bold; padding: 1px 6px; border-radius: 3px;',
|
|
INFO: 'background: #3b82f6; color: white; font-weight: bold; padding: 1px 6px; border-radius: 3px;',
|
|
DEBUG:
|
|
'background: #8b5cf6; color: white; font-weight: bold; padding: 1px 6px; border-radius: 3px;',
|
|
},
|
|
};
|
|
|
|
// Environment detection - use globalThis for cross-platform compatibility
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const isBrowser = typeof (globalThis as any).window !== 'undefined';
|
|
|
|
// Configuration state
|
|
let currentLogLevel: LogLevel = LogLevel.INFO;
|
|
|
|
// Get environment variable safely (works in both Node.js and browser)
|
|
function getEnvVar(name: string): string | undefined {
|
|
if (isBrowser) return undefined;
|
|
try {
|
|
return process?.env?.[name];
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
// Initialize configuration from environment variables
|
|
// Colors enabled by default in Node.js, can be disabled with LOG_COLORS=false
|
|
let colorsEnabled = !isBrowser && getEnvVar('LOG_COLORS') !== 'false';
|
|
let timestampsEnabled = getEnvVar('LOG_TIMESTAMPS') === 'true';
|
|
|
|
// Initialize log level from environment variable
|
|
const envLogLevel = getEnvVar('LOG_LEVEL')?.toLowerCase();
|
|
if (envLogLevel && LOG_LEVEL_NAMES[envLogLevel] !== undefined) {
|
|
currentLogLevel = LOG_LEVEL_NAMES[envLogLevel];
|
|
}
|
|
|
|
/**
|
|
* Format ISO timestamp
|
|
*/
|
|
function formatTimestamp(): string {
|
|
return new Date().toISOString();
|
|
}
|
|
|
|
/**
|
|
* Format short time for browser (HH:mm:ss.SSS)
|
|
*/
|
|
function formatShortTime(): string {
|
|
return new Date().toISOString().split('T')[1].slice(0, 12);
|
|
}
|
|
|
|
/**
|
|
* Format a log line for Node.js terminal output
|
|
*/
|
|
function formatNodeLog(level: string, context: string, levelColor: string): string {
|
|
const parts: string[] = [];
|
|
|
|
if (timestampsEnabled) {
|
|
parts.push(colorsEnabled ? `${ANSI.gray}${formatTimestamp()}${ANSI.reset}` : formatTimestamp());
|
|
}
|
|
|
|
const levelPadded = level.padEnd(5);
|
|
parts.push(colorsEnabled ? `${levelColor}${levelPadded}${ANSI.reset}` : levelPadded);
|
|
parts.push(colorsEnabled ? `${ANSI.blue}[${context}]${ANSI.reset}` : `[${context}]`);
|
|
|
|
return parts.join(' ');
|
|
}
|
|
|
|
/**
|
|
* Logger interface returned by createLogger
|
|
*/
|
|
export interface Logger {
|
|
error: (...args: unknown[]) => void;
|
|
warn: (...args: unknown[]) => void;
|
|
info: (...args: unknown[]) => void;
|
|
debug: (...args: unknown[]) => void;
|
|
}
|
|
|
|
/**
|
|
* Create a logger instance with a context prefix
|
|
*/
|
|
export function createLogger(context: string): Logger {
|
|
if (isBrowser) {
|
|
// Browser implementation with CSS styling
|
|
return {
|
|
error: (...args: unknown[]): void => {
|
|
if (currentLogLevel >= LogLevel.ERROR) {
|
|
console.error(
|
|
`%cERROR%c %c${formatShortTime()}%c %c[${context}]%c`,
|
|
BROWSER_STYLES.levels.ERROR,
|
|
BROWSER_STYLES.reset,
|
|
BROWSER_STYLES.timestamp,
|
|
BROWSER_STYLES.reset,
|
|
BROWSER_STYLES.context,
|
|
BROWSER_STYLES.reset,
|
|
...args
|
|
);
|
|
}
|
|
},
|
|
|
|
warn: (...args: unknown[]): void => {
|
|
if (currentLogLevel >= LogLevel.WARN) {
|
|
console.warn(
|
|
`%cWARN%c %c${formatShortTime()}%c %c[${context}]%c`,
|
|
BROWSER_STYLES.levels.WARN,
|
|
BROWSER_STYLES.reset,
|
|
BROWSER_STYLES.timestamp,
|
|
BROWSER_STYLES.reset,
|
|
BROWSER_STYLES.context,
|
|
BROWSER_STYLES.reset,
|
|
...args
|
|
);
|
|
}
|
|
},
|
|
|
|
info: (...args: unknown[]): void => {
|
|
if (currentLogLevel >= LogLevel.INFO) {
|
|
console.log(
|
|
`%cINFO%c %c${formatShortTime()}%c %c[${context}]%c`,
|
|
BROWSER_STYLES.levels.INFO,
|
|
BROWSER_STYLES.reset,
|
|
BROWSER_STYLES.timestamp,
|
|
BROWSER_STYLES.reset,
|
|
BROWSER_STYLES.context,
|
|
BROWSER_STYLES.reset,
|
|
...args
|
|
);
|
|
}
|
|
},
|
|
|
|
debug: (...args: unknown[]): void => {
|
|
if (currentLogLevel >= LogLevel.DEBUG) {
|
|
console.log(
|
|
`%cDEBUG%c %c${formatShortTime()}%c %c[${context}]%c`,
|
|
BROWSER_STYLES.levels.DEBUG,
|
|
BROWSER_STYLES.reset,
|
|
BROWSER_STYLES.timestamp,
|
|
BROWSER_STYLES.reset,
|
|
BROWSER_STYLES.context,
|
|
BROWSER_STYLES.reset,
|
|
...args
|
|
);
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
// Node.js implementation with ANSI colors
|
|
return {
|
|
error: (...args: unknown[]): void => {
|
|
if (currentLogLevel >= LogLevel.ERROR) {
|
|
console.error(formatNodeLog('ERROR', context, ANSI.red), ...args);
|
|
}
|
|
},
|
|
|
|
warn: (...args: unknown[]): void => {
|
|
if (currentLogLevel >= LogLevel.WARN) {
|
|
console.log(formatNodeLog('WARN', context, ANSI.yellow), ...args);
|
|
}
|
|
},
|
|
|
|
info: (...args: unknown[]): void => {
|
|
if (currentLogLevel >= LogLevel.INFO) {
|
|
console.log(formatNodeLog('INFO', context, ANSI.cyan), ...args);
|
|
}
|
|
},
|
|
|
|
debug: (...args: unknown[]): void => {
|
|
if (currentLogLevel >= LogLevel.DEBUG) {
|
|
console.log(formatNodeLog('DEBUG', context, ANSI.magenta), ...args);
|
|
}
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get the current log level
|
|
*/
|
|
export function getLogLevel(): LogLevel {
|
|
return currentLogLevel;
|
|
}
|
|
|
|
/**
|
|
* Set the log level programmatically (useful for testing)
|
|
*/
|
|
export function setLogLevel(level: LogLevel): void {
|
|
currentLogLevel = level;
|
|
}
|
|
|
|
/**
|
|
* Enable or disable colored output
|
|
*/
|
|
export function setColorsEnabled(enabled: boolean): void {
|
|
colorsEnabled = enabled;
|
|
}
|
|
|
|
/**
|
|
* Enable or disable timestamps in output
|
|
*/
|
|
export function setTimestampsEnabled(enabled: boolean): void {
|
|
timestampsEnabled = enabled;
|
|
}
|