Analizando los Logs del Apache con PHP (II)
El parseador del servidor Web Apache tiene como función procesar todos los datos que guarda el servidor en los ficheros de registros (logs) para posteriormente introducirlos con una cierta estructura y orden en una base de datos para poder analizar toda la información y realizar con ella estadísticas variadas tales como conocer desde qué páginas nos visitan más, que navegador es el más utilizado para navegar por nuestro sistema, etc.
if (stristr(PHP_OS,"win")) $logHoy = "$ruta/../../logs/access.log"; # el fichero de log's de hoy para windows else $logHoy = "$ruta/../../logs/access_log"; # el fichero de log's de hoy para linux |
El script va a continuar preguntándose por la plataforma bajo la que se está ejecutando, ya que el fichero de logs del que tendrá que sacar la información tiene un nombre diferente dependiendo si se ejecuta en un Linux (access_log) o un windows (access.log). Una vez conocido el fichero procederá a abrirlo en modo lectura, para poder leer todos y cada uno de los registros que introduce el servidor por cada petición recibida.
$fd = fopen("$logHoy","r"); //abrimos el fichero de logs de acceso del apache |
Por cada uno de estos logs, se produce lo que llamamos parseo, es decir un proceso de normalizar los datos de entrada para darle una salida específica. En nuestro caso la salida será una base de datos Mysql. Un registro de acceso tiene como ya hemos comentado con anterioridad la siguiente forma :
127.0.0.1 - deckerix [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326 "http://www.example.com/start.html" "Mozilla/4.08 [en] (Win98; I ;Nav)" |
Lo primero que se hace con el registro es separarlo por campos vacíos e introducir cada uno de ellos en una posición de un vector. La primera posición tendrá por tanto la IP del cliente, la siguiente el guión, y así sucesivamente. Algo a tener en cuenta
es que la posición 10 tendrá la referencia, y que desde la 11 hasta el final todos los campos deberían ser solo uno, el Agente de usuario, ya que es probable que esté formado por varios blancos. (por lo tanto hay un mecanismo que unifican esas posiciones del vector).
while ($x = fgets($fd,1024)){//mientras no sea final de fichero leemos registros y almacenamos $agente =""; $elHost =""; $fechaSinParsear=""; $bytes =""; $arraya = explode(" ", $x); //separamos el registro por blancos y lo insertamos al array $elHost = $arraya[0]; //primera posicion tiene la IP //1 seria - //2 seria user uid $fechaSinParsear = $arraya[3]; //toda la fecha sin parsear //4 seria la zona //5 seria post o get $peticion = $arraya[6]; //peticion $codigo = $arraya[8]; //codigo $bytes = $arraya[9]; //bytes ocupa el registro $referencia = $arraya[10]; //referer for ($i=11; $i< count($arraya);$i++){//no sabemos por cuantos blancos está formado el agente. //pillamos toda la información que quede $agente=$agente." ".$arraya[$i]; } $peticion = str_replace("\"","", $peticion);//parseando... $referencia = str_replace("\"","", $referencia);//parseando... |
Una vez tenemos los campos bien separados, son estos los que sufren el parseo final. Por ejemplo la fecha, que aparece así 10/Oct/2000, deberá tener el siguiente formato para ser introducida en la base de datos Mysql : 2000-10-10. El parseo más importante y complicado es
el del agente de usuario. De él se puede sacar tanto el sistema operativo y el navegador del cliente. Mediante una serie de expresiones regulares que ya expliqué en un post anterior, seremos capaces de cotejar todos los posibles casos de los valores del Agente de Usuario con los SO y los navegadores. Pero cabe la posibilidad de que el agente de usuario no sea un navegador, si no un robot Web, caso que también hemos registrado.
/******************PARSEAR FECHA******************/ $fechaParseada = str_replace("[", "", $fechaSinParsear); list($day, $month, $year) = explode("/",$fechaParseada,3);//recuperamos el dia list($year, $hour, $minute, $second) = explode(":",$year,4);//recuperamos la hora /*****PARSEAR EL MES********/ if ($month=="Jan") $month="01"; elseif ($month=="Feb") $month="02"; elseif ($month=="Mar") $month="03"; elseif ($month=="Apr") $month="04"; elseif ($month=="May") $month="05"; elseif ($month=="Jun") $month="06"; elseif ($month=="Jul") $month="07"; elseif ($month=="Aug") $month="08"; elseif ($month=="Sep") $month="09"; elseif ($month=="Oct") $month="10"; elseif ($month=="Nov") $month="11"; elseif ($month=="Dec") $month="12"; $fecha = "$year-$month-$day"; //unimos la info $hora = "$hour:$minute:$second";//unimos la info /******************FIN PARSEAR FECHA******************/ |
/******************************PARSEAR BYTES**********************************/ if ($bytes == "-") $bytes=0; /***************************FIN PARSEAR BYTES*********************************/ /****************************PARSEAR AGENTE***********************************/ $sistemaOperativo = reconocedorSistemaOperativo($agente); $navegador = reconocedorNavegador($agente); $robot = reconocerRobot($agente); /**************************FIN PARSEAR AGENTE*********************************/ |
Una vez se tienen todos los datos parseados, éstos se introducen en la base de datos.
Estos datos al estar ya en la base de datos, se borran del fichero de logs del apache para no malgastar disco duro.
//insertamos la información parseada en la base de datos mysql_query("INSERT stadisticas (codigo,bytes,so,nav,agente,ip,dia,hora,referencia,peticion) VALUES ('$codigo','$bytes','$sistemaOperativo','$navegador','$agente','$elHost','$fecha','$hora','$referenc ia','$peticion')", $conexion); } fclose($fd);//cerramos el fichero de logs |
Por último comentar que en la base de datos guardamos los siguientes campos: el código de estado de la petición, los Bytes que ocupa el recurso solicitado, el sistema operativo, el navegador, el agente completo, la IP, el país, el día en el que se realizó la petición, la hora, el referer y el recurso pedido. Con toda esta información hemos sido capaces de mostrar gran cantidad de información de alta
importancia.