/* eslint-disable @typescript-eslint/no-explicit-any */ "use client"; export enum LogType { API = "API", SOCKET = "SOCKET", ERROR = "ERROR", INFO = "INFO", } export enum LogDirection { REQUEST = "REQUEST", RESPONSE = "RESPONSE", INCOMING = "INCOMING", OUTGOING = "OUTGOING", } interface LogEntry { type: LogType; direction?: LogDirection; event: string; data?: unknown; url?: string; method?: string; status?: number; timestamp: Date; duration?: number; } class DevLogger { private enabled = false; private enabledTypes = new Set(Object.values(LogType)); private envEnabled = false; private serverLoggingEnabled = false; constructor() { // Check ENV variables first if (typeof window !== "undefined") { this.envEnabled = process.env.NEXT_PUBLIC_DEV_LOGGER_ENABLED !== "false"; } else { // Server side - check server env this.serverLoggingEnabled = process.env.DEV_LOGGER_SERVER_ENABLED === "true"; this.envEnabled = process.env.DEV_LOGGER_ENABLED !== "false"; } // Check localStorage for logging preferences (client-side only) if (typeof window !== "undefined") { const stored = localStorage.getItem("dev-logger-enabled"); this.enabled = stored ? JSON.parse(stored) : this.envEnabled; const storedTypes = localStorage.getItem("dev-logger-types"); if (storedTypes) { this.enabledTypes = new Set(JSON.parse(storedTypes)); } } else { this.enabled = this.envEnabled; } } private shouldLog(type: LogType): boolean { // Check ENV first, then user preferences return this.envEnabled && this.enabled && this.enabledTypes.has(type); } private shouldLogServer(type: LogType): boolean { // Server logging requires explicit ENV enable return ( this.serverLoggingEnabled && this.envEnabled && this.enabled && this.enabledTypes.has(type) ); } private getLogStyle( type: LogType, direction?: LogDirection ): { emoji: string; color: string; bgColor?: string } { const styles: Record = { [LogType.API]: { [LogDirection.REQUEST]: { emoji: "šŸš€", color: "#3b82f6", bgColor: "#eff6ff", }, [LogDirection.RESPONSE]: { emoji: "šŸ“Ø", color: "#10b981", bgColor: "#f0fdf4", }, }, [LogType.SOCKET]: { [LogDirection.OUTGOING]: { emoji: "🟢", color: "#16a34a" }, [LogDirection.INCOMING]: { emoji: "šŸ”µ", color: "#2563eb" }, }, [LogType.ERROR]: { emoji: "āŒ", color: "#ef4444" }, [LogType.INFO]: { emoji: "ā„¹ļø", color: "#6366f1" }, }; const typeStyles = styles[type]; if ( direction && typeof typeStyles === "object" && direction in typeStyles ) { return typeStyles[direction]; } return typeof typeStyles === "object" ? { emoji: "šŸ“", color: "#6b7280" } : typeStyles; } private formatTime(date: Date): string { return date.toLocaleTimeString("en-US", { hour12: false, hour: "2-digit", minute: "2-digit", second: "2-digit", fractionalSecondDigits: 3, }); } log(entry: Omit) { if (!this.shouldLog(entry.type)) return; const timestamp = new Date(); const { emoji, color, bgColor } = this.getLogStyle( entry.type, entry.direction ); const timeStr = this.formatTime(timestamp); const baseStyle = `color: ${color}; font-weight: bold;`; const groupStyle = bgColor ? `${baseStyle} background: ${bgColor}; padding: 2px 6px; border-radius: 3px;` : baseStyle; // Create compact collapsible group const groupTitle = `${emoji} ${entry.type}${ entry.direction ? ` ${entry.direction}` : "" }: ${entry.event}`; // Always use groupCollapsed for cleaner output console.groupCollapsed(`%c${groupTitle} [${timeStr}]`, groupStyle); // Compact one-line summary with key info const summaryParts = []; if (entry.method) summaryParts.push(`${entry.method}`); if (entry.status) { const statusColor = entry.status >= 200 && entry.status < 300 ? "āœ…" : "āŒ"; summaryParts.push(`${statusColor} ${entry.status}`); } if (entry.duration !== undefined) summaryParts.push(`ā±ļø ${entry.duration}ms`); if (summaryParts.length > 0) { console.log( `%c${summaryParts.join(" • ")}`, "color: #6b7280; font-size: 11px;" ); } if (entry.data !== undefined) { // Show preview for objects/arrays, full value for primitives if (typeof entry.data === "object" && entry.data !== null) { const preview = Array.isArray(entry.data) ? `Array[${entry.data.length}]` : `Object{${Object.keys(entry.data).slice(0, 3).join(", ")}${ Object.keys(entry.data).length > 3 ? "..." : "" }}`; console.log(`%cšŸ“¦ Data:`, "color: #6b7280; font-size: 11px;", preview); console.log(entry.data); } else { console.log( `%cšŸ“¦ Data:`, "color: #6b7280; font-size: 11px;", entry.data ); } } console.groupEnd(); } // API logging methods apiRequest(url: string, method: string, data?: unknown) { this.log({ type: LogType.API, direction: LogDirection.REQUEST, event: `${method.toUpperCase()} ${url.split("?")[0]}`, url, method, data, }); } apiResponse( url: string, method: string, status: number, data?: unknown, duration?: number ) { this.log({ type: LogType.API, direction: LogDirection.RESPONSE, event: `${method.toUpperCase()} ${url.split("?")[0]}`, url, method, status, data, duration, }); } // Socket logging methods socketOutgoing(event: string, data?: unknown) { this.log({ type: LogType.SOCKET, direction: LogDirection.OUTGOING, event, data, }); } socketIncoming(event: string, data?: unknown) { this.log({ type: LogType.SOCKET, direction: LogDirection.INCOMING, event, data, }); } // Connection state logging socketConnected() { console.log( `%cāœ… SOCKET CONNECTED`, "color: #10b981; font-weight: bold; background: #f0fdf4; padding: 2px 6px; border-radius: 3px;" ); } socketDisconnected(reason?: string) { console.log( `%cāŒ SOCKET DISCONNECTED`, "color: #ef4444; font-weight: bold; background: #fef2f2; padding: 2px 6px; border-radius: 3px;", reason || "" ); } socketError(error?: unknown) { console.log( `%cāš ļø SOCKET ERROR`, "color: #f59e0b; font-weight: bold; background: #fffbeb; padding: 2px 6px; border-radius: 3px;", error || "" ); } // Server-side logging methods serverApiRequest(url: string, method: string, body?: unknown) { if (!this.shouldLogServer(LogType.API)) return; console.group(`\nšŸš€ [SERVER] API REQUEST: ${method} ${url}`); if (body !== undefined) { console.log("šŸ“¦ Request Body:", JSON.stringify(body, null, 2)); } console.groupEnd(); } serverApiResponse( url: string, method: string, status: number, data?: unknown, duration?: number ) { if (!this.shouldLogServer(LogType.API)) return; const emoji = status >= 200 && status < 300 ? "āœ…" : "āŒ"; console.group( `\n${emoji} [SERVER] API ${ status >= 200 && status < 300 ? "SUCCESS" : "ERROR" }: ${method} ${url}` ); console.log(`šŸ“Š Status: ${status}`); if (duration !== undefined) { console.log(`ā±ļø Duration: ${duration}ms`); } if (data !== undefined) { // Limit response data display to avoid overwhelming logs const responsePreview = typeof data === "object" && data !== null ? Array.isArray(data) ? `Array[${data.length}]` : `Object{${Object.keys(data).slice(0, 5).join(", ")}${ Object.keys(data).length > 5 ? "..." : "" }}` : data; console.log("šŸ“¦ Response Preview:", responsePreview); // Full response data (collapsed) console.groupCollapsed("šŸ“„ Full Response Data:"); console.log(data); console.groupEnd(); } console.groupEnd(); } // Control methods enable() { this.enabled = true; if (typeof window !== "undefined") { localStorage.setItem("dev-logger-enabled", "true"); } console.log( "%cšŸ“ Dev Logger ENABLED", "color: #10b981; font-weight: bold;" ); } disable() { this.enabled = false; if (typeof window !== "undefined") { localStorage.setItem("dev-logger-enabled", "false"); } console.log( "%cšŸ“ Dev Logger DISABLED", "color: #ef4444; font-weight: bold;" ); } enableType(type: LogType) { this.enabledTypes.add(type); this.saveEnabledTypes(); console.log( `%cšŸ“ ${type} logging ENABLED`, "color: #10b981; font-weight: bold;" ); } disableType(type: LogType) { this.enabledTypes.delete(type); this.saveEnabledTypes(); console.log( `%cšŸ“ ${type} logging DISABLED`, "color: #ef4444; font-weight: bold;" ); } private saveEnabledTypes() { if (typeof window !== "undefined") { localStorage.setItem( "dev-logger-types", JSON.stringify(Array.from(this.enabledTypes)) ); } } // Helper method to show current settings status() { console.group( "%cšŸ”§ Dev Logger Status", "color: #6366f1; font-weight: bold;" ); console.log("Enabled:", this.enabled); console.log("Active Types:", Array.from(this.enabledTypes)); console.groupEnd(); } } // Create singleton instance export const devLogger = new DevLogger(); // Make it available globally for easy console access if (typeof window !== "undefined") { (window as any).devLogger = devLogger; } // Export convenience methods for quick filtering export const filterAPI = () => { console.clear(); devLogger.disableType(LogType.SOCKET); devLogger.disableType(LogType.ERROR); devLogger.disableType(LogType.INFO); devLogger.enableType(LogType.API); }; export const filterSocket = () => { console.clear(); devLogger.disableType(LogType.API); devLogger.disableType(LogType.ERROR); devLogger.disableType(LogType.INFO); devLogger.enableType(LogType.SOCKET); }; export const showAll = () => { console.clear(); Object.values(LogType).forEach((type) => devLogger.enableType(type)); };