tai-alphi 2.0.1
Logs y monitorización de recursos
License: MIT
Requires Python: <4.0,>=3.10
Keywords: logs, cloud, monitoring, logging, teams, nosql, logtail

# TAI-Alphi
*Librería de logs y monitorización de procesos*

## ¿Para qué sirve?
**TAI-Alphi** es una librería de logging avanzada que te permite:
- 📝 Escribir logs una vez y enviarlos a múltiples destinos
- ⚙️ Configurar múltiples loggers con diferentes comportamientos
- 🔄 Reconfigurar loggers dinámicamente sin reiniciar la aplicación
- 🎯 Personalizar niveles, formatos y destinos por logger
- 🏗️ Patrón Singleton para gestión centralizada

## Instalación

**Poetry**
```bash
poetry add tai-alphi
```

**Pip**
```bash
pip install tai-alphi
```

### Dependencias

**Requeridas** (se instalan automáticamente):
- `pydantic ^2.11`

**Opcionales** (instalar según necesidad):
- `pymongo` - Para enviar logs a MongoDB/CosmosDB
- `logtail` - Para enviar logs a BetterStack

## Uso Básico

### Inicio Rápido

```python
from tai_alphi import Alphi

# Crear instancia (Singleton)
bot = Alphi()

# Obtener logger
logger = bot.get_logger()

# Usar logger
logger.debug('Mensaje de depuración')
logger.info('Mensaje informativo')
logger.warning('Mensaje de advertencia')
logger.error('Mensaje de error')
logger.critical('Mensaje crítico')
```

### Con Configuración Inicial

```python
from tai_alphi import Alphi

# Configurar al inicio
config = {
    'app': {
        'consola': {
            'enabled': True,
            'log_level': 'INFO',
            'display_info': ['asctime', 'levelname'],
            'time_format': '%H:%M:%S'
        }
    }
}

# Crear instancia con configuración
alphi = Alphi(config)

# Obtener logger configurado
logger = alphi.get_logger('app')

logger.info('Aplicación iniciada')
```

## Destinos (Handlers)

TAI-Alphi puede enrutar logs hacia 4 destinos diferentes:

| Destino | Descripción | Configuración Requerida |
|---------|-------------|------------------------|
| 🖥️ **Consola** | Salida estándar (terminal) | Ninguna (por defecto) |
| 💬 **Teams** | Microsoft Teams | Webhook URL |
| 🗄️ **NoSQL** | MongoDB / CosmosDB | Credenciales de conexión |
| 📊 **LogTail** | BetterStack | Token de acceso |

Cada destino se puede configurar individualmente con diferentes niveles de log, formatos y filtros.

## Configuración

### Estructura de Configuración

La configuración se define como un diccionario Python con la siguiente estructura:

```python
config = {
    'nombre_logger': {
        'consola': {
            'enabled': bool,           # Activar/desactivar
            'log_level': str,          # DEBUG | INFO | WARN | ERROR | CRIT
            'display_info': list[str], # Campos a mostrar
            'time_format': str         # Formato de timestamp
        },
        'teams': {
            'enabled': bool,
            'log_level': str,
            'display_info': list[str],
            'time_format': str,
            'project': str,            # Nombre del proyecto
            'pipeline': str,           # Nombre del pipeline
            'notifications': list[str] # Emails a notificar
        },
        'nosql': {
            'enabled': bool,
            'log_level': str,
            'display_info': list[str],
            'time_format': str,
            'expiration': float        # Días hasta expiración (opcional)
        },
        'logtail': {
            'enabled': bool,
            'log_level': str
        }
    }
}
```

### Parámetros de Configuración

#### Niveles de Log (`log_level`)
- `'DEBUG'` - Información detallada para diagnóstico
- `'INFO'` - Eventos informativos generales
- `'WARN'` - Advertencias, requieren atención
- `'ERROR'` - Errores que afectan funcionalidad
- `'CRIT'` - Errores críticos que requieren acción inmediata

#### Información a Mostrar (`display_info`)
Opciones disponibles:
- `'asctime'` - Timestamp del log
- `'filename'` - Nombre del archivo fuente
- `'funcName'` - Nombre de la función
- `'levelname'` - Nivel del log
- `'lineno'` - Número de línea
- `'module'` - Nombre del módulo
- `'pathname'` - Ruta completa del archivo

#### Formato de Tiempo (`time_format`)
Usa formato estándar de Python `strftime`. Ejemplos:
- `'%H:%M:%S'` - 14:30:45
- `'%Y-%m-%d %H:%M:%S'` - 2025-11-18 14:30:45
- `'%d/%m/%Y %H:%M'` - 18/11/2025 14:30

[Documentación completa de strftime](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior)

### Configuración de Credenciales

Para usar Teams, NoSQL o LogTail, configura las credenciales:

```python
from tai_alphi import Alphi
import os

# Configurar al inicio de la aplicación
Alphi(config)

# Configurar Teams (método de clase)
Alphi.set_teams(webhook=os.getenv('TEAMS_WEBHOOK'))

# Configurar MongoDB/CosmosDB (método de clase)
Alphi.set_nosql(
    user=os.getenv('DB_USER'),
    pwd=os.getenv('DB_PASSWORD'),
    host=os.getenv('DB_HOST', 'localhost'),
    port=int(os.getenv('DB_PORT', '27017')),
    db_name='logs_db',
    collection_name='application_logs'
)

# Configurar LogTail (método de clase)
Alphi.set_logtail(token=os.getenv('LOGTAIL_TOKEN'))
```

> [!TIP]
> Usa variables de entorno para las credenciales por seguridad


## Uso Avanzado

### Múltiples Loggers

Puedes configurar y usar múltiples loggers con diferentes comportamientos:

```python
from tai_alphi import Alphi

config = {
    'app': {
        'consola': {
            'enabled': True,
            'log_level': 'INFO',
            'display_info': ['asctime', 'levelname']
        }
    },
    'database': {
        'consola': {
            'enabled': True,
            'log_level': 'DEBUG',
            'display_info': ['asctime', 'levelname', 'module']
        },
        'nosql': {
            'enabled': True,
            'log_level': 'WARN'
        }
    },
    'api': {
        'consola': {
            'enabled': True,
            'log_level': 'WARN'
        },
        'teams': {
            'enabled': True,
            'log_level': 'ERROR',
            'project': 'Mi API',
            'pipeline': 'Producción'
        }
    }
}

alphi = Alphi(config)

# Configurar credenciales (métodos de clase)
Alphi.set_nosql(user='...', pwd='...', host='...', port=27017, 
                db_name='logs', collection_name='logs')
Alphi.set_teams(webhook='https://...')

# Obtener y usar cada logger
app_logger = alphi.get_logger('app')
db_logger = alphi.get_logger('database')
api_logger = alphi.get_logger('api')

app_logger.info('Aplicación iniciada')        # Solo consola
db_logger.debug('Query ejecutado')            # Solo consola
db_logger.warning('Pool al 90%')              # Consola + NoSQL
api_logger.error('Timeout en API externa')    # Consola + Teams
```

### Acceso Directo con Método de Clase

```python
from tai_alphi import Alphi

# Configurar una vez
Alphi(config)

# Usar en cualquier módulo sin instanciar
logger = Alphi.get_logger_by_name('app')
logger.info('Acceso directo al logger')
```

### Configuración Dinámica

#### Configurar Antes de Crear

```python
from tai_alphi import Alphi

alphi = Alphi()

# Configurar logger antes de usarlo
Alphi.configure_logger('email-service', {
    'consola': {
        'enabled': True,
        'log_level': 'DEBUG',
        'display_info': ['asctime', 'levelname', 'funcName']
    }
})

# Crear logger (usa la configuración preestablecida)
email_logger = alphi.get_logger('email-service')
email_logger.debug('Conectando a servidor SMTP')
```

#### Reconfigurar Logger Existente

```python
from tai_alphi import Alphi

alphi = Alphi()

# Crear logger con configuración por defecto
logger = alphi.get_logger('notifications')
logger.info('Notificación enviada')

# Reconfigurar en caliente
Alphi.configure_logger('notifications', {
    'consola': {
        'enabled': True,
        'log_level': 'DEBUG',
        'display_info': ['asctime', 'levelname', 'module', 'funcName']
    }
})

# El mismo logger ahora usa la nueva configuración
logger.debug('Este mensaje ahora aparece!')
```

> [!IMPORTANT]
> La reconfiguración es automática. No necesitas obtener el logger nuevamente.

#### Configurar Múltiples Loggers Simultáneamente

```python
from tai_alphi import Alphi

configs = {
    'auth': {
        'consola': {'enabled': True, 'log_level': 'INFO'}
    },
    'payment': {
        'consola': {'enabled': True, 'log_level': 'WARN'}
    },
    'analytics': {
        'consola': {'enabled': True, 'log_level': 'ERROR'}
    }
}

Alphi.configure_multiple_loggers(configs)

# Usar los loggers
auth = Alphi.get_logger_by_name('auth')
payment = Alphi.get_logger_by_name('payment')
analytics = Alphi.get_logger_by_name('analytics')
```

### Sobreescritura de Configuración Global

Puedes reemplazar completamente la configuración de Alphi:

```python
from tai_alphi import Alphi

# Configuración inicial
config1 = {
    'service-a': {'consola': {'enabled': True, 'log_level': 'INFO'}},
    'service-b': {'consola': {'enabled': True, 'log_level': 'DEBUG'}}
}

alphi = Alphi(config1)
logger = alphi.get_logger('service-a')

# Sobreescribir con nueva configuración
config2 = {
    'service-x': {'consola': {'enabled': True, 'log_level': 'WARN'}},
    'service-y': {'consola': {'enabled': True, 'log_level': 'ERROR'}}
}

alphi = Alphi(config2)  # Misma instancia Singleton, nueva configuración

# Los loggers anteriores ya no existen en la configuración
# Los nuevos loggers usan la configuración actualizada
logger_x = alphi.get_logger('service-x')
```

### Cambiar Colección NoSQL por Logger

```python
from tai_alphi import Alphi

Alphi(config)
Alphi.set_nosql(user='...', pwd='...', host='...', port=27017,
                db_name='logs', collection_name='default_logs')

# Logger con colección por defecto
logger1 = Alphi.get_logger_by_name('service1')
logger1.info('Va a default_logs')

# Logger con colección específica
logger2 = Alphi.get_logger_by_name('service2')
logger2.set_nosql_collection(collection_name='service2_logs')
logger2.info('Va a service2_logs')
```

### Cambiar Webhook de Teams por Logger

```python
from tai_alphi import Alphi

Alphi(config)

# Logger con webhook personalizado
logger = Alphi.get_logger_by_name('critical-service')
logger.set_teams_webhook(webhook='https://teams.webhook.especifico')
logger.error('Este error va a un canal específico de Teams')
```

## Inspección y Gestión

### Listar Loggers Configurados

```python
from tai_alphi import Alphi

alphi = Alphi.get_instance()

# Ver todos los loggers y su origen
configured = alphi.list_configured_loggers()
for name, source in configured.items():
    print(f"{name}: {source}")
# Salida:
# app: config
# database: dynamic
# api: config

# Ver nombres disponibles
print(alphi.get_available_logger_names())
# ['tai-logger', 'app', 'database', 'api']

# Verificar si existe configuración específica
print(alphi.has_logger_config('app'))  # True
print(alphi.has_logger_config('inexistente'))  # False
```

### Obtener Configuración de un Logger

```python
from tai_alphi import Alphi

alphi = Alphi.get_instance()

# Obtener configuración completa
config = alphi.get_logger_config('app')

print(f"Consola habilitada: {config.consola.enabled}")
print(f"Nivel de log: {config.consola.log_level}")
print(f"Display info: {config.consola.display_info}")
print(f"Time format: {config.consola.time_format}")
```

### Remover Configuración Dinámica

```python
from tai_alphi import Alphi

alphi = Alphi.get_instance()

# Configurar dinámicamente
Alphi.configure_logger('temp', {
    'consola': {'enabled': True, 'log_level': 'DEBUG'}
})

logger = alphi.get_logger('temp')
logger.debug('Mensaje con DEBUG')

# Remover configuración dinámica (vuelve a la por defecto)
alphi.remove_logger_config('temp')

logger.info('Ahora usa configuración por defecto')
```

### Ver Loggers en Caché

```python
from tai_alphi import Alphi

alphi = Alphi.get_instance()

# Ver todos los loggers creados
for name, logger in alphi.loggers.items():
    print(f"{name}: {logger}")
```

## Patrón Singleton

Alphi implementa el patrón Singleton, garantizando una única instancia en toda la aplicación:

```python
from tai_alphi import Alphi

alphi1 = Alphi()
alphi2 = Alphi()
alphi3 = Alphi.get_instance()

# Todas son la misma instancia
assert alphi1 is alphi2 is alphi3  # True

# Para tests o reinicios, puedes resetear
Alphi.reset_instance()
```

## Valores por Defecto

Si no se especifica configuración, cada handler usa estos valores:

### Consola
```python
{
    'enabled': True,
    'log_level': 'INFO',
    'display_info': ['asctime', 'levelname'],
    'time_format': '%H:%M:%S'
}
```

### Teams
```python
{
    'enabled': False,
    'log_level': 'INFO',
    'display_info': ['asctime', 'levelname'],
    'time_format': '%H:%M:%S',
    'project': 'Project Name',
    'pipeline': 'Pipeline Name',
    'notifications': []
}
```

### NoSQL
```python
{
    'enabled': False,
    'log_level': 'INFO',
    'display_info': ['asctime', 'levelname'],
    'time_format': '%Y-%m-%d %H:%M:%S',
    'expiration': None  # Sin expiración
}
```

### LogTail
```python
{
    'enabled': False,
    'log_level': 'INFO'
}
```

## Mejores Prácticas

1. **Un logger por componente**: Crea loggers específicos para cada módulo importante de tu aplicación
   ```python
   app_logger = Alphi.get_logger_by_name('app')
   db_logger = Alphi.get_logger_by_name('database')
   api_logger = Alphi.get_logger_by_name('api')
   ```

2. **Niveles apropiados**: Usa `DEBUG` en desarrollo, `INFO` o superior en producción
   ```python
   import os
   
   log_level = 'DEBUG' if os.getenv('ENV') == 'dev' else 'INFO'
   config = {'app': {'consola': {'log_level': log_level}}}
   ```

3. **Variables de entorno**: Almacena credenciales de forma segura
   ```python
   Alphi.set_teams(webhook=os.getenv('TEAMS_WEBHOOK'))
   Alphi.set_nosql(
       user=os.getenv('DB_USER'),
       pwd=os.getenv('DB_PASSWORD'),
       # ...
   )
   ```

4. **Configuración dinámica**: Ajusta comportamiento sin reiniciar
   ```python
   # Activar debug temporalmente para investigar
   Alphi.configure_logger('app', {
       'consola': {'log_level': 'DEBUG'}
   })
   ```

5. **Acceso directo**: Usa `get_logger_by_name()` para acceso rápido
   ```python
   # En cualquier módulo
   logger = Alphi.get_logger_by_name('app')
   logger.info('Mensaje')
   ```

## Ejemplos Completos

### Aplicación Web con Múltiples Componentes

```python
from tai_alphi import Alphi
import os

# Configuración al inicio
config = {
    'web': {
        'consola': {'enabled': True, 'log_level': 'INFO'},
        'teams': {'enabled': True, 'log_level': 'ERROR', 
                  'project': 'Web App', 'pipeline': 'Production'}
    },
    'db': {
        'consola': {'enabled': True, 'log_level': 'WARN'},
        'nosql': {'enabled': True, 'log_level': 'ERROR', 'expiration': 90.0}
    },
    'api': {
        'consola': {'enabled': True, 'log_level': 'INFO'},
        'teams': {'enabled': True, 'log_level': 'CRIT',
                  'notifications': ['admin@example.com']}
    }
}

# Inicializar
Alphi(config)
Alphi.set_teams(webhook=os.getenv('TEAMS_WEBHOOK'))
Alphi.set_nosql(
    user=os.getenv('DB_USER'),
    pwd=os.getenv('DB_PASSWORD'),
    host=os.getenv('DB_HOST'),
    port=int(os.getenv('DB_PORT', '27017')),
    db_name='app_logs',
    collection_name='production_logs'
)

# Usar en diferentes módulos
web_logger = Alphi.get_logger_by_name('web')
db_logger = Alphi.get_logger_by_name('db')
api_logger = Alphi.get_logger_by_name('api')

# Logs de diferentes componentes
web_logger.info('Servidor web iniciado en puerto 8000')
db_logger.warning('Pool de conexiones al 85%')
api_logger.critical('API externa no responde - sistema degradado')
```

### Desarrollo vs Producción

```python
from tai_alphi import Alphi
import os

ENV = os.getenv('ENV', 'dev')

if ENV == 'dev':
    # Desarrollo: Solo consola, nivel DEBUG
    config = {
        'app': {
            'consola': {
                'enabled': True,
                'log_level': 'DEBUG',
                'display_info': ['asctime', 'levelname', 'funcName', 'lineno']
            }
        }
    }
else:
    # Producción: Consola + Teams + NoSQL
    config = {
        'app': {
            'consola': {
                'enabled': True,
                'log_level': 'INFO',
                'display_info': ['asctime', 'levelname']
            },
            'teams': {
                'enabled': True,
                'log_level': 'ERROR',
                'project': 'Production App',
                'notifications': ['team@example.com']
            },
            'nosql': {
                'enabled': True,
                'log_level': 'WARN',
                'expiration': 30.0
            }
        }
    }

Alphi(config)

if ENV != 'dev':
    Alphi.set_teams(webhook=os.getenv('TEAMS_WEBHOOK'))
    Alphi.set_nosql(
        user=os.getenv('DB_USER'),
        pwd=os.getenv('DB_PASSWORD'),
        host=os.getenv('DB_HOST'),
        port=int(os.getenv('DB_PORT')),
        db_name='logs',
        collection_name='app_logs'
    )

logger = Alphi.get_logger_by_name('app')
```

## API Reference

### Clase `Alphi`

#### Constructor
```python
Alphi(settings: dict | None = None)
```
Crea o retorna la instancia Singleton de Alphi.

**Parámetros:**
- `settings` (dict | None): Configuración de loggers

#### Métodos de Instancia

**`get_logger(logger_name: str = None, dev: bool = False, exec_info: bool = False) -> LoggerFactory`**

Obtiene o crea un logger.

**Parámetros:**
- `logger_name` (str): Nombre del logger. Si es `None`, usa el primero configurado o el default
- `dev` (bool): Si es `True`, desactiva todos los handlers excepto consola
- `exec_info` (bool): Si es `True`, añade traceback a logs ERROR y CRITICAL

**Retorna:** Instancia de `LoggerFactory`

---

#### Métodos de Clase para Configuración

**`set_teams(webhook: str) -> str`**

Método de clase para configurar el webhook de Microsoft Teams.

**Parámetros:**
- `webhook` (str): URL del webhook de Teams

**Retorna:** El webhook configurado

**Ejemplo:**
```python
Alphi.set_teams(webhook='https://outlook.office.com/webhook/...')
```

---

**`set_nosql(user: str, pwd: str, host: str, port: int, db_name: str, collection_name: str) -> CosmosDB`**

Método de clase para configurar la conexión a MongoDB/CosmosDB.

**Parámetros:**
- `user` (str): Usuario de la base de datos
- `pwd` (str): Contraseña
- `host` (str): Host del servidor
- `port` (int): Puerto del servidor
- `db_name` (str): Nombre de la base de datos
- `collection_name` (str): Nombre de la colección

**Retorna:** Instancia de `CosmosDB`

**Ejemplo:**
```python
Alphi.set_nosql(
    user='admin',
    pwd='password',
    host='localhost',
    port=27017,
    db_name='logs_db',
    collection_name='app_logs'
)
```

---

**`set_logtail(token: str) -> str`**

Método de clase para configurar el token de LogTail.

**Parámetros:**
- `token` (str): Token de acceso de LogTail

**Retorna:** El token configurado

**Ejemplo:**
```python
Alphi.set_logtail(token='your_logtail_token_here')
```

---

**`has_logger_config(logger_name: str) -> bool`**

Verifica si existe configuración específica para un logger.

---

**`get_available_logger_names() -> list[str]`**

Retorna todos los nombres de loggers configurados.

---

**`get_logger_config(logger_name: str) -> LoggerConfig`**

Obtiene la configuración de un logger específico.

---

**`list_configured_loggers() -> dict[str, str]`**

Lista todos los loggers con su origen de configuración ('config' o 'dynamic').

---

**`add_logger_config(logger_name: str, config: dict) -> None`**

Añade o actualiza configuración dinámica de un logger.

---

**`add_multiple_logger_configs(configs: dict[str, dict]) -> None`**

Añade o actualiza múltiples configuraciones dinámicas.

---

**`remove_logger_config(logger_name: str) -> None`**

Elimina configuración dinámica de un logger (vuelve a usar la por defecto).

#### Métodos de Clase

**`get_instance(settings: dict | None = None) -> Alphi`**

Obtiene la instancia Singleton. Si no existe, la crea. Si se proporciona `settings`, sobreescribe la configuración.

---

**`get_logger_by_name(logger_name: str = None, dev: bool = False, exec_info: bool = False) -> LoggerFactory`**

Obtiene un logger sin necesidad de instanciar Alphi explícitamente.

---

**`configure_logger(logger_name: str, config: dict) -> None`**

Configura un logger dinámicamente (método de clase).

---

**`configure_multiple_loggers(configs: dict[str, dict]) -> None`**

Configura múltiples loggers dinámicamente (método de clase).

---

**`reset_instance() -> None`**

Resetea la instancia Singleton (útil para testing).

### Clase `LoggerFactory`

Extiende `logging.Logger` con funcionalidad adicional.

#### Métodos Adicionales

**`reconfigure(new_config: LoggerConfig, nosql: CosmosDB = None, logtail_token: str = None, teams_webhook: str = None) -> None`**

Reconfigura el logger sin crear una nueva instancia.

---

**`set_nosql_collection(db_name: str = None, collection_name: str = None) -> None`**

Cambia la colección de NoSQL para este logger específico.

---

**`set_teams_webhook(webhook: str) -> None`**

Cambia el webhook de Teams para este logger específico.

## Tests

Ejecuta el test completo para ver todas las funcionalidades en acción:

```bash
python tests/test_completo.py
```

El test demuestra:
- Patrón Singleton
- Configuración inicial y dinámica
- Múltiples loggers y handlers
- Reconfiguración en caliente
- Inspección de configuraciones
- Sobreescritura global
- Y mucho más...

## Licencia

MIT

## Contribuciones

Las contribuciones son bienvenidas. Por favor, abre un issue o pull request en el repositorio oficial.

## Soporte

Para reportar bugs o solicitar nuevas funcionalidades, visita:
https://github.com/triplealpha-innovation/tai-alphi/issues