Consumir métricas con Windows_exporter

Windows_exporter es una herramienta de Prometheus que sirve para extraer métricas de sistemas Windows. Prometheus es el software encargado de tratar estas métricas y con Grafana podemos usar Dashboard para verlas. En este artículo veremos cómo, desde Python 3, podemos consumir y analizar estas métricas para integrarlas en nuestros propios scripts o aplicaciones.

¿Qué es Windows_exporter?

Windows_exporter es un agente que se instala en sistemas Windows y recopila información sobre el sistema (como uso de CPU, RAM, disco, red y otros) estos datos son expuestos en formato HTTP por lo que cuando el agente entre en funcionamiento los encontraremos en http://localhost:9182/metrics.

¿Por qué leer métricas con Python?

Si usamos Python para leer estas métricas tendremos ciertas ventajas.

  • No necesitamos un servidor central con Prometheus y Grafana instalado.
  • Integramos la monitorización en nuestras aplicaciones.
  • Procesamos y transformamos los datos para hacer reportes personalizados.
  • Nuestras aplicaciones pueden realizar acciones basadas en ciertos umbrales o condiciones de los datos.

¿Qué necesitaremos para hacer nuestros scripts o aplicaciones?

Descargar la última versión de Windows_exporter que podremos encontrar aquí: https://github.com/prometheus-community/windows_exporter/releases/latest. Existen dos versiones una .exe que se ejecuta directamente y que aconsejo para pruebas y otra .msi que funcionará como un servicio de Windows.

Seguridad

En cuanto iniciemos Windows_exporter dispondremos de todas las métricas en http://localhost:9182/metrics, dado que no dispone de ninguna opción nativa para limitar el acceso a estas métricas, podemos usar nuestro firewall para limitar el acceso externo al puerto 9182 o incluso limitar las direcciones IP de los equipos que tendrán acceso a estas métricas.

Requisitos previos

Para leer las métricas debemos tener instalada la biblioteca requests. Podemos hacerlo con:

pip install requests --upgrade

Ejemplo

El siguiente ejemplo obtiene todas las métricas, de hecho obtiene tanto métricas como comentarios, para su posterior procesado. Es muy importante ejecutar el programa windows_exporter previamente para obtener los resultados.

import requests

def obtener_metricas(url):
    try:
        respuesta = requests.get(url)
        respuesta.raise_for_status()
        return respuesta.text
    except requests.exceptions.RequestException as e:
        print(f'Error al obtener las métricas: {e}')
        return None

url_metricas = 'http://localhost:9182/metrics'
datos_metricas = obtener_metricas(url_metricas)

if datos_metricas:
    print('Métricas obtenidas correctamente.')
    print(f'Las métricas son: {datos_metricas}')
else:
    print('No se pudieron obtener las métricas.')

La función obtener_metricas(url) recibe el parámetro url que se corresponde con la url donde está ejecutandose windows_exporter, en el caso de ser nuestro propio equipo será http://localhost:9182/metrics y en el caso de ser un equipo en la red cuya dirección ip sea la 192.168.0.10 será http://192.268.0.10:9182/metrics. La variable respuesta se convierte en una instancia de la clase requests.models.Response al usar requests.get(url) de la clase requests y contendrá todos los métodos y atributos de esta en referencia a la url proporcionada. Por ejemplo, si la operación es incorrecta, el método raise_for_status() mostrará un mensaje parecido a este: 404 Client Error: Not Found for url: http://localhost:9182/metrics. Si llamamos al atributo reason podremos ver ‘Not Found’, sin embargo si todo es correcto, raise_for_status() no mostrará nada y en reason veremos ‘OK’.

El atributo text contiene el cuerpo del mensaje es decir todo el conjunto de métricas en el formato que usa Prometheus por lo que para poder usarlo deberemos convertirlos en algo diferente.

def analizar_metricas(datos):
    metricas = {}
    for linea in datos.splitlines():
        if linea.startswith('#') or not linea.strip():
            continue
        partes = linea.split()
        if len(partes) == 2:
            nombre, valor = partes
            try:
                metricas[nombre] = float(valor)
            except ValueError:
                continue
    return metricas

Esta función analiza las métricas, para ello va recorriendo los datos obtenidos anteriormente línea a línea, descartando las que comienzan con # ya que se trata de comentarios y diviendo el valor de la línea en dos, uno es el nombre de la métrica y el otro es el valor de la misma, convirtiendo el valor en float si se trata de un valor numérico con el bloque try que producirá una excepción y continuará si no es un número o lo asignará al nombre si lo es. Finalmente devolverá un diccionario con las métricas.

if metricas:
    print(f'RAM total: {metricas["windows_cs_physical_memory_bytes"]/(1024**3):.2f} GB')
    print(f'RAM libre: {metricas["windows_os_physical_memory_free_bytes"]/(1024**3):.2f} GB')       
else:
    print("No se encontraron métricas.")

Finalmente, como ejemplo, si hay métricas buscamos las que contienen windows_cs_physical_memory_bytes que contiene el valor de la memoria física total y windows_os_physical_memory_free_bytes que contiene la cantidad de memoria libre.

El código del ejemplo completo es este:

import requests

def obtener_metricas(url):
    try:
        respuesta = requests.get(url)
        respuesta.raise_for_status()
        return respuesta.text
    except requests.exceptions.RequestException as e:
        print(f'Error al obtener las métricas: {e}')
        return None

def analizar_metricas(datos):
    metricas = {}
    for linea in datos.splitlines():
        if linea.startswith('#') or not linea.strip():
            continue
        partes = linea.split()
        if len(partes) == 2:
            nombre, valor = partes
            try:
                metricas[nombre] = float(valor)
            except ValueError:
                continue
    return metricas

url_metricas = 'http://localhost:9182/metrics'
datos_metricas = obtener_metricas(url_metricas)

metricas = analizar_metricas(datos_metricas)

if metricas:
    print(f'RAM total: {metricas["windows_cs_physical_memory_bytes"]/(1024**3):.2f} GB')
    print(f'RAM libre: {metricas["windows_os_physical_memory_free_bytes"]/(1024**3):.2f} GB')       
else:
    print("No se encontraron métricas.")

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *