#pragma once #include // Logger global thread-safe para apps del registry. Escribe a archivo (cwd // junto al ejecutable, igual que app_settings.ini) y mantiene un ring buffer // in-memory que el visualizador (log_window) consume. // // Lifecycle: // - run_app llama logger_init(cfg.log_file, cfg.log_level) si log_file != nullptr // - app llama log_info / log_warn / ... durante su ciclo de vida // - run_app llama logger_close() al exit // // Apps que NO usan fn::run_app deben llamar logger_init/close manualmente. // Si nunca se llama logger_init, los log_* siguen funcionando contra el ring // buffer in-memory pero no escriben a disco. // // Formato de cada linea: // [YYYY-MM-DD HH:MM:SS.mmm] [LEVEL] mensaje namespace fn_log { enum class Level : int { Debug = 0, Info = 1, Warn = 2, Error = 3, }; // Inicializa el logger global. file_path se interpreta relativo al cwd // (donde la app ya escribe app_settings.ini). Crea/trunca el archivo si no // existe; si existe, abre en modo append. // // Idempotente: si ya hay un archivo abierto, lo cierra y reabre el nuevo. // Returns true si pudo abrir el archivo (false → solo buffer in-memory). bool logger_init(const char* file_path, Level min_level = Level::Info); // Cierra el archivo. log_* siguen funcionando contra el buffer in-memory. void logger_close(); // Nivel minimo. Mensajes por debajo se descartan silenciosamente (no van ni // al archivo ni al buffer). void logger_set_level(Level level); Level logger_level(); // Path del archivo activo. Vacio si no inicializado o cerrado. const char* logger_path(); // Emisores. Formato printf-style. Cada llamada escribe una linea completa. // Thread-safe (mutex interno). void log_debug(const char* fmt, ...); void log_info (const char* fmt, ...); void log_warn (const char* fmt, ...); void log_error(const char* fmt, ...); // === Ring buffer in-memory (para log_window) === constexpr std::size_t kBufferCapacity = 2000; constexpr std::size_t kEntryTextMax = 480; // deja sitio para timestamp + level struct Entry { Level level; long long ts_ms; // unix epoch en milisegundos char text[kEntryTextMax]; }; // Numero de entradas vivas en el buffer (≤ kBufferCapacity). std::size_t buffer_size(); // Acceso por indice [0, buffer_size()). i==0 es la entrada mas antigua viva. // Nullptr si i fuera de rango. Snapshot — el caller debe asumir que la // entrada puede ser sobrescrita en la siguiente llamada thread-unsafe; para // el viewer esto no es problema porque ImGui es single-threaded. const Entry* buffer_at(std::size_t i); // Limpia el ring buffer. No toca el archivo en disco. void buffer_clear(); // Helper para el viewer: nombre corto del nivel ("DEBUG"/"INFO"/...). const char* level_label(Level level); } // namespace fn_log