sys.monitoring: instrumentación de bajo overhead

Publicado el: 03/09/2026
Tempo de leitura: 6 minutos
Monitoreo de rendimiento y ejecución de código Python

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.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Código asíncrono que representa asyncio.eager_task_factory en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    asyncio.eager_task_factory: reduce overhead de tareas

    Aprende asyncio.eager_task_factory en Python para reducir overhead, entender cambios de orden y optimizar corrutinas cortas con seguridad.

    Ler mais

    Tempo de leitura: 5 minutos
    14/09/2026
    Desarrollador trabajando con timestamps UTC y calendar.timegm en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    calendar.timegm: convierte UTC a timestamp Unix

    Aprende calendar.timegm en Python para convertir fechas UTC en timestamps Unix y evitar errores de zona horaria y unidades.

    Ler mais

    Tempo de leitura: 6 minutos
    14/09/2026
    Programador analizando código para identificar tipos MIME de archivos con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    mimetypes.guess_file_type: detecta tipos MIME

    Aprende mimetypes.guess_file_type en Python para detectar tipos MIME en rutas, URLs, uploads y respuestas HTTP con fallbacks seguros.

    Ler mais

    Tempo de leitura: 4 minutos
    13/09/2026
    Componentes de servidor que representan intérpretes Python aislados ejecutándose en paralelo
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    InterpreterPoolExecutor: paralelismo real en Python

    Aprende InterpreterPoolExecutor en Python para tareas CPU-bound, intérpretes aislados, paralelismo real y concurrencia segura.

    Ler mais

    Tempo de leitura: 6 minutos
    13/09/2026
    Microprocesador que representa las CPU disponibles para un proceso Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    os.process_cpu_count: cuenta CPUs disponibles

    Aprende os.process_cpu_count en Python para dimensionar workers según las CPU disponibles para el proceso.

    Ler mais

    Tempo de leitura: 4 minutos
    12/09/2026
    Portátil con código digital que representa datos BLOB en SQLite
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    sqlite3.Blob: lee BLOBs sin cargar todo en memoria

    Aprende sqlite3.Blob en Python para leer y escribir BLOBs por partes, reducir memoria y manejar datos binarios en SQLite.

    Ler mais

    Tempo de leitura: 5 minutos
    12/09/2026