Sistema de Logging Centralizado¶
Arquitectura¶
SherlockDocs (logger raíz)
├── handlers: FileHandler, StreamHandler, DatabaseHandler
└── hijos (heredan handlers automáticamente):
├── SherlockDocs.sherlock_docs.application.use_cases.process_document
├── SherlockDocs.sherlock_docs.infrastructure.dedup.hybrid_detector
└── ... (cualquier módulo usando get_logger(__name__))
Configuración Inicial (app.py)¶
from sherlock_docs.infrastructure.logging import setup_logger
logger = setup_logger(
name="SherlockDocs",
log_dir="logs",
db_path="data/database/sherlock.db",
)
Uso en Módulos (OBLIGATORIO)¶
# ✅ CORRECTO
from sherlock_docs.infrastructure.logging import get_logger
logger = get_logger(__name__) # Hereda handlers del logger raíz
# ❌ INCORRECTO — logs no aparecerán
import logging
logger = logging.getLogger(__name__)
| Método | Logger creado | ¿Hereda handlers? |
|---|---|---|
get_logger(__name__) |
SherlockDocs.sherlock_docs.modulo |
✅ Sí |
logging.getLogger(__name__) |
sherlock_docs.modulo |
❌ No |
Qué loguear por componente¶
| Componente | Qué loguear | Nivel |
|---|---|---|
| OCR adapters | Inicio, éxito, error con exc_info | INFO/ERROR |
| NER adapters | Extracción inicio/fin, errores | INFO/ERROR |
| Duplicate detection | Candidatos encontrados, tiempos | INFO/DEBUG |
| Repository ops | Save, update, delete | INFO |
| Pipeline use case | Cada etapa inicio/fin | INFO |
| GUI pages | Errores capturados | WARNING/ERROR |
Patrón obligatorio¶
from sherlock_docs.infrastructure.logging import get_logger
logger = get_logger(__name__)
class SomeAdapter:
def process(self, file_path: Path) -> Result:
logger.debug(f"Iniciando: {file_path}")
try:
# ... lógica ...
logger.info(f"Completado: tiempo={processing_time:.0f}ms")
return result
except Exception as e:
logger.error(f"Falló: {e}", exc_info=True)
return Failure(error)
Patrón para clases ABC base¶
En clases abstractas (BaseExtractor, BaseValidator, BaseOCRAdapter), usar
type(self).__module__ para que el logger se resuelva al módulo concreto:
# ✅ CORRECTO — logger auto-resuelve al módulo de la subclase concreta
class BaseExtractor(ABC):
def __init__(self) -> None:
self._logger = get_logger(type(self).__module__)
# MarkerExtractor hereda BaseExtractor → self._logger apunta a
# "SherlockDocs.sherlock_docs.infrastructure.ner.extractors.marker_extractor"
# ❌ INCORRECTO — todos los extractores logueaban bajo "base"
class BaseExtractor(ABC):
def __init__(self, extractor_logger: logging.Logger | None = None) -> None:
self._logger = extractor_logger or get_logger(__name__)
Nunca usar except: pass silencioso. Siempre logger.error(msg, exc_info=True).
Ubicación¶
- Archivo:
logs/sherlock_YYYY-MM-DD.log(rotación diaria, 30 días) - Base de datos:
data/database/sherlock.dbtablalogs