sys.monitoring es la API moderna de Python para observar la ejecución de programas con menor overhead y más control que el tracing tradicional. Está diseñada para depuradores, profilers, herramientas de cobertura, analizadores de rendimiento y sistemas de observabilidad que necesitan eventos concretos sin recibir todas las notificaciones posibles.
En esta guía aprenderás a reservar un identificador de herramienta, seleccionar eventos, registrar callbacks, limitar el monitoreo a código específico, reducir impacto, evitar recursión y diseñar una arquitectura segura para producción.
Por qué usar sys.monitoring
El tracing clásico mediante sys.settrace puede ser costoso porque el callback se ejecuta para muchos eventos. Una herramienta que solo necesita detectar el inicio y el retorno de funciones termina pagando por una superficie más amplia. sys.monitoring permite activar únicamente los eventos útiles y, cuando corresponde, solo para determinados objetos de código.
Esto resulta útil para cobertura, depuración, conteo de llamadas, análisis de excepciones, diagnóstico en runtime y herramientas de desarrollo. Varias herramientas pueden coexistir gracias a identificadores separados.
Reservar un identificador
Cada consumidor debe reservar un ID y asociarlo con un nombre legible. El ID separa callbacks y configuraciones de otros monitores.
import sys
TOOL_ID = 3
sys.monitoring.use_tool_id(TOOL_ID, "mi-monitor")
try:
pass
finally:
sys.monitoring.free_tool_id(TOOL_ID)
Libera siempre el identificador. Un context manager o una clase dedicada ayuda a garantizar limpieza incluso cuando ocurre una excepción.
Seleccionar eventos
La API expone constantes de eventos que se combinan como una máscara de bits. Según la versión de Python, existen eventos para inicio y retorno de funciones Python, llamadas, saltos, instrucciones, excepciones y otros puntos.
events = sys.monitoring.events
mask = events.PY_START | events.PY_RETURN
sys.monitoring.set_events(TOOL_ID, mask)
Activa el conjunto mínimo. Monitorear cada instrucción cuando solo necesitas límites de función genera trabajo y datos innecesarios.
Registrar callbacks
Los callbacks se registran por evento. Sus argumentos dependen del evento, por lo que debes revisar la documentación de la versión soportada.
def on_start(code, instruction_offset):
print("inicio:", code.co_name, instruction_offset)
sys.monitoring.register_callback(
TOOL_ID,
sys.monitoring.events.PY_START,
on_start,
)
Mantén el callback corto. Es mejor incrementar contadores, escribir registros compactos en una cola o actualizar estructuras simples. Formatear logs, llamar servicios externos o ejecutar análisis complejo dentro del callback distorsiona el programa observado.
Monitoreo global y local
Los eventos globales afectan una superficie amplia. Los eventos locales permiten seleccionar objetos de código específicos. Esta capacidad es clave para reducir overhead.
Un profiler interno puede observar únicamente módulos de la aplicación e ignorar frameworks. Una herramienta de pruebas puede limitarse a funciones críticas. Un depurador puede activar detalle solo alrededor de una zona sospechosa.
Contador de llamadas
import sys
from collections import Counter
TOOL_ID = 3
calls = Counter()
def on_start(code, instruction_offset):
calls[code.co_name] += 1
sys.monitoring.use_tool_id(TOOL_ID, "contador")
sys.monitoring.register_callback(
TOOL_ID,
sys.monitoring.events.PY_START,
on_start,
)
sys.monitoring.set_events(
TOOL_ID,
sys.monitoring.events.PY_START,
)
# Ejecuta aquí la carga de trabajo.
sys.monitoring.set_events(TOOL_ID, 0)
sys.monitoring.free_tool_id(TOOL_ID)
print(calls)
En producción, usa try/finally, evita imprimir directamente y protege estructuras compartidas cuando existen hilos.
Buenas prácticas de rendimiento
Primero, selecciona pocos eventos. Segundo, filtra módulos y objetos de código. Tercero, mantén callbacks de tiempo constante cuando sea posible. Cuarto, agrega datos antes de exportarlos. Quinto, ofrece una opción para desactivar totalmente el monitoreo.
Mide la propia herramienta. Ejecuta benchmarks representativos con instrumentación activada y desactivada. Observa CPU, latencia, memoria y volumen de eventos. Si el monitor cambia demasiado el comportamiento, los resultados pueden ser engañosos.
Evitar reentrada
Un callback puede ejecutar código Python que también genera eventos. Sin protección, puede producir recursión. Usa una bandera thread-local o basada en contexto para impedir reentrada accidental. Mantén pocas dependencias y evita llamar funciones instrumentadas desde el callback.
El callback tampoco debería propagar excepciones hacia la aplicación. Captura errores internos, registra un diagnóstico compacto y desactiva la herramienta si su estado deja de ser confiable.
Hilos y asyncio
Los contadores compartidos necesitan sincronización o agregación segura. En servicios asíncronos, asocia observaciones con el contexto de la solicitud. El artículo sobre contextvars en Python explica cómo propagar contexto entre tareas. La guía de asyncio.Runner en Python aporta contexto sobre el ciclo de vida asíncrono.
Una arquitectura útil envía registros a una cola limitada y los procesa en otro worker. La cola acotada evita crecimiento infinito. Cuando se llena, decide si descartas muestras, agregas localmente o desactivas el detalle.
Logs, métricas y trazas
No escribas una línea de log por cada evento en un servicio ocupado. Agrega llamadas, duraciones, excepciones o muestras. Un registro estructurado puede incluir módulo, función, archivo, línea, tipo de evento e identificador de correlación.
Para métricas, contadores e histogramas suelen ser mejores que streams crudos. Para trazas, crea spans solo en límites relevantes. Filtra rutas, mensajes de excepción y datos sensibles antes de exportar.
Pruebas
Prueba funciones normales, métodos, generators, coroutines, llamadas anidadas, excepciones, recursión y cancelación. Confirma que los callbacks reciben los eventos previstos, que los filtros locales funcionan y que el cierre elimina registros.
Incluye pruebas de reentrada, concurrencia, desbordamiento de cola e inicialización parcial. Ejecuta una segunda prueba después del cierre para demostrar que no quedó ningún callback activo.
Compatibilidad
sys.monitoring no está disponible en versiones antiguas. Una biblioteca reutilizable debe detectarlo con hasattr(sys, "monitoring"). El fallback puede usar otro mecanismo, ofrecer menos funciones o mostrar un error claro.
Consulta la documentación oficial de sys.monitoring y la PEP 669. Para introspección complementaria, revisa inspect.signature en Python y inspect.getmembers_static en Python.
Arquitectura recomendada
Separa configuración, recolección, almacenamiento y exportación. La configuración elige eventos y filtros. Los callbacks generan registros mínimos. El almacenamiento agrega datos. La exportación convierte resultados en logs, métricas, archivos o interfaz.
Esta separación facilita benchmarks, pruebas y reemplazo de destinos. También permite modos distintos: contadores ligeros siempre activos y diagnóstico detallado habilitado temporalmente.
Seguridad y privacidad
La instrumentación puede revelar rutas, nombres de funciones, excepciones y estructura interna. Trata los datos como información operacional sensible. Aplica allowlists, redacta secretos, limita retención y restringe acceso.
No uses callbacks como lógica de negocio. La corrección de la aplicación nunca debe depender de que el monitor esté activo. Un fallo de observabilidad debe degradar el diagnóstico, no el servicio.
Cuándo elegir otra opción
Usa logging normal cuando eventos explícitos sean suficientes. Usa métricas para señales agregadas estables. Usa profilers por muestreo cuando basten estadísticas. Elige sys.monitoring cuando necesites eventos de ejecución y control selectivo.
Conclusión
sys.monitoring es una base potente para herramientas modernas de Python. Sus eventos selectivos, aislamiento por herramienta y monitoreo local reducen el costo. Con callbacks mínimos, filtros, limpieza, benchmarks, privacidad y pruebas robustas, puedes crear profilers, depuradores, cobertura y diagnósticos sin volver inestable la aplicación observada.







