1<?php 2 3/* 4 * This file is part of pgFouine. 5 * 6 * pgFouine - a PostgreSQL log analyzer 7 * Copyright (c) 2005-2008 Guillaume Smet 8 * 9 * pgFouine is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or (at 12 * your option) any later version. 13 * 14 * pgFouine is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with pgFouine; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. 22 */ 23 24require_once('lib/common.lib.php'); 25require_once('base.lib.php'); 26 27class GenericLogReader { 28 var $displayHelp = true; 29 30 var $fileName; 31 var $lineParserName; 32 var $accumulatorName; 33 34 var $lineParsedCounter = 0; 35 var $timeToParse; 36 37 var $firstLineTimestamp; 38 var $lastLineTimestamp; 39 40 var $reportAggregators = array(); 41 var $listeners = array(); 42 43 function GenericLogReader($fileName, $lineParserName, $accumulatorName, $displayHelp = true) { 44 $this->fileName = $fileName; 45 $this->lineParserName = $lineParserName; 46 $this->accumulatorName = $accumulatorName; 47 48 $this->displayHelp = $displayHelp; 49 } 50 51 function addReportAggregator(& $reportAggregator) { 52 $this->reportAggregators[] =& $reportAggregator; 53 } 54 55 function & getReportAggregators() { 56 return $this->reportAggregators; 57 } 58 59 function parse() { 60 global $lineParsedCounter; 61 62 $this->prepare(); 63 64 $startTimestamp = time(); 65 66 $accumulator = new $this->accumulatorName; 67 $lineParser = new $this->lineParserName; 68 69 foreach(array_keys($this->listeners) AS $listenerName) { 70 $listener =& $this->listeners[$listenerName]; 71 foreach($listener->getSubscriptions() AS $eventType) { 72 $accumulator->addListener($eventType, $listener); 73 } 74 } 75 76 if(DEBUG) { 77 debug('Using parser: '.$this->lineParserName); 78 debug('Using accumulator: '.$this->accumulatorName); 79 debug('Using listeners: '.implode(', ', array_keys($this->listeners))); 80 } 81 82 $filePointer = @fopen($this->fileName, 'r'); 83 if(!$filePointer) { 84 trigger_error('File '.$this->fileName.' is not readable.', E_USER_ERROR); 85 } 86 87 $lineParsedCounter = 0; 88 $lineDetected = false; 89 90 if(DEBUG) debug(getMemoryUsage()); 91 if(PROFILE) { 92 $GLOBALS['profiler'] = new Profiler(); 93 $GLOBALS['profiler']->start(); 94 } 95 96 $this->readFile($accumulator, $filePointer, $lineParser, $lineParsedCounter, $lineDetected); 97 98 DEBUG && debug('Before close - '.getMemoryUsage()); 99 $accumulator->close(); 100 DEBUG && debug('After close - '.getMemoryUsage()); 101 102 fclose($filePointer); 103 104 $this->timeToParse = time() - $startTimestamp; 105 $this->lineParsedCounter = $lineParsedCounter; 106 107 DEBUG && debug("\nParsed ".$lineParsedCounter.' lines in '.$this->timeToParse.' s'); 108 109 if(PROFILE) { 110 $GLOBALS['profiler']->end(); 111 $GLOBALS['profiler']->displayProfile(); 112 } 113 114 if(!$lineParsedCounter) { 115 stderr('Log file is empty.'); 116 exit(0); 117 } 118 119 if(!$lineDetected && $this->displayHelp) { 120 stderr('pgFouine did not find any valid PostgreSQL log line in your log file:'); 121 stderr('* check that PostgreSQL uses an english locale for logging (lc_messages in your postgresql.conf),'); 122 stderr('* check that you use the -logtype option (syslog, stderr) according to your log file,'); 123 stderr('* if you use syslog and log_line_prefix, check that your log_line_prefix has a trailing space,'); 124 stderr('* if you use stderr, check that your log_line_prefix is of the form \'%t [%p]: [%l-1] \'.'); 125 stderr('If you think your log file and your options are correct, please contact the author (gsmet on #postgresql@freenode or guillaume-pg at smet dot org).'); 126 exit(1); 127 } 128 } 129 130 function readFile(& $accumulator, & $filePointer, &$lineParser, &$lineParsedCounter, &$lineDetected) { 131 $currentTimestamp = time(); 132 133 while (!feof($filePointer)) { 134 $text = rtrim(fgets($filePointer), "\r\n"); 135 if(empty($text)) { 136 continue; 137 } 138 $lineParsedCounter ++; 139 140 $line =& $lineParser->parse($text); 141 if($line) { 142 if(!isset($this->firstLineTimestamp)) { 143 $this->firstLineTimestamp = $line->getTimestamp(); 144 } 145 $this->lastLineTimestamp = $line->getTimestamp(); 146 $accumulator->append($line); 147 148 if(!is_a($line, 'PostgreSQLContinuationLine')) { 149 $lineDetected = true; 150 } 151 } 152 if($lineParsedCounter % 20000 == 0 && isset($this->lastLineTimestamp)) { 153 if(DEBUG) { 154 debug(' Garbage collector:'); 155 debug(' before: '.getMemoryUsage()); 156 } 157 $accumulator->garbageCollect($this->lastLineTimestamp); 158 if(DEBUG) { 159 debug(' after: '.getMemoryUsage()); 160 } 161 } 162 if($lineParsedCounter % 100000 == 0) { 163 stderr('parsed '.$lineParsedCounter.' lines'); 164 if(DEBUG) { 165 $currentTime = time() - $currentTimestamp; 166 $currentTimestamp = time(); 167 debug(' '.getMemoryUsage()); 168 debug(' Time: '.$currentTime.' s'); 169 } 170 } 171 } 172 } 173 174 function output() { 175 for($i = 0; $i < count($this->reportAggregators); $i++) { 176 $this->reportAggregators[$i]->output(); 177 } 178 } 179 180 function prepare() { 181 $needs = array(); 182 183 for($i = 0; $i < count($this->reportAggregators); $i++) { 184 $needs = array_merge($needs, $this->reportAggregators[$i]->getNeeds()); 185 } 186 $needs = array_unique($needs); 187 foreach($needs AS $need) { 188 $this->addListener($need); 189 } 190 } 191 192 function getLineParsedCounter() { 193 return $this->lineParsedCounter; 194 } 195 196 function addListener($listenerName) { 197 $listener = new $listenerName(); 198 $this->listeners[$listenerName] =& $listener; 199 } 200 201 function & getListener($listenerName) { 202 if(isset($this->listeners[$listenerName])) { 203 $listener =& $this->listeners[$listenerName]; 204 } else { 205 $listener = false; 206 } 207 return $listener; 208 } 209 210 function getFileName() { 211 return $this->fileName; 212 } 213 214 function getTimeToParse() { 215 return $this->timeToParse; 216 } 217 218 function getLineParsedCount() { 219 return $this->lineParsedCounter; 220 } 221 222 function getFirstLineTimestamp() { 223 return $this->firstLineTimestamp; 224 } 225 226 function getLastLineTimestamp() { 227 return $this->lastLineTimestamp; 228 } 229} 230 231?>