feat(cpp/core): logger + log_window + selectable_text widgets
Logger global thread-safe con ring buffer in-memory de 2000 entradas + escritura opcional a archivo. log_window flotante consume el ring buffer con filtros por nivel, busqueda y autoscroll; se abre desde Settings -> Logs en la menubar. selectable_text cubre el patron drag-to-select + Ctrl+C en cualquier ventana. app_menubar y framework run_app integran log_window_render() en el frame loop. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
// 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
|
||||
Reference in New Issue
Block a user