# Especificación de Logging — ComercioNet ## Objetivo Implementar un sistema de logging jerárquico con formato visual de árbol (`├──`, `└──`, `│`) que permita entender rápidamente el flujo de procesamiento de cada archivo y sus sub-operaciones (SAP B1, SQL, etc.). ## API El módulo `logger_manager.py` expone una función única: ```python from logger_manager import get_logger log = get_logger() # devuelve una instancia de TaskLogger ``` ### Métodos de `TaskLogger` | Método | Descripción | Prefijo visual | |---|---|---| | `info(msg, _last=False)` | Mensaje informativo | `├──` o `└──` | | `warning(msg, _last=False)` | Advertencia | `├──` o `└──` | | `error(msg, _last=False)` | Error | `├──` o `└──` | | `task(name, emoji="")` | Context manager — abre un bloque anidado (push depth) | `├── + emoji + name` | | `last_task(name, emoji="")` | Context manager — último bloque del padre (push depth) | `└── + emoji + name` | ### Parámetro `_last` En `info()`, `warning()`, `error()`, el parámetro `_last=True` cambia el prefijo de `├──` a `└──`, indicando que es el último mensaje en ese nivel. ```python log.info("Primer paso") # ├── Primer paso log.info("Paso intermedio") # ├── Paso intermedio log.info("Último paso", _last=True) # └── Último paso ``` ## Estructura jerárquica con `task()` / `last_task()` ```python with log.task("📦 Archivo X.xml"): log.info("XML parseado") with log.task("🔐 SAP B1"): log.info("Sesión iniciada") log.info("Catálogo OK") log.last_task("📁 Movido a processed") ``` ### Salida generada ``` 2026-07-31 10:15:03 ─── 📦 Archivo X.xml ├── XML parseado ├── 🔐 SAP B1 │ ├── Sesión iniciada │ └── Catálogo OK └── 📁 Movido a processed ``` ## Formato de cada línea ``` {timestamp} ─── {prefijo_árbol}{mensaje} ``` Ejemplo: ``` 2026-07-31 10:15:03 ─── │ ├── XML parseado ``` - `timestamp`: `YYYY-MM-DD HH:mm:ss` (precisión de segundos) - `───`: separador fijo - `prefijo_árbol`: caracteres de indentación y árbol según profundidad - `mensaje`: texto del log ## Formato en archivo de log El archivo `logs/comercio-net.log` usa el mismo formato con caracteres de árbol. No se incluyen niveles (INFO, WARNING, ERROR) en el texto — la diferenciación visual se logra con emojis y el contexto jerárquico. ## Emojis por etapa | Etapa | Emoji | |---|---| | Procesar archivo XML | 📦 | | Parseo XML / datos extraídos | 📄 | | Guardar JSON / HTML | 💾 | | SAP B1 (sesión, catálogo, draft) | 🔐 | | Consultas HTTP (GET / POST) | 🔄 | | Éxito / Draft creado | ✅ | | Advertencia (precio difiere) | ⚠️ | | Error | ❌ | | Archivo movido | 📁 | | Inicio / fin de servicio | 🚀 / 🛑 | ## Configuración vía entorno Variable `LOG_LEVEL` en `.env` (valores: `DEBUG`, `INFO`, `WARNING`, `ERROR`). Por defecto `INFO`. ``` LOG_LEVEL=DEBUG ``` ## Thread-safety Se usa `contextvars` para mantener el stack de profundidad por cada hilo / contexto asíncrono, permitiendo ejecución concurrente sin mezclar indentaciones. ## Archivos modificados | Archivo | Cambio | |---|---| | `logger_manager.py` | Clases `TaskLogger`, `TreeFormatter`, `_TaskContext`. `get_logger()` devuelve `TaskLogger`. | | `service.py` | Usa `get_logger()`. `process_file()` y bloques SAP envueltos con `task()`. | | `sapb1_manager.py` | Usa `log.info()/warning()/error()` en lugar de `print()`. | | `sql_manager.py` | Usa `log.error()` en lugar de `print()`. | | `main.py` | Usa `get_logger()` y `task()` en el ciclo principal. | | `mail_sender.py` | Usa `log.info()/warning()` para logging de envío de correos. | ## Ejemplo completo de salida ``` 2026-07-31 10:15:03 ─── 🚀 Servicio ComercioNet - Procesador OC 2026-07-31 10:15:03 ─── 📦 OC98765.xml ├── 📄 XML parseado (orden E001-2439) ├── 💾 JSON guardado en data/output/OC98765.json ├── 💾 HTML generado ├── 🔐 SAP B1 │ ├── Sesión iniciada (B1SESSION: a634d0ec...) │ ├── 🔄 [GET] CardCode por GLN │ ├── Catálogo: 1520 items │ ├── Precios: 980 registros │ ├── ✅ Draft creado → DocEntry 2022 │ └── 💾 Payload guardado └── 📁 XML movido a processed/ ```