Código del Día: Mensajes informativos en tu archivo de sucesos (log)

Leonardo Herrera
Creado: 16/4/2002
Última Actualización: 21/9/2004

Nota: Este código funciona con GCC 3.1 o superior. Ni idea acerca de otros compiladores.

Considérese la siguiente función:

void somefunc(){
     int resultado = 2 + 2;
     if ( resultado == 5 )
        Log( "Esto es imposible. %d", resultado );
}

En el (improbable) caso de que se llegue a la línea donde se llama a "Log", en la línea 85 del archivo "main.c", generaría un mensaje en stderr de la forma:

main.c:85:somefunc:
Esto es imposible. 5

Si te has encontrado con la necesidad de "logear" (llevar un registro) de varios mensajes de advertencia o "debug de los pobres", esta técnica te puede ayudar. Consiste de dos partes. Primero, una función, que llamaremos strLog. Está definida de la siguiente forma:

void strLog(char *file, char *function, int line, char *fmt, ...)
{
     FILE *fpl = NULL;
     char buf[1024];
     va_list args;

     va_start(args, fmt);
     vsprintf(buf, fmt, args);
     va_end(args);

     fprintf(stderr, "%s:%s:%d:%s\n", file, function, line, buf);
}

Esta función es bastante sencilla, una vez que se entiende. La magia está en la declaración:

void strLog(char *file, char *function, int line, char *fmt, ...)

Esta declaración indica al compilador que la función toma argumentos (parámetros) variables. La llamada a las funciones (o macros) va_start, vsprintf y va_end se asegura que en "buf" quedará un string con el formato usado en las funciones "printf" (printf, sprintf,. fprintf, etc.)

Pero strLog no es muy práctica: cada vez que la llamo, debo incluir a mano las variables file, function y line, y eso no tiene gracia. Es aquí donde incluímos una macro:

#define Log( ... ) \
        strLog( __FILE__, __PRETTY_FUNCTION__, \
        __LINE__, __VA_ARGS__ )

Esta macro es de un tipo especial de macros, llamadas variadic. ¿Por qué ese nombre? No lo sé, pero la gente de GCC es buena programando, por lo que no me cuestiono ni los cuestiono a ellos. Funciona utilizando las variables de compilador __FILE__, __LINE__ y __PRETTY_FUNCTION__. __VA_ARGS__ se expande como los parámetros que se utilizaron en la macro.

Obviamente, hay un par de detalles. El primero es el tamaño limitado del buffer usado en strLog. Si se hace crea un string muy largo o se expande más allá de 1024 caracteres (incluyendo el cero terminador) se puede incurrir en buffer overrun. Segundo, no es muy portable (__PRETTY_FUNCTION__ funciona en GCC, pero hay otros versiones para esta macro, tales como __func__ que al final creo que es una variable.)

Se puede experimentar un poco más analizando los resultados de ejecutar el preprocesador en el código a expandir (gcc -E).

Consultas y sugerencias a mi correo.

Este sitio es mantenido con ePublish