24x7 Linux Página personal y profesional HTML 4.01 válido CSS válido
Generación de archivos de log personalizados

Salvo que las páginas hospedadas en su servidor web Apache sólo las vaya a ver usted, las pueda ver más gente pero no reciba ninguna visita, o simplemente no le interese saber quién, cuándo, cómo y desde dónde son visitadas, seguro que le interesará saber que Apache dispone de numerosas posibilidades para registrar información sobre las visitas a su sitio web.

Si procesamos los registros o logs generados por el servidor Apache con alguno de los múltiples y variados programas analizadores de logs disponibles (tanto gratuitos como de pago) además de tener constancia de todas las visitas podremos ver estadísticas diversas, como páginas más visitadas, lugares del mundo donde es más popular nuestro sitio, en qué franjas horarias recibimos más visitantes, o el porcentaje de uso de cada navegador web entre los visitantes de nuestro sitio.

Entre otros programas analizadores de log tenemos algunos libres (y gratuitos), con diversos niveles de complejidad, velocidad, profusión de estadísticas y vistosidad. Desde analog a awstats, pasando por webalizer, cualquier analizador de logs que entienda el formato CLF (Common Log Format) servirá para obtener estadísticas de visitas a nuestra web.

Pero Apache no sólo puede generar registros de visitas en este formato, sino en cualquier formato que desee el administrador, puesto que el contenido y tipo de las visitas que se registran son completamente configurables por el administrador. Es más, podemos tener archivos de log personalizados para cada uno de los diferentes virtual hosts que albergue nuestro servidor, o tener varios archivos de log cada uno de los cuales contenga información específica de algún detalle de la visita, etc.

En Apache, sin embargo, disponemos de un tipo especial de archivo de log que no es personalizable: el archivo de errores, definido por la directiva ErrorLog. Si bien tenemos la posibilidad de crear un archivo de errores por cada virtual host no podemos influir en el formato de la información que se registra en él, puesto que contiene los errores de inicio y en tiempo de ejecución tanto de Apache como de los posibles módulos cargables en uso, o la ejecución de los programas CGI.

Para personalizar los logs que genera Apache contamos principalmente con dos directivas de configuración: la directiva LogFormat (permite definir un formato personalizado, es decir, qué información se registra en cada petición de página), y CustomLog (permite definir un nuevo archivo de log donde se almacene la información definida en una directiva LogFormat previa). Es decir, separamos los conceptos de archivo de log (CustomLog) y la información que registramos cuando se nos pide una página, o formato del archivo (LogFormat).

Como puede comprobar en el archivo de configuración de Apache (típicamente /etc/apache/httpd.conf) ya están definidos de manera predeterminada algunos formatos de log:

LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %T %v" full
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %P %T" debug
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-Agent}i" agent

Los más habituales son los cuatro últimos, especialmente el LogFormat de nombre common, puesto que se corresponde con el formato CLF mencionado antes, y es el que entienden todos los programas analizadores de logs del mercado (algunos analizadores entienden más formatos, o son personalizables, pero todos entienden el formato CLF). El formato de la directiva incluye la información que registrar (indicada mediante las magic cookies precedidas por el signo %), y el nombre que le damos al formato, nombre que será el que usemos posteriormente en tantas directivas CustomLog como queramos. Dese cuenta también de que el formato llamado combined incluye la información de los formatos common, referer y agent.

Si echa un vistazo a la lista de cookies soportadas verá qué significa cada campo de las directivas LogFormat anteriores. Por ejemplo, la cookie %h registrará la dirección IP del cliente, y %{User-Agent} la identificación del navegador que usa (consulte este artículo para saber algo más sobre el funcionamiento del protocolo HTTP).

Pero con las directivas LogFormat sólo hemos definido varios formatos de información que podemos registrar ante cada petición de página, pero no hemos indicado a Apache que realmente la guarde en disco. Para ello debemos usar una o más directivas CustomLog, tanto en la configuración global del servidor (aplicará al servidor predeterminado) como en cada uno de los virtual hosts donde queramos personalizar los logs. Por ejemplo:

CustomLog /var/log/apache/access.log combined

<VirtualHost 192.168.1.101:80>
    ServerName www.example.com
    DocumentRoot /var/www/example.com
    DirectoryIndex index.html
    CustomLog /var/log/apache/example.com_common.log common
    CustomLog /var/log/apache/example.com_useragent.log agent
</VirtualHost>

Estos archivos de log definidos arriba comenzarán a guardar la información correspondiente en cuanto se señalice al proceso principal de Apache. Y cuando contengan datos de acceso ya los podríamos procesar con la utilidad de estadísticas web de nuestra elección. Pero a´n cabe la posibilidad de tener más control sobre la información registrada en disco, por ejemplo, decidiendo petición por petición si guardamos información de la misma en función de determinados parámetros de la propia petición (por ejemplo, sólo nos interesa conocer qué navegadores usan los usuarios que acceden a una determinada parte de la web).

Para hacer este registro condicional de la información necesitamos conocer otra directiva de Apache, la directiva SetEnvIfNoCase. Como su propio nombre indica, esta directiva establece una determinada variable de entorno si la petición HTTP recibida cumple un determinado patrón, evaluado sin distinguir entre mayúsculas y minúsculas (existe una directiva SetEnvIf, igual que la anterior pero donde el patrón se evalua distinguiendo entre mayúsculas y minúsculas).

La directiva CustomLog dispone de un parámetro opcional adicional a los ya vistos, que es la especificación de una variable de entorno que, de estar presente, hace registrar a disco la información definida por el LogFormat correspondiente. Si la variable de entorno indicada no está presente (por ejemplo, porque la petición no coincida con el patrón indicado en una directiva SetEnvIfNoCase previa), esa petición no se registrará en ese archivo de log (aunque bien pudiera registrarse en otro archivo distinto, con otro formato distinto).

Lo explicado en los párrafos previos se verá mucho más claro con un ejemplo práctico: desde principios del año 2003 Telefonica ha colocado dispositivos de proxy caché para los usuarios de líneas ADSL, en teoría para mejorar la velocidad de la navegación. Sin embargo, por cómo funciona el mecanismo de proxy, la dirección IP que ve nuestro servidor web no es la del usuario final, sino la del dispositivo que hace de proxy. Si este dispositivo da servicio a muchos usuarios que acceden a nuestras páginas, aparecerán todos estos accesos como provenientes desde la IP del proxy, no de los usuarios individuales. Parece evidente que en webs cuyo público mayoritario sea residente en España las estadísticas de acceso quedarán completamente desvirtuadas.

Lo que sucede es que en el caso de conexiones llegadas a través de un proxy, la cookie %h no contiene la IP del usuario final, sino la del dispositivo de proxy. Para evitar este problema de alguna manera tenemos que conseguir que Apache genere las entradas de log de manera distinta en función de si la petición llega directamente desde el usuario final, o a través de un proxy intermedio.

Afortunadamente los proxy comunican al servidor web la dirección IP del cliente en cuyo nombre están pidiendo la página. Para ello suelen enviar las cabeceras HTTP Client-IP y X-Forwarded-For en la petición de la página, variables cuya existencia y contenido podemos comprobar desde las directivas SetEnvIfNoCase. La idea será usar esta directiva para establecer una cierta variable de entorno cuando la petición provenga de un proxy, y luego tener dos CustomLog diferentes, uno el habitual (para conexiones directas) y otro donde sustituyamos la cookie %h por el contenido de alguna de las cabeceras con la dirección IP del cliente enviadas por el proxy.

# Sólo las peticiones de un proxy contendrán una dirección IP en Client-IP
SetEnvIfNoCase Client-IP "\." proxy

# Formato predeterminado de los logs CLF
LogFormat "%h %l %u %t \"%r\" %>s %b" common
# Definimos un formato de log especial para peticiones desde proxy
LogFormat "%{Client-IP}i %l %u %t \"%r\" %>s %b" common_proxy

# Las peticiones directas desde los clientes, con el formato habitual
CustomLog /var/log/apache/access.log common env=!proxy
# En las peticiones desde los proxy, usamos el formato alternativo
CustomLog /var/log/apache/access.log common_proxy env=proxy

Con la directiva SetEnvIfNoCase definimos una variable de entorno para las peticiones que contienen una cabecera Client-IP (en realidad miramos si el valor de la cabecera contiene un punto, lo cual sólo se cumple si la cabecera existe y contiene una dirección IP). Luego definimos un formato personalizado de log donde usamos el contenido de la cabecera HTTP Client-IP en lugar de el contenido de la cookie %h, que en el caso de un proxy contiene su dirección IP, no la del usuario final. Por último, guardamos la información de acceso en el formato correspondiente a peticiones directas (env=!proxy significa "variable proxy no definida") y a peticiones a través de proxy (env=proxy).

Fíjese que estamos guardando ambos tipos de información en el mismo archivo en disco: si bien la documentación de Apache no dice nada al respecto de si esto se puede hacer o no, el caso es que funciona, incluso en situaciones donde se guarda información simultáneamente en ambos formatos (en nuestro ejemplo puesto que la petición es directa o indirecta, sólo se generará una entrada de log en el archivo por cada petición de página). Otra opción sería guardar a dos archivos diferentes, y antes de procesarlos con nuestra herramienta de generación de estadísticas, unirlos (podría ser suficiente concatenarlos con un simple comando cat).

En este ejemplo hemos usado la cabecera HTTP Client-IP, puesto que si la petición proviene de un proxy contendrá la dirección IP del navegador del usuario. Por su parte, en la cabecera X-Forwarded-For se incluyen las IP de todos los proxy en el camino de la petición del usuario desde su navegador al servidor, y en situaciones con más de un proxy intermedio contendrá más de una dirección. A día de hoy desconozco si la anterior configuración de Apache funciona correctamente, entre otras cosas porque no estoy seguro de en qué cabecera HTTP los proxy están obligados a incluir la dirección IP del usuario al cual dan servicio (además de la cabecera Client-IP también está la cabecera Remote-Addr, por ejemplo).

Una manera de solucionar el problema potencial descrito en el párrafo anterior consistiría en definir tantas ternas de directivas SetEnvIfNoCase, LogFormat y CustomLog como posibles cabeceras puedan contener la IP real del cliente. Pero en tal caso también deberíamos modificar el CustomLog predeterminado, para que sólo aplique cuando la petición de página sea directa, por ejemplo, estableciendo una variable de entorno con SetEnvIfNoCase para todas las peticiones sin cabecera X-Forwarded-For. Evidentemente todo esto es demasiado laborioso como para exponerlo aquí, y posiblemente haya soluciones mucho mejores para hacer lo mismo, así que se deja como ejercicio al lector :-).

Última modificación: 25-February-2003 18:21:49 -0500

© 2002-2007 José Luis Domingo López. Todos los derechos reservados.
Contacte con el webmaster para informarle de fallos, incorrecciones o sugerencias.
Esta página cumple con los estándares HTML 4.01 y CSS2 del World Wide Web Consortium.