Gabriel Ramos
feat: Docling Document Processor - Gradio + ZeroGPU
780413d
"""
Sistema de logging para o Docling Document Processor.
Este módulo configura e gerencia o sistema de logging da aplicação,
incluindo rotação de arquivos e formatação consistente.
"""
import logging
import sys
from logging.handlers import RotatingFileHandler
from pathlib import Path
from typing import Optional
import config
# Flag para evitar configuração duplicada
_logging_configured = False
# Cache de loggers
_loggers: dict[str, logging.Logger] = {}
def setup_logger(
name: str = "docling_space",
level: int = logging.INFO,
log_to_file: bool = True,
log_to_console: bool = True
) -> logging.Logger:
"""
Configura e retorna um logger.
Args:
name: Nome do logger.
level: Nível de logging (default: INFO).
log_to_file: Se deve logar em arquivo.
log_to_console: Se deve logar no console.
Returns:
Logger configurado.
"""
global _logging_configured
# Se já existe no cache, retorna
if name in _loggers:
return _loggers[name]
# Cria o logger
logger = logging.getLogger(name)
logger.setLevel(level)
# Evita handlers duplicados
if logger.handlers:
return logger
# Formatter
formatter = logging.Formatter(
config.LOG_FORMAT,
datefmt=config.LOG_DATE_FORMAT
)
# Handler de console
if log_to_console:
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(level)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
# Handler de arquivo com rotação
if log_to_file:
try:
# Garante que o diretório existe
config.LOGS_DIR.mkdir(parents=True, exist_ok=True)
log_file = config.LOGS_DIR / config.LOG_FILE
file_handler = RotatingFileHandler(
log_file,
maxBytes=config.LOG_MAX_BYTES,
backupCount=config.LOG_BACKUP_COUNT,
encoding="utf-8"
)
file_handler.setLevel(level)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
except Exception as e:
# Se não conseguir criar o arquivo de log, continua só com console
if log_to_console:
logger.warning(f"Não foi possível criar arquivo de log: {e}")
# Não propaga para o root logger
logger.propagate = False
# Adiciona ao cache
_loggers[name] = logger
_logging_configured = True
return logger
def get_logger(name: Optional[str] = None) -> logging.Logger:
"""
Obtém um logger pelo nome.
Se o logger não existir, cria um novo com as configurações padrão.
Args:
name: Nome do logger. Se None, usa "docling_space".
Returns:
Logger configurado.
"""
if name is None:
name = "docling_space"
# Se for um nome de módulo completo, usa apenas a última parte
if "." in name:
short_name = name.split(".")[-1]
else:
short_name = name
logger_name = f"docling_space.{short_name}"
if logger_name not in _loggers:
return setup_logger(logger_name)
return _loggers[logger_name]
def log_exception(
logger: logging.Logger,
message: str,
exc: Exception,
include_traceback: bool = True
) -> None:
"""
Loga uma exceção com detalhes.
Args:
logger: Logger a usar.
message: Mensagem descritiva.
exc: Exceção a logar.
include_traceback: Se deve incluir traceback completo.
"""
if include_traceback:
logger.exception(f"{message}: {exc}")
else:
logger.error(f"{message}: {type(exc).__name__}: {exc}")
def log_processing_start(
logger: logging.Logger,
filename: str,
file_size: int
) -> None:
"""
Loga o início do processamento de um arquivo.
Args:
logger: Logger a usar.
filename: Nome do arquivo.
file_size: Tamanho em bytes.
"""
size_mb = file_size / (1024 * 1024)
logger.info(f"Iniciando processamento: {filename} ({size_mb:.2f} MB)")
def log_processing_complete(
logger: logging.Logger,
filename: str,
duration_seconds: float,
output_format: str
) -> None:
"""
Loga a conclusão do processamento de um arquivo.
Args:
logger: Logger a usar.
filename: Nome do arquivo.
duration_seconds: Tempo de processamento em segundos.
output_format: Formato de saída usado.
"""
logger.info(
f"Processamento concluído: {filename} "
f"({duration_seconds:.2f}s, formato: {output_format})"
)
def log_validation_error(
logger: logging.Logger,
filename: str,
error_code: str,
message: str
) -> None:
"""
Loga um erro de validação.
Args:
logger: Logger a usar.
filename: Nome do arquivo.
error_code: Código do erro.
message: Mensagem de erro.
"""
logger.warning(f"Validação falhou [{error_code}] {filename}: {message}")
class ProcessingLogger:
"""
Context manager para logging de processamento.
Automaticamente loga início e fim do processamento com timing.
"""
def __init__(
self,
logger: logging.Logger,
operation: str,
filename: str
):
self.logger = logger
self.operation = operation
self.filename = filename
self.start_time: float = 0
def __enter__(self):
import time
self.start_time = time.time()
self.logger.info(f"[INÍCIO] {self.operation}: {self.filename}")
return self
def __exit__(self, exc_type, exc_val, exc_tb):
import time
duration = time.time() - self.start_time
if exc_type is None:
self.logger.info(
f"[FIM] {self.operation}: {self.filename} ({duration:.2f}s)"
)
else:
self.logger.error(
f"[ERRO] {self.operation}: {self.filename} "
f"({duration:.2f}s) - {exc_type.__name__}: {exc_val}"
)
# Não suprime exceções
return False