Skip to main content

Sistema de logging centralizado con retry automático, cache local y reintentos automáticos en background

Project description

🪶 Robin Logger

Robin Logger es una librería Python para enviar eventos de login, auditoría y actividad hacia un backend centralizado mediante una API HTTP.

Versión: 0.2.0

✨ Características

  • 🚀 Envío asíncrono mediante threading (no bloquea tu aplicación)
  • 🔄 Retry automático con backoff exponencial para manejar fallos de red
  • 💾 Cache local con límite de tamaño (30 MB por defecto)
  • 🔁 Reintentos automáticos en background para logs en cache
  • ⏱️ Backoff exponencial inteligente que se ajusta automáticamente
  • 📦 Fácil de usar con una API simple e intuitiva
  • 🔧 Altamente configurable mediante parámetros o variables de entorno
  • 📊 Soporte completo para JSON acepta cualquier estructura de datos

📦 Instalación

pip install robin-logger

🚀 Uso Rápido

Ejemplo básico

from robin_logger import RobinLogger

# Inicializar el logger
logger = RobinLogger(
    base_url="https://api.robinlogs.com/v1/logs",
    api_key="tu_api_key_aqui"
)

# Enviar un log de login exitoso
logger.send_log(
    type="login",
    category="user_auth",
    subcategory="success",
    level="info",
    data={
        "username": "william",
        "ip": "192.168.1.10",
        "timestamp": "2025-10-29T11:23:00Z"
    }
)

# Enviar un log de auditoría
logger.send_log(
    type="audit",
    category="data_access",
    subcategory="read",
    level="info",
    data={
        "user_id": 1234,
        "resource": "users_table",
        "action": "SELECT",
        "affected_rows": 150
    }
)

Usando variables de entorno

import os

# Configurar variables de entorno
os.environ["ROBIN_LOGGER_URL"] = "https://api.robinlogs.com/v1/logs"
os.environ["ROBIN_LOGGER_API_KEY"] = "tu_api_key_aqui"

# Inicializar sin parámetros (usa las variables de entorno)
from robin_logger import RobinLogger

logger = RobinLogger()

logger.send_log(
    type="activity",
    category="user_action",
    subcategory="document_created",
    level="info",
    data={
        "user_id": 789,
        "document_id": "doc-456",
        "title": "Informe Q4 2025"
    }
)

Context Manager

from robin_logger import RobinLogger

with RobinLogger(
    base_url="https://api.robinlogs.com/v1/logs",
    api_key="tu_api_key"
) as logger:
    logger.send_log(
        type="login",
        category="user_auth",
        subcategory="success",
        level="info",
        data={"username": "alice"}
    )
# La conexión se cierra automáticamente

🔧 Configuración

Parámetros del constructor

RobinLogger(
    base_url: str = None,                    # URL del API (o ROBIN_LOGGER_URL)
    api_key: str = None,                     # API Key (o ROBIN_LOGGER_API_KEY)
    timeout: int = 10,                       # Timeout en segundos
    max_retries: int = 3,                    # Número máximo de reintentos
    backoff_factor: float = 0.5,             # Factor de backoff (0.5, 1, 2, 4 segundos...)
    enable_local_cache: bool = True,         # Habilitar cache local
    cache_dir: str = None,                   # Directorio de cache (default: ~/.robin_logger_cache)
    cache_max_size_mb: float = 30.0,         # 🆕 Límite de tamaño del cache en MB
    async_mode: bool = True,                 # Envío asíncrono (True) o síncrono (False)
    auto_retry_enabled: bool = True,         # 🆕 Reintentos automáticos en background
    auto_retry_interval: int = 60,           # 🆕 Intervalo inicial de reintentos (segundos)
    auto_retry_max_interval: int = 3600,     # 🆕 Intervalo máximo de reintentos (segundos)
    auto_retry_async: bool = True            # 🆕 Modo asíncrono para reintentos
)

Variables de entorno

Variable Descripción Requerida
ROBIN_LOGGER_URL URL base del API de logging Sí*
ROBIN_LOGGER_API_KEY API Key para autenticación Sí*

*Si no se proporcionan como parámetros en el constructor

Ejemplo con archivo .env

# .env
ROBIN_LOGGER_URL=https://api.robinlogs.com/v1/logs
ROBIN_LOGGER_API_KEY=sk_live_abc123xyz789
from dotenv import load_dotenv
from robin_logger import RobinLogger

load_dotenv()
logger = RobinLogger()  # Lee las variables automáticamente

📊 Parámetros del método send_log()

logger.send_log(
    type: str,          # Tipo de evento: "login", "audit", "activity", etc.
    category: str,      # Categoría: "user_auth", "data_access", "system", etc.
    subcategory: str,   # Subcategoría: "success", "failure", "read", "write", etc.
    level: str,         # Nivel: "info", "warning", "error", "critical"
    data: dict,         # Cualquier estructura JSON con los datos del evento
    timestamp: str = None  # Timestamp ISO 8601 (opcional, se genera automáticamente)
)

💾 Cache Local

Cuando el envío falla (por ejemplo, sin conexión a internet), los logs se guardan automáticamente en cache local.

🆕 Límite de Tamaño del Cache

El cache tiene un límite configurable (default: 30 MB). Cuando se excede, elimina logs antiguos automáticamente (FIFO).

logger = RobinLogger(
    base_url="...",
    api_key="...",
    cache_max_size_mb=50.0  # Límite de 50 MB
)

🆕 Reintentos Automáticos en Background

El logger reintenta automáticamente enviar logs del cache en segundo plano:

logger = RobinLogger(
    base_url="...",
    api_key="...",
    auto_retry_enabled=True,        # Activar reintentos automáticos
    auto_retry_interval=60,         # Reintentar cada 60 segundos
    auto_retry_max_interval=3600,   # Máximo 1 hora
    auto_retry_async=True           # En background
)

# El sistema automáticamente:
# 1. Reintenta enviar logs cada 60 segundos
# 2. Usa backoff exponencial si falla (60s → 120s → 240s → ...)
# 3. Se resetea a 60s después de éxitos
# 4. No bloquea tu aplicación

Ver estadísticas del cache

stats = logger.get_cache_stats()
print(stats)
# {
#     'enabled': True,
#     'count': 10,
#     'size_mb': 5.2,
#     'max_size_mb': 30.0,
#     'usage_percent': 17.33,
#     'cache_dir': '/Users/user/.robin_logger_cache'
# }

Ver estado de reintentos automáticos

retry_stats = logger.get_retry_stats()
print(retry_stats)
# {
#     'enabled': True,
#     'running': True,
#     'current_interval': 60,
#     'max_interval': 3600,
#     'failures': 0,
#     'async_mode': True
# }

Reintentar logs manualmente

# Reintento manual (útil si desactivas reintentos automáticos)
result = logger.retry_cached_logs()
print(result)
# {'sent': 5, 'failed': 0, 'total': 5}

Detener reintentos automáticos

logger.stop_auto_retry()

# O automáticamente al cerrar
logger.close()

Limpiar cache

logger.clear_cache()

🔄 Sistema de Retry Completo

Robin Logger implementa dos niveles de retry:

1. Retry HTTP Inmediato

Para errores temporales durante el envío:

  • Reintentos: 3 por defecto (configurable)
  • Backoff: 0.5s, 1s, 2s, 4s... (configurable)
  • Códigos HTTP reintentables: 429, 500, 502, 503, 504
logger = RobinLogger(
    base_url="https://api.robinlogs.com/v1/logs",
    api_key="tu_api_key",
    max_retries=5,        # 5 reintentos inmediatos
    backoff_factor=1.0    # 1s, 2s, 4s, 8s, 16s
)

2. 🆕 Reintentos Automáticos en Background

Para logs que van al cache (sin conexión o fallo persistente):

  • Reintentos periódicos: Cada N segundos en background
  • Backoff exponencial inteligente: Se ajusta automáticamente
  • Recuperación automática: Cuando el servidor vuelve
logger = RobinLogger(
    base_url="https://api.robinlogs.com/v1/logs",
    api_key="tu_api_key",
    auto_retry_enabled=True,        # Activar reintentos automáticos
    auto_retry_interval=60,         # Empezar con 60 segundos
    auto_retry_max_interval=3600    # Máximo 1 hora
)

# Secuencia de backoff si el servidor sigue caído:
# Intento 1: 60s
# Intento 2 (fallo): 120s
# Intento 3 (fallo): 240s
# Intento 4 (fallo): 480s
# Intento 5+ (fallo): 3600s (máximo)
# Después de éxito: vuelve a 60s

🌐 Ejemplos de Uso Real

Aplicación Web (Flask)

from flask import Flask, request
from robin_logger import RobinLogger

app = Flask(__name__)
logger = RobinLogger()

@app.route('/login', methods=['POST'])
def login():
    username = request.json.get('username')
    password = request.json.get('password')
    
    # Autenticar usuario...
    if authenticate(username, password):
        logger.send_log(
            type="login",
            category="user_auth",
            subcategory="success",
            level="info",
            data={
                "username": username,
                "ip": request.remote_addr,
                "user_agent": request.headers.get('User-Agent')
            }
        )
        return {"status": "success"}
    else:
        logger.send_log(
            type="login",
            category="user_auth",
            subcategory="failure",
            level="warning",
            data={
                "username": username,
                "ip": request.remote_addr,
                "reason": "invalid_credentials"
            }
        )
        return {"status": "error"}, 401

Script de ETL

from robin_logger import RobinLogger
import pandas as pd

logger = RobinLogger()

def process_data():
    try:
        # Cargar datos
        df = pd.read_csv('data.csv')
        
        logger.send_log(
            type="etl",
            category="data_processing",
            subcategory="start",
            level="info",
            data={
                "rows": len(df),
                "source": "data.csv"
            }
        )
        
        # Procesar...
        result = df.groupby('category').sum()
        
        logger.send_log(
            type="etl",
            category="data_processing",
            subcategory="success",
            level="info",
            data={
                "rows_processed": len(df),
                "output_rows": len(result),
                "duration_seconds": 45.2
            }
        )
        
    except Exception as e:
        logger.send_log(
            type="etl",
            category="data_processing",
            subcategory="error",
            level="error",
            data={
                "error": str(e),
                "traceback": traceback.format_exc()
            }
        )

API REST (FastAPI)

from fastapi import FastAPI, Request
from robin_logger import RobinLogger

app = FastAPI()
logger = RobinLogger()

@app.middleware("http")
async def log_requests(request: Request, call_next):
    # Log entrada de request
    logger.send_log(
        type="api_request",
        category="http",
        subcategory="incoming",
        level="info",
        data={
            "method": request.method,
            "path": request.url.path,
            "client_ip": request.client.host
        }
    )
    
    response = await call_next(request)
    
    # Log respuesta
    logger.send_log(
        type="api_response",
        category="http",
        subcategory="outgoing",
        level="info",
        data={
            "method": request.method,
            "path": request.url.path,
            "status_code": response.status_code
        }
    )
    
    return response

📝 Estructura de datos enviados

El logger envía el siguiente payload al API:

{
  "type": "login",
  "category": "user_auth",
  "subcategory": "success",
  "level": "info",
  "timestamp": "2025-10-31T12:00:00Z",
  "data": {
    "username": "william",
    "ip": "192.168.1.10"
  }
}

Los headers incluyen:

Content-Type: application/json
Authorization: Bearer {api_key}
User-Agent: robin-logger-python/0.1.0

🔀 Modos de Operación

Envío de Logs

Por defecto, los logs se envían de forma asíncrona. Para envío síncrono (bloqueante):

logger = RobinLogger(
    base_url="https://api.robinlogs.com/v1/logs",
    api_key="tu_api_key",
    async_mode=False  # Modo síncrono
)

# Este call bloqueará hasta que se complete
logger.send_log(type="test", category="sync", subcategory="test", level="info", data={})

🆕 Reintentos Automáticos

Los reintentos también pueden ser síncronos o asíncronos:

# Modo asíncrono (recomendado): Reintentos en threads separados
logger = RobinLogger(
    base_url="...",
    api_key="...",
    auto_retry_async=True  # Default: No bloquea
)

# Modo síncrono: Reintentos en el mismo thread de control
logger = RobinLogger(
    base_url="...",
    api_key="...",
    auto_retry_async=False  # Más predecible pero potencialmente más lento
)

🆕 Casos de Uso

Aplicación Móvil con Conectividad Intermitente

logger = RobinLogger(
    base_url="https://api.example.com/logs",
    api_key="mobile_key",
    cache_max_size_mb=10.0,      # Espacio limitado
    auto_retry_interval=120,     # Cada 2 minutos
    auto_retry_max_interval=3600 # Máximo 1 hora
)
# Los logs se guardan cuando no hay red
# Se reenvían automáticamente cuando hay conexión

Servidor de Alta Disponibilidad

logger = RobinLogger(
    base_url="https://api.example.com/logs",
    api_key="server_key",
    cache_max_size_mb=100.0,     # Más capacidad
    auto_retry_interval=30,      # Reintentos rápidos (30s)
    auto_retry_max_interval=600  # Máximo 10 minutos
)
# Recuperación rápida de fallos temporales

Dispositivo IoT con Recursos Limitados

logger = RobinLogger(
    base_url="https://api.example.com/logs",
    api_key="iot_key",
    cache_max_size_mb=5.0,       # Muy limitado
    auto_retry_interval=300,     # Cada 5 minutos
    auto_retry_async=False       # Síncrono para control de recursos
)

Control Manual (Sin Reintentos Automáticos)

logger = RobinLogger(
    base_url="https://api.example.com/logs",
    api_key="manual_key",
    auto_retry_enabled=False     # Desactivar automático
)

# Reintentar manualmente cuando sea necesario
result = logger.retry_cached_logs()
print(f"Enviados: {result['sent']}, Fallidos: {result['failed']}")

### Usando Makefile (comandos útiles)

```bash
make help           # Ver todos los comandos disponibles
make install-dev    # Instalar en modo desarrollo
make test           # Ejecutar tests
make test-cov       # Tests con cobertura
make format         # Formatear código
make build          # Construir paquete
make clean          # Limpiar archivos temporales
make quick-test     # Prueba rápida con httpbin

📦 Instalación y Publicación

Instalar desde código fuente

git clone https://github.com/tu-usuario/robin-logger.git
cd robin-logger
pip install -e .

Publicar en PyPI

# 1. Instalar herramientas
pip install build twine

# 2. Construir el paquete
python -m build

# 3. Subir a PyPI
twine upload dist/*
# Username: __token__
# Password: [tu-token-de-pypi]

📋 Resumen de Funcionalidades

Funcionalidad Descripción Configurable
🚀 Envío Asíncrono No bloquea tu aplicación async_mode
🔄 Retry HTTP Reintentos inmediatos con backoff max_retries, backoff_factor
💾 Cache Local Guarda logs cuando no hay conexión enable_local_cache
📦 Límite de Cache Límite de tamaño con rotación automática cache_max_size_mb
🔁 Reintentos Automáticos Reintenta en background periódicamente auto_retry_enabled
⏱️ Backoff Exponencial Intervalos crecientes inteligentes auto_retry_interval, auto_retry_max_interval
🔀 Modos Async/Sync Configurable para envío y reintentos async_mode, auto_retry_async
🌍 Variables de Entorno Configuración desde env vars ROBIN_LOGGER_URL, ROBIN_LOGGER_API_KEY
📊 JSON Completo Cualquier estructura en data

🤝 Contribuir

Las contribuciones son bienvenidas! Por favor:

  1. Fork el repositorio
  2. Crea una rama para tu feature (git checkout -b feature/AmazingFeature)
  3. Commit tus cambios (git commit -m 'Add some AmazingFeature')
  4. Push a la rama (git push origin feature/AmazingFeature)
  5. Abre un Pull Request

📄 Licencia

Este proyecto está licenciado bajo la Licencia MIT - ver el archivo LICENSE para más detalles.

🆕 Novedades v0.2.0

  • Límite de tamaño del cache (30 MB default, configurable)
  • Rotación automática de logs antiguos cuando se excede el límite
  • Reintentos automáticos en background con thread dedicado
  • Backoff exponencial inteligente para reintentos automáticos
  • Modos asíncrono/síncrono para reintentos
  • Estadísticas mejoradas con tamaño de cache y estado de reintentos
  • Recuperación automática cuando el servidor vuelve a estar disponible

🔗 Enlaces

📧 Soporte

Para preguntas o soporte, por favor abre un issue en GitHub.

📄 Licencia

Este proyecto está licenciado bajo la Licencia MIT - ver el archivo LICENSE para más detalles.


Robin Logger v0.2.0 - Sistema de logging centralizado con reintentos automáticos y cache inteligente
Hecho con ❤️ por Diego

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

robin_logger-0.2.1.tar.gz (19.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

robin_logger-0.2.1-py3-none-any.whl (14.5 kB view details)

Uploaded Python 3

File details

Details for the file robin_logger-0.2.1.tar.gz.

File metadata

  • Download URL: robin_logger-0.2.1.tar.gz
  • Upload date:
  • Size: 19.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for robin_logger-0.2.1.tar.gz
Algorithm Hash digest
SHA256 52066a478bf634dfd39286612f60dba7dfd921c6e49b12f3349e01d5372674a2
MD5 d4b3ff57e41d1d593605b4ff74d22dc0
BLAKE2b-256 903d0bb1ffac3fde647349e5aaaf9d0a21849e76d16467baf6b5deb75874adfc

See more details on using hashes here.

File details

Details for the file robin_logger-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: robin_logger-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 14.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for robin_logger-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 88566146c6a7026cbc0042dadb51847d2d0334350963b74c75bd3928bc367c0b
MD5 a25f551d6f1d9e751af95050ca448e81
BLAKE2b-256 a2b081d2b2e602eed3286ce5d0b9ebae371e63974e9ca7caf932dc77576ef16b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page