--- name: logger kind: function lang: cpp domain: core version: "1.0.0" purity: impure signature: "bool fn_log::logger_init(const char* file_path, fn_log::Level min_level = Level::Info); void fn_log::logger_close(); void fn_log::logger_set_level(Level); fn_log::Level fn_log::logger_level(); const char* fn_log::logger_path(); void fn_log::log_debug(const char* fmt, ...); void fn_log::log_info(const char* fmt, ...); void fn_log::log_warn(const char* fmt, ...); void fn_log::log_error(const char* fmt, ...); std::size_t fn_log::buffer_size(); const fn_log::Entry* fn_log::buffer_at(std::size_t); void fn_log::buffer_clear(); const char* fn_log::level_label(fn_log::Level)" description: "Logger global thread-safe para apps C++ del registry. Escribe a archivo (cwd, junto al ejecutable) en modo append y mantiene un ring buffer in-memory de 2000 entradas que el visualizador log_window consume. Formato: [YYYY-MM-DD HH:MM:SS.mmm] [LEVEL] mensaje." tags: [logger, logging, file, infra, thread-safe] uses_functions: [] uses_types: [log_level_cpp_core, log_entry_cpp_core] returns: [] returns_optional: false error_type: "error_go_core" imports: [chrono, cstdarg, cstdio, ctime, mutex, string] tested: false tests: [] test_file_path: "" file_path: "cpp/functions/core/logger.cpp" framework: "" params: - name: file_path desc: "Ruta del archivo de log relativa al cwd (junto al ejecutable). Modo append. Si vacio o nullptr, no se escribe a disco — solo buffer in-memory" - name: min_level desc: "Nivel minimo a aceptar. Mensajes por debajo se descartan antes de tocar archivo o buffer" - name: fmt desc: "Formato printf-style de log_debug/info/warn/error. Cada llamada produce una linea independiente" - name: i desc: "Indice [0, buffer_size()) en el ring buffer. 0 = entrada mas antigua viva" output: "logger_init retorna true si pudo abrir el archivo (false → solo buffer). logger_close cierra archivo (idempotente). log_* mutan estado global thread-safe. buffer_at retorna puntero valido o nullptr si i fuera de rango" notes: "consumido por cpp/framework/app_base.cpp (init/close automatico via AppConfig.log) y cpp/functions/core/log_window.cpp (lectura del buffer)" --- # logger Logger global, thread-safe, integrado en `fn::run_app`: las apps solo declaran un `AppLogConfig` y emiten con `log_info(...)` etc. ## Uso desde una app ```cpp #include "app_base.h" #include "core/logger.h" int main() { return fn::run_app({ .title = "Mi App", .log = {.file_path = "mi_app.log", .level = static_cast(fn_log::Level::Info)} }, render); } ``` Tras esto, `fn::run_app` llama `logger_init` antes del primer frame y `logger_close` al exit. La app solo necesita usar los emisores: ```cpp fn_log::log_info ("usuario abrio archivo %s", path); fn_log::log_warn ("retry %d/%d", attempt, max); fn_log::log_error("connection failed: %s", reason); fn_log::log_debug("estado interno: %d items", n); ``` ## Uso sin fn::run_app Apps que arman su propio main loop deben llamar manualmente: ```cpp fn_log::logger_init("app.log", fn_log::Level::Info); // ... vida de la app ... fn_log::logger_close(); ``` ## Reglas - Ruta relativa al cwd (igual convencion que `app_settings.ini`). - Modo append: relanzar la app conserva el historico previo en disco. - Thread-safe: un mutex interno protege archivo + buffer + nivel. - Truncacion: cada mensaje cabe en `kEntryTextMax - 64` caracteres formateados; el resto se trunca silenciosamente. - Si `logger_init` no se llama o falla, los `log_*` siguen siendo seguros: solo escriben al ring buffer in-memory (que la ventana `Logs` puede mostrar igualmente). ## Integracion - `fn::AppConfig::log` activa el logger desde el framework. - `fn_ui::log_window` lee el ring buffer y pinta la ventana "Logs..." del menubar.