From ca14f5e4a32889201debfb08b4e973355ec66744 Mon Sep 17 00:00:00 2001 From: Luigi Pederzani Date: Wed, 9 Jul 2025 23:00:05 -0700 Subject: [PATCH] Fix logging on V8 --- src/logging.ts | 191 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 172 insertions(+), 19 deletions(-) diff --git a/src/logging.ts b/src/logging.ts index e3551625..37b8baff 100644 --- a/src/logging.ts +++ b/src/logging.ts @@ -16,8 +16,118 @@ interface LoggerOptions { const DEFAULT_LOGGER_NAME = 'mcp-use' +// Environment detection function (similar to telemetry) +function isNodeJSEnvironment(): boolean { + try { + // Check for Cloudflare Workers specifically + if (typeof navigator !== 'undefined' && navigator.userAgent?.includes('Cloudflare-Workers')) { + return false + } + + // Check for other edge runtime indicators + if (typeof (globalThis as any).EdgeRuntime !== 'undefined' || typeof (globalThis as any).Deno !== 'undefined') { + return false + } + + // Check for Node.js specific globals that are not available in edge environments + const hasNodeGlobals = ( + typeof process !== 'undefined' + && typeof process.platform !== 'undefined' + && typeof __dirname !== 'undefined' + ) + + // Check for Node.js modules + const hasNodeModules = ( + typeof fs !== 'undefined' + && typeof createLogger === 'function' + ) + + return hasNodeGlobals && hasNodeModules + } + catch { + return false + } +} + +// Simple console logger for non-Node.js environments +class SimpleConsoleLogger { + private _level: LogLevel + private name: string + + constructor(name: string = DEFAULT_LOGGER_NAME, level: LogLevel = 'info') { + this.name = name + this._level = level + } + + private shouldLog(level: LogLevel): boolean { + const levels = ['error', 'warn', 'info', 'http', 'verbose', 'debug', 'silly'] + const currentIndex = levels.indexOf(this._level) + const messageIndex = levels.indexOf(level) + return messageIndex <= currentIndex + } + + private formatMessage(level: LogLevel, message: string): string { + const timestamp = new Date().toLocaleTimeString('en-US', { hour12: false }) + return `${timestamp} [${this.name}] ${level}: ${message}` + } + + error(message: string): void { + if (this.shouldLog('error')) { + console.error(this.formatMessage('error', message)) + } + } + + warn(message: string): void { + if (this.shouldLog('warn')) { + console.warn(this.formatMessage('warn', message)) + } + } + + info(message: string): void { + if (this.shouldLog('info')) { + console.info(this.formatMessage('info', message)) // eslint-disable-line no-console + } + } + + debug(message: string): void { + if (this.shouldLog('debug')) { + console.debug(this.formatMessage('debug', message)) // eslint-disable-line no-console + } + } + + http(message: string): void { + if (this.shouldLog('http')) { + console.log(this.formatMessage('http', message)) // eslint-disable-line no-console + } + } + + verbose(message: string): void { + if (this.shouldLog('verbose')) { + console.log(this.formatMessage('verbose', message)) // eslint-disable-line no-console + } + } + + silly(message: string): void { + if (this.shouldLog('silly')) { + console.log(this.formatMessage('silly', message)) // eslint-disable-line no-console + } + } + + // Make it compatible with Winston interface + get level(): LogLevel { + return this._level + } + + set level(newLevel: LogLevel) { + this._level = newLevel + } +} + function resolveLevel(env: string | undefined): LogLevel { - switch (env?.trim()) { + // Safely access environment variables + const envValue = (typeof process !== 'undefined' && process.env) ? env : undefined + + switch (envValue?.trim()) { case '2': return 'debug' case '1': @@ -40,10 +150,21 @@ const emojiFormatter = printf(({ level, message, label, timestamp }) => { }) export class Logger { - private static instances: Record = {} + private static instances: Record = {} + private static simpleInstances: Record = {} private static currentFormat: 'minimal' | 'detailed' | 'emoji' = 'minimal' - public static get(name: string = DEFAULT_LOGGER_NAME): WinstonLogger { + public static get(name: string = DEFAULT_LOGGER_NAME): WinstonLogger | SimpleConsoleLogger { + // Use simple console logger in non-Node.js environments + if (!isNodeJSEnvironment()) { + if (!this.simpleInstances[name]) { + const debugEnv = (typeof process !== 'undefined' && process.env?.DEBUG) || undefined + this.simpleInstances[name] = new SimpleConsoleLogger(name, resolveLevel(debugEnv)) + } + return this.simpleInstances[name] + } + + // Use Winston logger in Node.js environments if (!this.instances[name]) { this.instances[name] = createLogger({ level: resolveLevel(process.env.DEBUG), @@ -76,17 +197,28 @@ export class Logger { public static configure(options: LoggerOptions = {}): void { const { level, console = true, file, format = 'minimal' } = options - const resolvedLevel = level ?? resolveLevel(process.env.DEBUG) + const debugEnv = (typeof process !== 'undefined' && process.env?.DEBUG) || undefined + const resolvedLevel = level ?? resolveLevel(debugEnv) this.currentFormat = format const root = this.get() root.level = resolvedLevel - root.clear() + // For non-Node.js environments, just update the level + if (!isNodeJSEnvironment()) { + Object.values(this.simpleInstances).forEach((logger) => { + logger.level = resolvedLevel + }) + return + } + + // Winston-specific configuration for Node.js environments + const winstonRoot = root as WinstonLogger + winstonRoot.clear() if (console) { - root.add(new transports.Console()) + winstonRoot.add(new transports.Console()) } if (file) { @@ -94,19 +226,21 @@ export class Logger { if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }) } - root.add(new transports.File({ filename: file })) + winstonRoot.add(new transports.File({ filename: file })) } - // Update all existing loggers with new format + // Update all existing Winston loggers with new format Object.values(this.instances).forEach((logger) => { - logger.level = resolvedLevel - logger.format = combine( - colorize(), - splat(), - label({ label: DEFAULT_LOGGER_NAME }), - timestamp({ format: 'HH:mm:ss' }), - this.getFormatter(), - ) + if (logger && 'format' in logger) { + logger.level = resolvedLevel + ;(logger as WinstonLogger).format = combine( + colorize(), + splat(), + label({ label: DEFAULT_LOGGER_NAME }), + timestamp({ format: 'HH:mm:ss' }), + this.getFormatter(), + ) + } }) } @@ -118,10 +252,21 @@ export class Logger { level = 'info' else level = 'info' - Object.values(this.instances).forEach((logger) => { + // Update both simple and Winston loggers + Object.values(this.simpleInstances).forEach((logger) => { logger.level = level }) - process.env.DEBUG = enabled ? (enabled === true ? '2' : String(enabled)) : '0' + + Object.values(this.instances).forEach((logger) => { + if (logger) { + logger.level = level + } + }) + + // Safely set environment variable + if (typeof process !== 'undefined' && process.env) { + process.env.DEBUG = enabled ? (enabled === true ? '2' : String(enabled)) : '0' + } } public static setFormat(format: 'minimal' | 'detailed' | 'emoji'): void { @@ -130,5 +275,13 @@ export class Logger { } } -Logger.configure() +// Only configure Winston features if in Node.js environment +if (isNodeJSEnvironment()) { + Logger.configure() +} +else { + // For non-Node.js environments, just initialize with defaults + Logger.configure({ console: true }) +} + export const logger = Logger.get()