mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-10 12:05:53 +00:00
feat: Add unified logger utility with file and console output
- Create logger factory with explicit module names (no stack traces) - Support log/warn/error/debug levels with clean method names - Write to ~/.vibetunnel/log.txt with automatic cleanup on startup - Add colored console output with timestamps and module names - Support debug mode via flag or environment variable 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
1c007a2181
commit
3a3b5e44e9
1 changed files with 180 additions and 0 deletions
180
web/src/server/utils/logger.ts
Normal file
180
web/src/server/utils/logger.ts
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import chalk from 'chalk';
|
||||
|
||||
// Log file path
|
||||
const LOG_DIR = path.join(os.homedir(), '.vibetunnel');
|
||||
const LOG_FILE = path.join(LOG_DIR, 'log.txt');
|
||||
|
||||
// Debug mode flag
|
||||
let debugMode = false;
|
||||
|
||||
// File handle for log file
|
||||
let logFileHandle: fs.WriteStream | null = null;
|
||||
|
||||
// ANSI color codes for stripping from file output
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const ANSI_PATTERN = /\x1b\[[0-9;]*m/g;
|
||||
|
||||
/**
|
||||
* Initialize the logger - creates log directory and file
|
||||
*/
|
||||
export function initLogger(debug: boolean = false): void {
|
||||
debugMode = debug;
|
||||
|
||||
try {
|
||||
// Ensure log directory exists
|
||||
if (!fs.existsSync(LOG_DIR)) {
|
||||
fs.mkdirSync(LOG_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
// Delete old log file if it exists
|
||||
if (fs.existsSync(LOG_FILE)) {
|
||||
fs.unlinkSync(LOG_FILE);
|
||||
}
|
||||
|
||||
// Create new log file write stream
|
||||
logFileHandle = fs.createWriteStream(LOG_FILE, { flags: 'a' });
|
||||
} catch (error) {
|
||||
// Don't throw, just log to console
|
||||
console.error('Failed to initialize log file:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the logger
|
||||
*/
|
||||
export function closeLogger(): void {
|
||||
if (logFileHandle) {
|
||||
logFileHandle.end();
|
||||
logFileHandle = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format log message with timestamp
|
||||
*/
|
||||
function formatMessage(
|
||||
level: string,
|
||||
module: string,
|
||||
args: any[]
|
||||
): { console: string; file: string } {
|
||||
const timestamp = new Date().toISOString();
|
||||
|
||||
// Format arguments
|
||||
const message = args
|
||||
.map((arg) => {
|
||||
if (typeof arg === 'object') {
|
||||
try {
|
||||
// Use JSON.stringify with 2-space indent for objects
|
||||
return JSON.stringify(arg, null, 2);
|
||||
} catch {
|
||||
return String(arg);
|
||||
}
|
||||
}
|
||||
return String(arg);
|
||||
})
|
||||
.join(' ');
|
||||
|
||||
// Console format with colors
|
||||
let consoleFormat: string;
|
||||
const moduleColor = chalk.cyan(`[${module}]`);
|
||||
const timestampColor = chalk.gray(timestamp);
|
||||
|
||||
switch (level) {
|
||||
case 'ERROR':
|
||||
consoleFormat = `${timestampColor} ${chalk.red(level)} ${moduleColor} ${chalk.red(message)}`;
|
||||
break;
|
||||
case 'WARN':
|
||||
consoleFormat = `${timestampColor} ${chalk.yellow(level)} ${moduleColor} ${chalk.yellow(message)}`;
|
||||
break;
|
||||
case 'DEBUG':
|
||||
consoleFormat = `${timestampColor} ${chalk.magenta(level)} ${moduleColor} ${chalk.gray(message)}`;
|
||||
break;
|
||||
default: // LOG
|
||||
consoleFormat = `${timestampColor} ${chalk.green(level)} ${moduleColor} ${message}`;
|
||||
}
|
||||
|
||||
// File format (no colors)
|
||||
const fileFormat = `${timestamp} ${level.padEnd(5)} [${module}] ${message}`;
|
||||
|
||||
return { console: consoleFormat, file: fileFormat };
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to log file
|
||||
*/
|
||||
function writeToFile(message: string): void {
|
||||
if (logFileHandle) {
|
||||
try {
|
||||
// Strip ANSI color codes from message
|
||||
const cleanMessage = message.replace(ANSI_PATTERN, '');
|
||||
logFileHandle.write(cleanMessage + '\n');
|
||||
} catch {
|
||||
// Silently ignore file write errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable debug mode
|
||||
*/
|
||||
export function setDebugMode(enabled: boolean): void {
|
||||
debugMode = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log from a specific module (used by client-side API)
|
||||
*/
|
||||
export function logFromModule(level: string, module: string, args: any[]): void {
|
||||
if (level === 'DEBUG' && !debugMode) return;
|
||||
|
||||
const { console: consoleMsg, file: fileMsg } = formatMessage(level, module, args);
|
||||
|
||||
// Log to console
|
||||
switch (level) {
|
||||
case 'ERROR':
|
||||
console.error(consoleMsg);
|
||||
break;
|
||||
case 'WARN':
|
||||
console.warn(consoleMsg);
|
||||
break;
|
||||
default:
|
||||
console.log(consoleMsg);
|
||||
}
|
||||
|
||||
// Log to file
|
||||
writeToFile(fileMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a logger for a specific module
|
||||
* This is the main factory function that should be used
|
||||
*/
|
||||
export function createLogger(moduleName: string) {
|
||||
return {
|
||||
log: (...args: any[]) => {
|
||||
const { console: consoleMsg, file: fileMsg } = formatMessage('LOG', moduleName, args);
|
||||
console.log(consoleMsg);
|
||||
writeToFile(fileMsg);
|
||||
},
|
||||
warn: (...args: any[]) => {
|
||||
const { console: consoleMsg, file: fileMsg } = formatMessage('WARN', moduleName, args);
|
||||
console.warn(consoleMsg);
|
||||
writeToFile(fileMsg);
|
||||
},
|
||||
error: (...args: any[]) => {
|
||||
const { console: consoleMsg, file: fileMsg } = formatMessage('ERROR', moduleName, args);
|
||||
console.error(consoleMsg);
|
||||
writeToFile(fileMsg);
|
||||
},
|
||||
debug: (...args: any[]) => {
|
||||
if (debugMode) {
|
||||
const { console: consoleMsg, file: fileMsg } = formatMessage('DEBUG', moduleName, args);
|
||||
console.log(consoleMsg);
|
||||
writeToFile(fileMsg);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
Loading…
Reference in a new issue