Saltar al contenido principal

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étricaDescripciónUmbral de alerta típico
InvocationsNúmero de llamadasSegún línea base
DurationTiempo de ejecución> 80% del timeout
ErrorsNúmero de errores> 1% de las invocaciones
ThrottlesInvocaciones limitadas> 0
ConcurrentExecutionsEjecuciones 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:

  1. Timeout: La función tarda casi 30 segundos (límite)
  2. Memoria: 125/128 MB utilizados (97% - ¡crítico!)
  3. OOM: "Runtime exited... signal: killed" = Out of Memory

Soluciones:

  1. Aumentar la memoria (256 MB o más)
  2. Optimizar el código de conexión a la base de datos (connection pooling)
  3. Aumentar el timeout si es necesario
  4. 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