La importancia del logging
Ganando visibilidad en tu Código
El logging es una de las herramientas más simples y poderosas que tenemos para monitorear y solucionar problemas en software. Sin embargo, con demasiada frecuencia, nos encontramos con sistemas críticos con poco o ningún logging, lo que conduce a una total falta de visibilidad cuando ocurren incidentes.
En este artículo, quiero enfatizar la importancia del logging y compartir algunas técnicas prácticas para introducir logging en tu código de manera limpia y efectiva. Ya sea que estés trabajando en un sistema de comercio electrónico o en cualquier otro tipo de software, tener logs adecuados te ahorrará horas de investigación durante postmortems y hará que la resolución de incidentes sea mucho más fluida.
Por Qué Necesitas Hacer Logging: Un Escenario del Mundo Real
Imagina que estás investigando un incidente: los pedidos están fallando, el almacén está paralizado y no tienes idea de qué ha salido mal. Profundizas en el código y te das cuenta de que no hay ni un solo log para guiarte. Esencialmente estás ciego.
Los postmortems en estas situaciones generalmente llegan a la misma conclusión: “Deberíamos haber agregado logs aquí.” Pero para entonces, ya es demasiado tarde. El incidente ya ha causado daños: pérdida de ingresos, usuarios frustrados y clientes descontentos.
El logging nunca debería ser una idea de último momento. Es gratuito, no daña a nadie y hace tu vida (y la vida de tu equipo) significativamente más fácil.
Un Caso de Uso Simple: Creación de Pedidos
Tomemos un ejemplo de un sistema de comercio electrónico básico con un caso de uso de crear pedido. Esta función recibe un usuario
y una lista de productos
. Realiza algunas verificaciones básicas:
- Si el usuario no existe, devuelve un error.
- Si no se encuentran productos válidos, devuelve un error.
- Si todo está bien, crea un pedido.
Aquí está la lógica base en pseudocódigo:
def create_order(user_id, products):
user = find_user(user_id)
if user is None:
return None # El usuario no existe
valid_products = []
for product in products:
if is_valid_product(product):
valid_products.append(product)
if not valid_products:
return None # No hay productos para procesar
order_id = random_id()
simulate_payment_delay() # Simulando demora de base de datos
return order_id
A primera vista, esta función funciona. Pero, ¿qué sucede si algo falla? Exploremos:
- ¿Qué pasa si el usuario no existe?
- ¿Qué pasa si la lista de productos está vacía?
- ¿Qué pasa si se proporciona un producto inválido?
Si no haces logging de estos casos, no sabrás dónde ni por qué falló la creación del pedido. Arreglemos eso.
Agregando Logs Simples: La Visibilidad Importa
Agregar logs es sencillo. Comencemos cubriendo algunos casos extremo:
1. Usuario No Encontrado
Si el usuario no existe, loggea ese evento específico:
if user is None:
logger.info(f"Usuario no encontrado: {user_id}")
return None
Ahora, si ocurre un incidente, sabrás inmediatamente qué ID de usuario causó el problema.
2. No se Encontraron Productos
Si la lista de productos está vacía, loggea eso también:
if not valid_products:
logger.info(f"No se encontraron productos para el pedido")
return None
3. Productos Inválidos
Si los productos son omitidos porque son inválidos, loggea cada ocurrencia:
for product in products:
if not is_valid_product(product):
logger.info(f"Producto no encontrado: {product}")
continue
valid_products.append(product)
Clases de Instrumentación
Una queja común sobre el logging es que puede saturar el código, dificultando su lectura. Para evitar esto, podemos usar una clase de instrumentación para encapsular nuestros logs.
Aquí hay un ejemplo:
class CreateOrderInstrumentation:
def user_not_found(self, user_id):
logger.info(f"Usuario no encontrado: {user_id}")
def no_products_found(self):
logger.info(f"No se encontraron productos")
def product_not_found(self, product):
logger.info(f"Producto no encontrado: {product}")
Usando la Clase de Instrumentación
En lugar de loggear directamente en tu lógica de negocio, puedes llamar a métodos de la clase de instrumentación:
instrumentation = CreateOrderInstrumentation()
if user is None:
instrumentation.user_not_found(user_id)
return None
for product in products:
if not is_valid_product(product):
instrumentation.product_not_found(product)
continue
valid_products.append(product)
if not valid_products:
instrumentation.no_products_found()
return None
Este enfoque tiene varios beneficios:
- Código Más Limpio: La lógica de negocio permanece enfocada en su propósito.
- Separación de Responsabilidades: El logging y monitoreo se abstracten en una clase separada.
- Extensibilidad: Puedes agregar lógica de métricas o tracing sin tocar el código principal.
Enriqueciendo Logs con Contexto Adicional
Los logs son más útiles cuando contienen contexto. Por ejemplo, puedes incluir un Trace ID para correlacionar todos los logs de un único request o flujo:
import uuid
class CreateOrderInstrumentation:
def __init__(self):
self.trace_id = uuid.uuid4()
def log(self, level, message):
logger.log(level, f"{self.trace_id} - {message}")
def user_not_found(self):
self.log(logging.INFO, "Usuario no encontrado")
def no_products_found(self):
self.log(logging.WARNING, "No se encontraron productos")
def product_not_found(self, product):
self.log(logging.INFO, f"Producto no encontrado: {product}")
Con un Trace ID, puedes:
- Correlacionar logs entre servicios y sistemas.
- Rastrear el flujo de un request a través de diferentes partes de tu código.
Reflexiones Finales: Construye una Cultura de Logging
El logging no es un lujo, es una necesidad. Proporciona visibilidad en tus sistemas, te ayuda a identificar problemas rápidamente y hace que los postmortems sean mucho más efectivos.
Aquí está lo que puedes hacer a partir de hoy:
- loggea casos extremos: Cuando algo inesperado suceda, loggealo.
- Usa clases de instrumentación: Mantén tu código limpio y enfocado en la lógica de negocio.
- Agrega contexto: Usa IDs y detalles adicionales para hacer los logs más accionables.
El logging no cuesta nada. Es gratuito, es fácil, y te salvará de desastres. Así que adelante—agrega esos logs, construye esa visibilidad y crea una cultura de logging en tu equipo.
Tu yo futuro te lo agradecerá.
Si te ha gustado este artículo, considera compartirlo con tu equipo y suscribirte a mi blog para más consejos sobre escribir código limpio, mantenible y listo para producción. ¡Nos vemos en la próxima publicación!