Logs y CloudWatch para Lambda
Introducción
Los logs son esenciales para comprender el comportamiento de sus funciones Lambda y diagnosticar problemas. Amazon CloudWatch Logs es el servicio nativo para recolectar, almacenar y analizar estos logs.
Arquitectura de los Logs Lambda
┌─────────────────────────────────────────────────────────────────┐
│ FLUJO DE LOGS LAMBDA │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Lambda │────►│ CloudWatch │────►│ Análisis │ │
│ │ Function │ │ Logs │ │ / Alertas │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ Cada invocación genera: │
│ • START RequestId │
│ • Sus logs (print, console.log) │
│ • END RequestId │
│ • REPORT (duración, memoria, facturación) │
│ │
└─────────────────────────────────────────────────────────────────┘
Estructura de los Log Groups
CloudWatch Logs
│
├── /aws/lambda/ma-fonction-1
│ ├── Log Stream 2024/01/15/[$LATEST]abc123
│ ├── Log Stream 2024/01/15/[$LATEST]def456
│ └── Log Stream 2024/01/16/[$LATEST]ghi789
│
├── /aws/lambda/ma-fonction-2
│ └── ...
│
└── /aws/lambda/ma-fonction-3
└── ...
Conceptos clave:
- Log Group: Un grupo por función Lambda (
/aws/lambda/nombre-función) - Log Stream: Un flujo por instancia de contenedor Lambda
- Log Event: Cada línea individual de log
Formato de los Logs Lambda
Mensaje START
START RequestId: 12345-abcde-67890 Version: $LATEST
Mensajes personalizados (su código)
# Python
print("Tratamiento del usuario:", user_id)
logger.info("Conexión a la base de datos")
logger.error("Error: archivo no encontrado")
// Node.js
console.log("Tratamiento del usuario:", userId);
console.info("Conexión a la base de datos");
console.error("Error: archivo no encontrado");
Mensaje END
END RequestId: 12345-abcde-67890
Mensaje REPORT (¡muy importante!)
REPORT RequestId: 12345-abcde-67890
Duration: 234.56 ms
Billed Duration: 235 ms
Memory Size: 128 MB
Max Memory Used: 67 MB
Init Duration: 145.23 ms
Análisis del REPORT
┌─────────────────────────────────────────────────────────────────┐
│ ANÁLISIS DEL REPORT │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Duration: 234.56 ms │
│ └── Tiempo real de ejecución de su código │
│ │
│ Billed Duration: 235 ms │
│ └── Tiempo facturado (redondeado al ms superior) │
│ │
│ Memory Size: 128 MB │
│ └── Memoria ASIGNADA (configurada) │
│ │
│ Max Memory Used: 67 MB │
│ └── Memoria REALMENTE utilizada │
│ Si está cerca de Memory Size = ¡riesgo de OOM! │
│ │
│ Init Duration: 145.23 ms (solo Cold Start) │
│ └── Tiempo de inicialización del contenedor │
│ │
└─────────────────────────────────────────────────────────────────┘
Buenas Prácticas de Logging
1. Logging estructurado (JSON)
import json
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
# Log estructurado para facilitar el análisis
log_entry = {
"level": "INFO",
"request_id": context.aws_request_id,
"action": "process_order",
"order_id": event.get("order_id"),
"user_id": event.get("user_id")
}
logger.info(json.dumps(log_entry))
2. Niveles de Log
┌─────────────────────────────────────────────────────────────────┐
│ NIVELES DE LOG │
├─────────────────────────────────────────────────────────────────┤
│ │
│ DEBUG → Detalles técnicos (solo desarrollo) │
│ INFO → Operaciones normales │
│ WARNING → Situaciones anómalas pero gestionadas │
│ ERROR → Errores que requieren atención │
│ CRITICAL → Errores graves, función comprometida │
│ │
│ Producción: INFO y superiores │
│ Desarrollo: DEBUG y superiores │
│ │
└─────────────────────────────────────────────────────────────────┘
3. Información esencial a registrar
def lambda_handler(event, context):
# SIEMPRE registrar al inicio
logger.info(f"START - RequestId: {context.aws_request_id}")
logger.info(f"Event: {json.dumps(event)}")
try:
# Su lógica
result = process_data(event)
# Registrar el éxito
logger.info(f"SUCCESS - Result: {result}")
return result
except Exception as e:
# Registrar el error con detalles
logger.error(f"ERROR - Type: {type(e).__name__}")
logger.error(f"ERROR - Message: {str(e)}")
logger.error(f"ERROR - Event: {json.dumps(event)}")
raise
Búsqueda en los Logs
CloudWatch Logs Insights
-- Encontrar todos los errores
fields @timestamp, @message
| filter @message like /ERROR/
| sort @timestamp desc
| limit 100
-- Analizar las duraciones de ejecución
fields @timestamp, @duration
| filter @type = "REPORT"
| stats avg(@duration), max(@duration), min(@duration)
-- Encontrar los Cold Starts
fields @timestamp, @message
| filter @message like /Init Duration/
| sort @timestamp desc
-- Memoria cerca del límite
fields @timestamp, @maxMemoryUsed, @memorySize
| filter @type = "REPORT"
| filter @maxMemoryUsed / @memorySize > 0.8
Filtros de patrones
# Encontrar errores
?ERROR ?Error ?error
# Encontrar un RequestId específico
"12345-abcde-67890"
# Encontrar los timeouts
"Task timed out"
# Encontrar los OOM (Out of Memory)
"Runtime exited" "memory"
Retención de los Logs
┌─────────────────────────────────────────────────────────────────┐
│ RETENCIÓN DE LOS LOGS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Por defecto: Los logs se conservan INDEFINIDAMENTE │
│ └── ¡Atención a los costos de almacenamiento! │
│ │
│ Opciones de retención: │
│ • 1 día, 3 días, 5 días, 1 semana │
│ • 2 semanas, 1 mes, 2 meses, 3 meses │
│ • 6 meses, 1 año, 13 meses, 18 meses │
│ • 2 años, 3 años, 5 años, 10 años │
│ • Nunca expirar │
│ │
│ Recomendación: │
│ • Dev/Test: 1-7 días │
│ • Producción: 30-90 días │
│ • Compliance: según regulación │
│ │
└─────────────────────────────────────────────────────────────────┘
Métricas Importantes
| Métrica | Descripción | Umbral de alerta típico |
|---|---|---|
| Invocations | Número de llamadas | Según línea base |
| Duration | Tiempo de ejecución | > 80% del timeout |
| Errors | Número de errores | > 1% de las invocaciones |
| Throttles | Invocaciones limitadas | > 0 |
| ConcurrentExecutions | Ejecuciones paralelas | > 80% del límite |
Ejercicio Práctico
Escenario: Analizar los logs de una función problemática
Logs observados:
────────────────────────────────────────────────────────
START RequestId: abc-123
Connecting to database...
REPORT Duration: 29543.21 ms Billed: 29544 ms
Memory: 128 MB Max Used: 125 MB
START RequestId: def-456
Connecting to database...
Task timed out after 30.00 seconds
START RequestId: ghi-789
Runtime exited with error: signal: killed
────────────────────────────────────────────────────────
Preguntas:
1. ¿Cuál es el problema principal?
2. ¿Qué métricas son anómalas?
3. ¿Qué soluciones propone?
Ver el análisis
Problemas identificados:
- Timeout: La función tarda casi 30 segundos (límite)
- Memoria: 125/128 MB utilizados (97% - ¡crítico!)
- OOM: "Runtime exited... signal: killed" = Out of Memory
Soluciones:
- Aumentar la memoria (256 MB o más)
- Optimizar el código de conexión a la base de datos (connection pooling)
- Aumentar el timeout si es necesario
- Añadir logs para identificar el cuello de botella
Resumen
- CloudWatch Logs captura automáticamente todos los logs de Lambda
- El mensaje REPORT contiene las métricas de rendimiento
- Utilice logging estructurado (JSON) para facilitar el análisis
- Configure la retención para controlar los costos
- CloudWatch Logs Insights permite consultas SQL sobre los logs
Próxima sección: Quiz de Validación