En los títulos y los textos vais a encontrar unas cuantas citaciones cinematográficas (y si, soy un cinéfilo). Si no os interesan podéis fingir no verlas, ya que no son fundamentales para la comprensión de los post...

Este blog es la versión en Español de mi blog en Italiano L'arte della programmazione in C. Espero que mis traducciones sean comprensibles...

lunes, 20 de enero de 2014

Syslog Royale
cómo escribir un Syslog en C - pt.1

Al escribir software siempre se debe intentar ser profesionales, independientemente del uso del software que estamos escribiendo. Por ejemplo, no se puede olvidar de prever un buen sistema de log (hay, también, algunos profesionales de la programación que ni siquiera saben lo que es un buen sistema de log... pero esta es otra historia.)

Para agregar un log al nuestro software podemos ayudarnos con el sistema operativo, por ejemplo en UNIX y Linux tenemos Syslog que es muy flexible y que nos hace la vida más fácil. En el caso de no poder (o no querer) utilizar los recursos proporcionados por el OS podemos pensar de escribir nuestro propio sistema de log, como veremos más adelante en este post. Vamos a tratar de emular , más o menos, la sintaxis y la función de Syslog, tratando de hacer un producto sencillo pero de calidad, o sea un producto profesional, porque siempre tratamos de ser profesionales, tal vez sin llegar a los excesos de profesionalidad del protagonista del título.
Me llamo Log, James Log
Sea porque no quiero hacer un post muy largo (se convertiría en aburrido), que por seguir una especie de enfoque específica+implementación (adaptado a un C -Blog ), he dividido el post en dos partes: en la primera describiré, a modo de especifica funcional, el header file (mylog.h) y un ejemplo de uso (main.c). En la segunda entrega describiré la implementación real .

Vemos el header file, mylog.h:
// niveles de mylog
#define MYLOG_NOLOG   -1 // ningún mensaje de log
#define MYLOG_ERROR   0  // condición de errore
#define MYLOG_WARNING 1  // condición de warning
#define MYLOG_DEBUG   2  // mensaje de debug

// prototipos globales
void myOpenLog(const char *fname, int level);
void myLog(int level, const char *format, ...);
void myCloseLog();
Simple y que se explica por sí mismo, ¿no? Nuestro producto se compone de tres funciones canónicas (abre, usa, cierra), y trabaja en niveles de gravedad (actualmente cuatro). Casi no hace falta explicar que, estableciendo un nivel, en el logfile aparecerán todos los mensajes de nivel de gravedad igual o mayor del nivel seleccionado. El nivel de NOLOG, luego, es básicamente una flag de inhabilitación que nos permite iniciar la aplicación sin logfile, para aligerar la ejecución: se supone que debe ser una inhabilitación real, es decir, la CPU y el sistema de I/O deben realimente trabajar menos sin el log habilitado.

Veamos ahora el ejemplo de uso, main.c:
#include <stdio.h>
#include <stdlib.h>
#include "mylog.h"

int main(int argc, char* argv[])
{
    // test argumentos
    if (argc != 3) {
        printf("mylog: wrong arguments counts\n");
        printf("usage: mylog logfile loglevel [e.g.: mylog log.txt 1]\n");
        return EXIT_FAILURE;
    }

    // open log: set logfname y loglevel (0=error/1=warning/2=debug; -1=nolog)
    myOpenLog(argv[1], atoi(argv[2]));

    // test mylog()
    myLog(MYLOG_ERROR, "esto es un msg de tipo %d (el nivel impostado es %d)",
        MYLOG_ERROR, atoi(argv[2]));
    myLog(MYLOG_WARNING, "esto es un msg de tipo %d (el nivel impostado es %d)",
        MYLOG_WARNING, atoi(argv[2]));
    myLog(MYLOG_DEBUG, "esto es un msg de tipo %d (el nivel impostado es %d)",
       MYLOG_DEBUG, atoi(argv[2]));

    // close log
    myCloseLog();

    // exit
    return EXIT_SUCCESS;
}
Sencillo, ¿no? Ejecuto un test de argumentos con ejemplo de uso (usage...), y luego abro, uso, cierro, salgo. Mediante una línea de comandos como "mylog log.log 1", ¿como será el resultado? Creará un file "log.log" que contendrá las siguientes dos líneas:
2014-01-18 18:39:33.890271 - esto es un msg de tipo 0 (el nivel impostado es 1)
2014-01-18 18:39:33.890407 - esto es un msg de tipo 1 (el nivel impostado es 1)
Añadimos detalles a la específica: usos sucesivos de la aplicación deben colgar líneas al logfile, ya que una clave fundamental de un log es mantener información de lo que pasa (y si cada vez reiniciamos el file la vamos a perder). Y si queremos empezar desde cero, podemos utilizar un otro filename, o borrar el viejo logfile antes de ejecutar. Otro detalle que llama la atención es  que necesitamos, además de nuestros textos de traza, también informaciones horarias, tal vez muy precisas (en el ejemplo hay los microsegundos: en el próximo episodio explicaré por qué).

Para hoy terminamos. Los mas trabajadores podrán, esperando la segunda parte, escribir su propia implementación, y luego compararla con la mía, pero la mía será seguramente mejor... (se aleja riendo).

¡Hasta el próximo post!