La class ApacheLog Parser
Le but de la Class est de parcourir chaque ligne du fichier logs et d'en extraire les valeurs dans un tableau. Pour faciliter le filtrage, 3 fonctions ont été ajoutées:- open(): ouvre le fichier et lancer l'analyse. Le format log ou compressé gz est accepté. L'utilisation du paramètre CallBack fixe la règle de filtrage,
- getCurrentFile(): retourne le fichier log en cours,
- setParseFormat(): optionnel, modifie la méthode de parsing, lorsque le format est de type long.
<?php
/**
* ApacheLog_Parser : parser le fichier log de Apache
*
* @author Fobec 12/2012
* @copyright http://www.fobec.com/php5/1120/analyser-fichier-logs-apache.html
*/
class ApacheLog_Parser {
const PARSE_FORMAT_CLASSIC='short';
const PARSE_FORMAT_LONG='server';
private $parseFormat=self::PARSE_FORMAT_CLASSIC;
/**
* Analyse le fichier log
* @param String $filename
* @param function $callback
* @return Array
*/
public function open($filename,$callback=NULL) {
if (!is_readable($filename)) {
throw new Exception('File '.$file.' could not be open !!!');
}
if ($callback!=NULL && is_callable($callback)==FALSE) {
throw new Exception('Invalid Callback !!!');
}
$ext=strtolower(strrchr($filename,'.'));
if ($ext=='.gz') {
return $this->openfile_gz($filename,$callback);
} else {
return $this->openfile($filename,$callback);
}
}
/**
* Trouver le fichier log du jour
* @param String $path dossier fichier log
* @return String
*/
public function getCurrentFile($path) {
$curfile=array('date'=>0,'name'>'');
//dossier inaccessible
$dh = opendir($path);
if (!$dh) {
throw new Exception('Path '.$path.' could not be open !!!');
}
while (($file = readdir($dh)) !== false) {
if ($file!='.'&&$file!='..') {
$dt=filemtime($path.$file);
if ($file!='.htaccess' && $dt>$curfile['date'] && strpos($file, 'access')!== false) {
$curfile['date']=$dt;
$curfile['name']=$path.$file;
}
}
}
closedir($dh);
return $curfile['name'];
}
/**
* Fixer le format des lignes Apache
* @param <type> $parseFormat
*/
public function setParseFormat($parseFormat) {
$this->parseFormat=$parseFormat;
}
/*******************************************************************************
* Private
*******************************************************************************/
/**
* Ouvrir fichier non compressé
* @param <type> $filename
* @param <type> $callback
* @return <type>
*/
private function openfile($filename,$callback=NULL) {
$ar=array();
$handle = fopen($filename, 'r');
$n=array();
while (!feof($handle) ) {
$buf = fgets($handle);
$n=$this->parse($buf);
if ($n!=NULL && $callback!=NULL) {
$bool=call_user_func($callback, $n);
} else {
$bool=($n!=NULL);
}
if ($bool) {
$ar[]=$n;
}
}
fclose($handle);
return $ar;
}
/**
* Ouvrir fichier compressé gz
* @param <type> $filename
* @param <type> $callback
* @return <type>
*/
private function openfile_gz($filename,$callback=NULL) {
$ar=array();
$handle = gzopen($filename, 'r');
while (!gzeof($handle)) {
$buf = gzgets($handle, 4096);
$n=$this->parse($buf);
if ($n!=NULL && $callback!=NULL) {
$bool=call_user_func($callback, $n);
} else {
$bool=($n!=NULL);
}
if ($bool) {
$ar[]=$n;
}
}
gzclose($handle);
return $ar;
}
/**
* Parser une ligne
* @param <type> $line
* @return <type>
*/
private function parse($line) {
if ($this->parseFormat==self::PARSE_FORMAT_CLASSIC) {
$n = sscanf(trim($line), '%s %s %s [%[^]]] "%s %s %[^"]" %d %s "%[^"]" "%[^"]"',
$out['ip'],
$out['client'],
$out['user'],
$out['time'],
$out['http_method'],
$out['uri'],
$out['http_prot'],
$out['http_code'],
$out['size'],
$out['referer'],
$out['ua']
);
if ($n==11) {
return $out;
} else {
return NULL;
}
}
else if ($this->parseFormat==self::PARSE_FORMAT_LONG) {
$n = sscanf(trim($line), '%s %s %s [%[^]]] "%s %s %[^"]" %d %s %s "%[^"]" "%[^"]"',
$out['ip'],
$out['client'],
$out['user'],
$out['time'],
$out['http_method'],
$out['uri'],
$out['http_prot'],
$out['http_code'],
$out['size'],
$out['server'],
$out['referer'],
$out['ua']
);
if ($n==12) {
return $out;
} else {
return NULL;
}
}
}
}
?>
Comment afficher l'adresse IP, l'url et le UserAgent
Pour fichier log access.log, le script ci-dessous va extraire et afficher l'adresse IP, l'url demandée et le UserAgent pour chaque ligne.<?php
require 'apachelog_parser.php';
$parser=new ApacheLog_Parser();
$f=$_SERVER['DOCUMENT_ROOT'].'/logs/access.log';
$n=$parser->open($f);
foreach ($n as $row) {
echo $row['ip'].' - '.$row['uri'].' - '.$row['ua'].'<br/>';
}
?>
Comment trouver les erreurs sur son serveur
Dans l'exemple suivant, la fonction filterbyError() est utiliser pour filtrer les lignes. Ne seront retournées que les requêtes ayant générées une erreur. Pour ce faire, la fonction filterbyError() est passée en paramètre à la Class. Contrairement pour chaque ligne, le parser appelle la fonction de comparaison et détermine si la ligne est ajoutée ou non au tableau de sortie.<?php
require 'apachelog_parser.php';
$parser=new ApacheLog_Parser();
$parser->setParseFormat(ApacheLog_Parser::PARSE_FORMAT_LONG);
$f= $parser->getCurrentFile($_SERVER['DOCUMENT_ROOT'].'/logs/');
$n=$parser->open($f,'filterbyError');
foreach ($n as $row) {
echo $row['http_code'].' - '.date("H:m:s",strtotime($row['time'])).' - '.$row['uri'].'<br/>';
}
function filterbyError($rows) {
//200:ok, 301&302:redirect, 304 Not Modified
if (!in_array($rows['http_code'],array('200','301','302','304'))) {
return TRUE;
} else {
return FALSE;
}
}
?>
Comment trouver les pages indexées par Google
Pour déterminer les pages crawlées par Google, la fonction filterByGoogleBot() recherche la mention GoogleBot dans l'UserAgent de la requête. Le script est inclu dans une class pour illustrer l'appel d'un Callback avec dans ce cas.<?php
require 'apachelog_parser.php';
class Sample_Class {
public function show() {
$parser=new ApacheLog_Parser();
$parser->setParseFormat(ApacheLog_Parser::PARSE_FORMAT_LONG);
$f= $parser->getCurrentFile($_SERVER['DOCUMENT_ROOT'].'/logs/');
$n=$parser->open($f,array($this,'filterByGoogleBot'));
foreach ($n as $row) {
echo date("H:m:s",strtotime($row['time'])).' - '.$row['uri'].' - '.$row['ip'].'<br/>';
}
}
public public static function filterByGoogleBot($rows) {
if (stripos($rows['ua'],'Googlebot')!== false) {
return TRUE;
} else {
return FALSE;
}
}
}
$o=new Sample_Class();
$o->show();
?>