diff options
Diffstat (limited to 'logger.ts')
| -rw-r--r-- | logger.ts | 141 |
1 files changed, 134 insertions, 7 deletions
@@ -1,27 +1,154 @@ type LogLevel = "INFO" | "WARN" | "ERROR" | "DEBUG"; +type LogDomain = "server" | "upload" | "download" | "security" | "cleanup" | "request"; + +interface LogEntry { + timestamp: string; + level: LogLevel; + domain: LogDomain; + message: string; + meta?: Record<string, unknown>; +} class Logger { - private log(level: LogLevel, message: string, meta?: Record<string, unknown>) { + private logDir = "./logs"; + private currentDate = ""; + private logBuffer: string[] = []; + private flushInterval: number; + + constructor() { + this.ensureLogDir(); + // Flush logs every 5 seconds + this.flushInterval = setInterval(() => this.flush(), 5000); + } + + private async ensureLogDir() { + try { + await Deno.mkdir(this.logDir, { recursive: true }); + } catch (error) { + if (!(error instanceof Deno.errors.AlreadyExists)) { + console.error("Failed to create log directory:", error); + } + } + } + + private getDateString(): string { + const now = new Date(); + return now.toISOString().split('T')[0]; // YYYY-MM-DD + } + + private formatLogEntry(entry: LogEntry): string { + const metaStr = entry.meta ? ` ${JSON.stringify(entry.meta)}` : ""; + return `[${entry.timestamp}] [${entry.level}] [${entry.domain}] ${entry.message}${metaStr}\n`; + } + + private async writeToFile(domain: LogDomain, content: string) { + const date = this.getDateString(); + const filename = `${this.logDir}/${domain}-${date}.log`; + + try { + await Deno.writeTextFile(filename, content, { append: true }); + } catch (error) { + console.error(`Failed to write to log file ${filename}:`, error); + } + } + + private log(level: LogLevel, domain: LogDomain, message: string, meta?: Record<string, unknown>) { const timestamp = new Date().toISOString(); - const metaStr = meta ? ` ${JSON.stringify(meta)}` : ""; - console.log(`[${timestamp}] [${level}] ${message}${metaStr}`); + const entry: LogEntry = { timestamp, level, domain, message, meta }; + + // Write to console + const formatted = this.formatLogEntry(entry); + console.log(formatted.trim()); + + // Buffer for file writing + this.logBuffer.push(JSON.stringify({ ...entry, file: domain })); } + private async flush() { + if (this.logBuffer.length === 0) return; + + const entries = [...this.logBuffer]; + this.logBuffer = []; + + // Group entries by domain + const byDomain = new Map<LogDomain, LogEntry[]>(); + + for (const entryStr of entries) { + try { + const entry = JSON.parse(entryStr); + const domain = entry.file as LogDomain; + if (!byDomain.has(domain)) { + byDomain.set(domain, []); + } + byDomain.get(domain)!.push(entry); + } catch (error) { + console.error("Failed to parse log entry:", error); + } + } + + // Write to files + for (const [domain, domainEntries] of byDomain) { + const content = domainEntries.map(e => this.formatLogEntry(e)).join(''); + await this.writeToFile(domain, content); + } + } + + // Server domain logs + server(level: LogLevel, message: string, meta?: Record<string, unknown>) { + this.log(level, "server", message, meta); + } + + // Upload domain logs + upload(level: LogLevel, message: string, meta?: Record<string, unknown>) { + this.log(level, "upload", message, meta); + } + + // Download domain logs + download(level: LogLevel, message: string, meta?: Record<string, unknown>) { + this.log(level, "download", message, meta); + } + + // Security domain logs (rate limiting, IP tracking) + security(level: LogLevel, message: string, meta?: Record<string, unknown>) { + this.log(level, "security", message, meta); + } + + // Cleanup domain logs + cleanup(level: LogLevel, message: string, meta?: Record<string, unknown>) { + this.log(level, "cleanup", message, meta); + } + + // Request domain logs (HTTP requests) + request(level: LogLevel, message: string, meta?: Record<string, unknown>) { + this.log(level, "request", message, meta); + } + + // Legacy methods for backward compatibility info(message: string, meta?: Record<string, unknown>) { - this.log("INFO", message, meta); + this.log("INFO", "server", message, meta); } warn(message: string, meta?: Record<string, unknown>) { - this.log("WARN", message, meta); + this.log("WARN", "server", message, meta); } error(message: string, meta?: Record<string, unknown>) { - this.log("ERROR", message, meta); + this.log("ERROR", "server", message, meta); } debug(message: string, meta?: Record<string, unknown>) { - this.log("DEBUG", message, meta); + this.log("DEBUG", "server", message, meta); + } + + async shutdown() { + clearInterval(this.flushInterval); + await this.flush(); } } export const logger = new Logger(); + +// Ensure logs are flushed on exit +addEventListener("unload", () => { + logger.shutdown(); +}); |
