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?>