Hora del código #59
Tenía que dar una presentación sobre un servidor web que lleva en línea 11 años y hay que migrar porque los anteriores encargados no le dieron mantenimiento en los últimos 4 años, ahí los logs de Nginx dejan de ser solo archivos de texto que revisás cuando algo se rompe. Son la única fuente de verdad sobre quién visita el sitio, qué descargan, qué bots lo están rastreando y qué IPs están probando vulnerabilidades a las tres de la mañana. El problema es que leer esos logs directamente no es viable cuando tenés años de datos acumulados en archivos rotados y comprimidos, y necesitás responder preguntas concretas ante una reunión o un informe.
Así que escribí mi propio analizador en Python. Genera un reporte HTML completo que podés abrir en el navegador sin depender de ningún servidor adicional.
Lo que hace el script es leer todos los archivos .log y .log.gz de un directorio, parsear cada línea con una expresión regular que extrae la IP, timestamp, método, ruta, código de respuesta, bytes transferidos, referer y user agent, y luego clasifica cada request en tres categorías: humano, bot o script. La clasificación es por user agent, con una lista de más de setenta firmas compiladas que incluyen crawlers de buscadores, agentes de LLMs como GPTBot o ClaudeBot, herramientas de monitoreo, librerías HTTP de programación y headless browsers. Lo que no coincide con ninguna firma y tiene un UA razonable se asume humano. Si no tiene UA, es script.
Con esos datos clasifica sesiones humanas usando una ventana de treinta minutos de inactividad por combinación de IP y hash de user agent, calcula tasa de rebote, páginas por sesión y duración promedio. También resuelve geolocalización con las bases de datos GeoLite2 de MaxMind, lo que me da país, ciudad y ASN por cada IP. Ese dato de ASN es particularmente útil porque permite detectar tráfico que llega con user agent de navegador humano pero desde rangos de IP de AWS, Google Cloud, Azure u otros proveedores de hosting, que en la mayoría de los casos son bots disfrazados. El reporte los marca como “humanos desde hosting” y los separa del conteo real.
El output es un único archivo HTML autocontenido con Chart.js incrustado que incluye KPIs en la parte superior, tendencia mensual con visitantes únicos por mes (IPs únicas humanas, no solo pageviews), distribución por hora, heatmap de día por hora, desglose de bots y scripts, análisis geográfico con ciudades, panel de seguridad con requests sospechosos y abusadores de tasa, análisis de descargas de PDFs separado por humanos y bots, errores 404 con distinción entre probes sin referer y links rotos con referer, y ranking de IPs por bandwidth y por cantidad de requests. Todo en un solo HTML sin dependencias externas, que podés archivar, mandar por correo o poner en un directorio protegido del servidor.
Ahora, lo justo es reconocer qué existe ya en el mercado antes de justificar por qué escribí algo propio.
La herramienta más conocida para esto es GoAccess. Es open source, escrita en C, increíblemente rápida, y tiene tanto modo terminal interactivo como generación de HTML. La versión estable actual es la 1.10.2. Sus ventajas son reales: análisis en tiempo real, soporte nativo para logs comprimidos, poco consumo de recursos, y puede hacer reporting continuo con WebSockets si la dejás corriendo. El problema es que su modelo de clasificación de bots es básico, no tiene una lista curada extensa de firmas, y la geolocalización requiere compilar la herramienta con soporte para GeoIP2 desde el código fuente, lo que en muchos servidores de producción no es trivial. Yo de hecho uso GoAccess en el mismo servidor y me ha servido bien para revisiones rápidas vía SSH y algún reporte HTML, pero no me da la segmentación que necesitaba.
AWStats es la herramienta clásica, existe desde el año 2000 y todavía se mantiene activa. Está escrita en Perl, genera reportes HTML estáticos y es lo que la mayoría de paneles de hosting incluían por defecto hace quince años. El problema es que configurarla con Nginx es un proceso innecesariamente complejo, con archivos de configuración uno por dominio con nombres específicos en directorios específicos, y sus reportes no distinguen bien entre tráfico humano real y bots. Para el tipo de análisis que necesitaba hacer, era demasiado limitada sin ser más simple que la alternativa.
En la categoría de analytics completos con base de datos propia están Matomo y las opciones más modernas orientadas a privacidad como Plausible, Umami o GoatCounter. Matomo es robusto y soporta importar logs de servidor, pero requiere PHP, MariaDB y un stack completo para funcionar. Es básicamente instalar otra aplicación web en el servidor. Plausible y Umami son más ligeras pero dependen de JavaScript en el cliente, lo que significa que no registran visitas de usuarios con bloqueadores, bots que no ejecutan JS, ni descargas directas de archivos. Para un sitio donde parte importante del tráfico (en transferencia) es descargas de PDFs desde aplicaciones externas, ese punto ciego es inaceptable.
La diferencia que justifica haber escrito mi propio script es que ninguna de esas herramientas hace exactamente lo que necesito: análisis profundo de logs sin infraestructura adicional, con clasificación detallada de bots por familia, detección de bots disfrazados por ASN, análisis específico de descargas de PDFs, y un reporte portable en un solo archivo. Python, Chart.js y las bases de MaxMind hacen posible todo eso en menos de ochocientas líneas sin instalar nada que no tenga ya en el servidor.
El script está disponible en mi repositorio si te interesa adaptarlo a tu propio caso.