AbdulElahGwaith's picture
Upload folder using huggingface_hub
b91e262 verified
import fs from 'fs'
import path from 'path'
export interface LogEntry {
timestamp: string
source: 'Server' | 'Browser'
level: string
message: string
}
// Logging server and browser logs to a file
export class FileLogger {
private logFilePath: string = ''
private isInitialized: boolean = false
private logQueue: string[] = []
private flushTimer: NodeJS.Timeout | null = null
private mcpServerEnabled: boolean = false
public initialize(distDir: string, mcpServerEnabled: boolean): void {
this.logFilePath = path.join(distDir, 'logs', `next-development.log`)
this.mcpServerEnabled = mcpServerEnabled
if (this.isInitialized) {
return
}
// Only initialize if mcpServer is enabled
if (!this.mcpServerEnabled) {
return
}
try {
// Clean up the log file on each initialization
// ensure the directory exists
fs.mkdirSync(path.dirname(this.logFilePath), { recursive: true })
fs.writeFileSync(this.logFilePath, '')
this.isInitialized = true
} catch (error) {
console.error(error)
}
}
private formatTimestamp(): string {
// Use performance.now() instead of Date.now() for avoid sync IO of cache components
const now = performance.now()
const hours = Math.floor(now / 3600000)
.toString()
.padStart(2, '0')
const minutes = Math.floor((now % 3600000) / 60000)
.toString()
.padStart(2, '0')
const seconds = Math.floor((now % 60000) / 1000)
.toString()
.padStart(2, '0')
const milliseconds = Math.floor(now % 1000)
.toString()
.padStart(3, '0')
return `${hours}:${minutes}:${seconds}.${milliseconds}`
}
private formatLogEntry(entry: LogEntry): string {
const { timestamp, source, level, message } = entry
const levelPadded = level.toUpperCase().padEnd(7, ' ') // Pad level to 7 characters for alignment
const sourcePadded = source === 'Browser' ? source : 'Server '
return `[${timestamp}] ${sourcePadded} ${levelPadded} ${message}\n`
}
private scheduleFlush(): void {
// Debounce the flush
if (this.flushTimer) {
clearTimeout(this.flushTimer)
this.flushTimer = null
}
// Delay the log flush to ensure more logs can be batched together asynchronously
this.flushTimer = setTimeout(() => {
this.flush()
}, 100)
}
public getLogQueue(): string[] {
return this.logQueue
}
private flush(): void {
if (this.logQueue.length === 0) {
return
}
// Only flush to disk if mcpServer is enabled
if (!this.mcpServerEnabled) {
this.logQueue = [] // Clear the queue without writing
this.flushTimer = null
return
}
try {
// Ensure the directory exists before writing
const logDir = path.dirname(this.logFilePath)
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir, { recursive: true })
}
const logsToWrite = this.logQueue.join('')
// Writing logs to files synchronously to ensure they're written before returning
fs.appendFileSync(this.logFilePath, logsToWrite)
this.logQueue = []
} catch (error) {
console.error('Failed to flush logs to file:', error)
} finally {
this.flushTimer = null
}
}
private enqueueLog(formattedEntry: string): void {
this.logQueue.push(formattedEntry)
// Cancel existing timer and start a new one to ensure all logs are flushed together
if (this.flushTimer) {
clearTimeout(this.flushTimer)
this.flushTimer = null
}
this.scheduleFlush()
}
log(source: 'Server' | 'Browser', level: string, message: string): void {
// Don't log anything if mcpServer is disabled
if (!this.mcpServerEnabled) {
return
}
if (!this.isInitialized) {
return
}
const logEntry: LogEntry = {
timestamp: this.formatTimestamp(),
source,
level,
message,
}
const formattedEntry = this.formatLogEntry(logEntry)
this.enqueueLog(formattedEntry)
}
logServer(level: string, message: string): void {
this.log('Server', level, message)
}
logBrowser(level: string, message: string): void {
this.log('Browser', level, message)
}
// Force flush all queued logs immediately
forceFlush(): void {
if (this.flushTimer) {
clearTimeout(this.flushTimer)
this.flushTimer = null
}
this.flush()
}
// Cleanup method to flush logs on process exit
destroy(): void {
this.forceFlush()
}
}
// Singleton instance
let fileLogger: FileLogger | null = null
export function getFileLogger(): FileLogger {
if (!fileLogger || process.env.NODE_ENV === 'test') {
fileLogger = new FileLogger()
}
return fileLogger
}
// Only used for testing
export function test__resetFileLogger(): void {
if (fileLogger) {
fileLogger.destroy()
}
fileLogger = null
}