PyWombat

← Volver a artículos

Sistema de logs con Python y Systemd

August 2, 2021

1700 views

4 min de lectura

En un par de entregas anteriores, puntualmente: Systemd y Loggin, hablamos del cómo poder crear y programar servicios para nuestro sistema, y que estos se ejecuten de forma automática, además de conocer el cómo testear, de forma correcta, mediante mensajes nuestros programas en Python. 😎

Pues bien, en continuación con esos 2 artículos, en esta ocasión me gustaría compartir un pequeño tutorial sobre cómo podemos registrar en logs, todas aquellas salidas para nuestros programas en Python que se ejecuten con Systemd. 🐍

Es un tutorial sumamente interesante, ya que estaremos integrando muchas más herramientas al despliegue de nuestras aplicaciones. Un tutorial ideal para todas aquellas personas en el área de DevOps o que se encuentren administrando servidores. Si este es tu caso, te invito a que te quedes.

Antes de comenzar me gustaría mencionar que para este tutorial estaré trabajando con un sistema operativo Ubuntu. Si bien Systemd y journald, son herramientas predeterminadas para muchas distribuciones Unix no son un estándar global, por lo que es posible que en ciertos sistemas operativos no se puedan utilizar. Te recomiendo que te cerciores que el tuyo no sea el caso. 😰

Bien, sin más que decir, comencemos con el tutorial. 🥳

Journald y Python

Para poder llevar un control global sobre cada uno de los mensajes registrados en el sistema, haremos uso de journald, una servicio que se encarga de recolectar y almacenar en logs todos los mensajes del kernel.

Una de las principales ventajas de esta herramienta recae en la forma en la cual almacena los mensajes, reemplazando los clásicos archivos en texto con formatos mucho más complejos , algo así como una base de datos de logs centralizada. Lo cual sin duda nos viene perfecto para búsquedas concretas. Esos días de buscar sobre docenas de archivos logs ya puedes dejarlos en el pasado.

Por lo tanto, para poder almacenar los mensajes de nuestros programas, indiscutiblemente lo haremos con journald.

Lo primero que haremos será instalar la librería systemd. Recomiendo ampliamente lo hagamos utilizando un entorno de desarrollo.

pip install systemd

Activamos el entorno.

source env/bin/activate

Instalamos la librería.

pip install systemd

En caso tengas algún tipo de error al instalar systemd, te recomiendo ejecutes antes el siguiente comando.

sudo apt install libsystemd-dev

Posteriormente integraremos el módulo logging a nuestro script en Python.

En mi caso el script quedaría de la siguiente manera.

#main.py

import logging

import time
import datetime

# Instanciamos un objeto logger
logger = logging.getLogger(__name__)

# Definimos a partir de que nivel se imprimirán los mensajes
logger.setLevel(logging.DEBUG)


while True:
    now = datetime.datetime.now()

    logger.info(
        now.strftime("%b %d %Y %H:%M:%S")
    )

    time.sleep(10)

En mi caso el programa es bastante sencillo. Cada 10 segundos se imprime en consola la fecha actual. Para ello reemplazo la función print por logger.info.

Lo siguiente será instanciar y configurar nuestro objeto de tipo JournaldLogHandler.

Mi script final quedaría así.

import logging

import time
import datetime

from systemd.journal import JournaldLogHandler

# Instanciamos un objeto logger
logger = logging.getLogger(__name__)

# Definimos a partir de que nivel se imprimirán los mensajes
logger.setLevel(logging.DEBUG)

journald_handler = JournaldLogHandler()

# Definimos el formato para los mensajes
journald_handler.setFormatter(logging.Formatter(
    '[%(levelname)s] %(message)s'
))

# Añadimos el journald_handler al logger
logger.addHandler(journald_handler)

while True:
    now = datetime.datetime.now()

    logger.info(
        now.strftime("%b %d %Y %H:%M:%S")
    )

    time.sleep(10)

Perfecto. Lo que debemos hacer ahora será registrar nuestro servicio mediante systemctl.

Procedemos a crear un nuevo archivo .service. En mi caso el archivo tendrá por nombre pywombat. Tú puedes usar el nombre que tú desees.

nano /etc/systemd/system/pywombat.service

Mi configuración sería la siguiente.

[Unit]
Description=Simple Python Script to test logs

[Service]
Type=simple
WorkingDirectory=/root/project/
ExecStart=/env/bin/python main.py
Restart=on-abort

[Install]
WantedBy=multi-user.target

Una vez con el archivo del servicio creado, lo siguiente será iniciarlo.

sudo systemctl start pywombat

En mi caso no pretendo que el servicio inicie cuando el sistema así lo hago. En caso tú así lo desees, deberás ejecutar el siguiente comando.

sudo systemctl enable <nombre del servicio>

Para cerciorarnos que todo funcione correctamente echémosle un vistazo a sus estatus.

sudo systemctl status pywombat

Si todo ha salido bien, deberías ver una salida como la siguiente.

 pywombat.service - Simple Python Script to test logs
     Loaded: loaded (/etc/systemd/system/pywombat.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2021-08-03 01:12:31 UTC; 25s ago
   Main PID: 9773 (python)
      Tasks: 1 (limit: 1136)
     Memory: 5.4M
     CGroup: /system.slice/pywombat.service
             └─9773 /root/project/env/bin/python main.py

Aug 03 01:12:31 ubuntu-s-1vcpu-1gb-intel-sfo3-01 systemd[1]: Started Simple Python Script to test logs.
Aug 03 01:12:31 ubuntu-s-1vcpu-1gb-intel-sfo3-01 __main__[9773]: [INFO] Aug 03 2021 01:12:31
Aug 03 01:12:41 ubuntu-s-1vcpu-1gb-intel-sfo3-01 __main__[9773]: [INFO] Aug 03 2021 01:12:41
Aug 03 01:12:51 ubuntu-s-1vcpu-1gb-intel-sfo3-01 __main__[9773]: [INFO] Aug 03 2021 01:12:51

Ver el log

Listo, una vez configurado nuestro servicio, cualquier mensaje que este produzca, ya sea un impresión en consola o un mensaje de error (stdout o stderr) debería registrarse en los logs de journald.

Para confirmar esto haremos uso del programa journalctl.

Ejecutamos el siguiente comando.

journalctl -b -u <nombre del servicio>

La bandera -b nos permite filtrar los mensajes de Logs con respecto a un servicio. Y mediante la bandera -b le indicamos a journald que solo nos muestre los mensajes registrados a partir del último inicio del sistema.

journalctl -b -u pywombat

En mi caso la salida es la siguiente.

-- Logs begin at Tue 2021-08-03 00:54:38 UTC, end at Tue 2021-08-03 01:13:51 UTC. --
Aug 03 01:12:31 ubuntu-s-1vcpu-1gb-intel-sfo3-01 systemd[1]: Started Simple Python Script to test logs.
Aug 03 01:12:31 ubuntu-s-1vcpu-1gb-intel-sfo3-01 __main__[9773]: [INFO] Aug 03 2021 01:12:31
Aug 03 01:12:41 ubuntu-s-1vcpu-1gb-intel-sfo3-01 __main__[9773]: [INFO] Aug 03 2021 01:12:41
Aug 03 01:12:51 ubuntu-s-1vcpu-1gb-intel-sfo3-01 __main__[9773]: [INFO] Aug 03 2021 01:12:51
Aug 03 01:13:01 ubuntu-s-1vcpu-1gb-intel-sfo3-01 __main__[9773]: [INFO] Aug 03 2021 01:13:01
Aug 03 01:13:11 ubuntu-s-1vcpu-1gb-intel-sfo3-01 __main__[9773]: [INFO] Aug 03 2021 01:13:11
Aug 03 01:13:21 ubuntu-s-1vcpu-1gb-intel-sfo3-01 __main__[9773]: [INFO] Aug 03 2021 01:13:21
Aug 03 01:13:31 ubuntu-s-1vcpu-1gb-intel-sfo3-01 __main__[9773]: [INFO] Aug 03 2021 01:13:31
Aug 03 01:13:41 ubuntu-s-1vcpu-1gb-intel-sfo3-01 __main__[9773]: [INFO] Aug 03 2021 01:13:41
Aug 03 01:13:51 ubuntu-s-1vcpu-1gb-intel-sfo3-01 __main__[9773]: [INFO] Aug 03 2021 01:13:51

Si tú, al igual que yo, visualizas los mensajes en consola, solo basta darte la en hora buena, haz configurado tu proyecto para llevar un mejor control sobre cada uno de los mensajes creados. 🥳

Dejamos aun lados los clásicos archivos .logs de toda la vida, y nos centramos en usar herramientas mucho más poderosas para estas tareas. 💾