/**
* Logger abstraction for MCP server
*
* MCP servers communicate over stdio, so we need to be careful about logging.
* This logger writes to stderr to avoid polluting the protocol channel.
*
* Controlled via environment variables:
* - SECOND_BRAIN_DEBUG=true - Enable logging (default: disabled)
* - SECOND_BRAIN_LOG_LEVEL - Minimum log level (debug|info|warn|error, default: info)
*/
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
interface LoggerConfig {
enabled: boolean;
minLevel: LogLevel;
}
const LOG_LEVELS: Record<LogLevel, number> = {
debug: 0,
info: 1,
warn: 2,
error: 3,
};
class Logger {
private config: LoggerConfig;
constructor() {
this.config = {
enabled: process.env.SECOND_BRAIN_DEBUG === 'true',
minLevel: this.parseLogLevel(process.env.SECOND_BRAIN_LOG_LEVEL),
};
}
/**
* Parse log level from environment variable
*/
private parseLogLevel(level: string | undefined): LogLevel {
if (level && level in LOG_LEVELS) {
return level as LogLevel;
}
return 'info';
}
/**
* Check if a message at the given level should be logged
*/
private shouldLog(level: LogLevel): boolean {
if (!this.config.enabled) {
return false;
}
return LOG_LEVELS[level] >= LOG_LEVELS[this.config.minLevel];
}
/**
* Format a log message with timestamp and level
*/
private formatMessage(
level: LogLevel,
message: string,
meta?: Record<string, unknown>
): string {
const timestamp = new Date().toISOString();
const metaStr = meta ? ` ${JSON.stringify(meta)}` : '';
return `[${timestamp}] [${level.toUpperCase()}] ${message}${metaStr}`;
}
/**
* Log a debug message
*/
debug(message: string, meta?: Record<string, unknown>): void {
if (this.shouldLog('debug')) {
console.error(this.formatMessage('debug', message, meta));
}
}
/**
* Log an info message
*/
info(message: string, meta?: Record<string, unknown>): void {
if (this.shouldLog('info')) {
console.error(this.formatMessage('info', message, meta));
}
}
/**
* Log a warning message
*/
warn(message: string, meta?: Record<string, unknown>): void {
if (this.shouldLog('warn')) {
console.error(this.formatMessage('warn', message, meta));
}
}
/**
* Log an error message
*/
error(message: string, meta?: Record<string, unknown>): void {
if (this.shouldLog('error')) {
console.error(this.formatMessage('error', message, meta));
}
}
/**
* Check if logging is enabled
*/
isEnabled(): boolean {
return this.config.enabled;
}
/**
* Get current configuration (for testing)
*/
getConfig(): LoggerConfig {
return { ...this.config };
}
}
// Export singleton instance
export const logger = new Logger();
// Export class for testing
export { Logger };