Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 6x 10x 10x 4x 4x 1x 10x 10x 10x 2x 10x 10x 8x 8x 2x 2x 6x 4x | import * as fs from 'fs';
import * as path from 'path';
export type LoggingMode = 'file' | 'stderr' | 'auto';
export class Logger {
private static instance: Logger | undefined;
private logFile: string;
private sessionId: string;
private isStdioMode: boolean = false;
private loggingMode: LoggingMode;
private constructor() {
const date = new Date().toISOString().split('T')[0];
this.sessionId = Math.random().toString(36).substring(2, 8);
// Use single log file for all sessions
this.logFile = path.join('/tmp', `.log.nexus.${date}`);
// Get logging mode from environment variable
const envMode = process.env.NEXUS_LOG_MODE?.toLowerCase() as LoggingMode;
this.loggingMode = ['file', 'stderr', 'auto'].includes(envMode) ? envMode : 'auto';
// Create log file if it doesn't exist (for file mode)
this.ensureLogFile();
}
static getInstance(): Logger {
Logger.instance ??= new Logger();
return Logger.instance;
}
setStdioMode(enabled: boolean): void {
this.isStdioMode = enabled;
}
private shouldUseFileLogging(): boolean {
switch (this.loggingMode) {
case 'file':
return true;
case 'stderr':
return false;
case 'auto':
default:
// Auto mode: file for STDIO, stderr for HTTP/SSE
return this.isStdioMode;
}
}
private ensureLogFile(): void {
try {
if (!fs.existsSync(this.logFile)) {
fs.writeFileSync(
this.logFile,
`[${new Date().toISOString()}] [INFO] [${this.sessionId}] Project Nexus MCP Server Log Started\n`,
);
}
} catch {
// Fallback to stderr if we can't create log file
if (!this.shouldUseFileLogging()) {
console.error('Failed to create log file, falling back to stderr');
}
}
}
private writeToFile(level: string, message: string, ...args: unknown[]): void {
try {
const timestamp = new Date().toISOString();
const formattedMessage =
args.length > 0
? `${message} ${args
.map((arg) =>
typeof arg === 'object' && arg !== null
? JSON.stringify(arg, null, 2)
: String(arg),
)
.join(' ')}`
: message;
const logLine = `[${timestamp}] [${level}] [${this.sessionId}] ${formattedMessage}\n`;
fs.appendFileSync(this.logFile, logLine);
} catch {
// Silent fail - don't pollute stderr when using file logging
if (!this.shouldUseFileLogging()) {
console.error('Failed to write to log file');
}
}
}
log(message: string, ...args: unknown[]): void {
if (this.shouldUseFileLogging()) {
this.writeToFile('INFO', message, ...args);
} else E{
console.log(message, ...args);
}
}
error(message: string, ...args: unknown[]): void {
if (this.shouldUseFileLogging()) {
this.writeToFile('ERROR', message, ...args);
} else E{
console.error(message, ...args);
}
}
warn(message: string, ...args: unknown[]): void {
if (this.shouldUseFileLogging()) {
this.writeToFile('WARN', message, ...args);
} else {
console.warn(message, ...args);
}
}
debug(message: string, ...args: unknown[]): void {
if (this.shouldUseFileLogging()) {
this.writeToFile('DEBUG', message, ...args);
} else {
console.debug(message, ...args);
}
}
getLogFile(): string {
return this.logFile;
}
}
// Global logger instance
export const logger = Logger.getInstance();
|