---
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<int>(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.
