1: <?php
2:
3: /**
4: *
5: * Generic log file class to encapsulate common file I/O and rendering calls
6: *
7: * @package alpha::util
8: * @since 1.0
9: * @author John Collins <dev@alphaframework.org>
10: * @version $Id: LogFile.inc 1624 2012-12-21 12:17:55Z alphadevx $
11: * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
12: * @copyright Copyright (c) 2012, John Collins (founder of Alpha Framework).
13: * All rights reserved.
14: *
15: * <pre>
16: * Redistribution and use in source and binary forms, with or
17: * without modification, are permitted provided that the
18: * following conditions are met:
19: *
20: * * Redistributions of source code must retain the above
21: * copyright notice, this list of conditions and the
22: * following disclaimer.
23: * * Redistributions in binary form must reproduce the above
24: * copyright notice, this list of conditions and the
25: * following disclaimer in the documentation and/or other
26: * materials provided with the distribution.
27: * * Neither the name of the Alpha Framework nor the names
28: * of its contributors may be used to endorse or promote
29: * products derived from this software without specific
30: * prior written permission.
31: *
32: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
33: * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
34: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
35: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36: * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
37: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
39: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
40: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
42: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
43: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
44: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45: * </pre>
46: *
47: */
48: class LogFile {
49: /**
50: * The log file path
51: *
52: * @var string
53: * @since 1.0
54: */
55: private $path;
56:
57: /**
58: * An array of column headers use when rendering the log file
59: *
60: * @var array
61: * @since 1.0
62: */
63: private $columnHeadings;
64:
65: /**
66: * The value seperator to use in the file (default is a pipe)
67: *
68: * @var string
69: * @since 1.0
70: */
71: private $seperator = '|';
72:
73: /**
74: * The maximum size of the log file in megabytes before a backup is created and a
75: * new file is created, default is 5
76: *
77: * @var integer
78: * @since 1.0
79: */
80: private $MaxSize = 5;
81:
82: /**
83: * The constructor
84: *
85: * @param string $path
86: * @since 1.0
87: */
88: public function __construct($path) {
89: $this->path = $path;
90: }
91:
92: /**
93: * Set the max log size in megabytes
94: *
95: * @param integer $MaxSize
96: * @since 1.0
97: */
98: public function setMaxSize($MaxSize) {
99: $this->MaxSize = $MaxSize;
100: }
101:
102: /**
103: * Set the value seperator
104: *
105: * @param string $seperator
106: * @since 1.0
107: */
108: public function setSeperator($seperator) {
109: $this->seperator = $seperator;
110: }
111:
112: /**
113: * Writes a line of data to the log file
114: *
115: * $param array $line
116: * @since 1.0
117: */
118: public function writeLine($line) {
119: global $config;
120:
121: $mergedLine = '';
122:
123: $colCount = count($line);
124:
125: for($i = 0; $i < $colCount; $i++) {
126: /*
127: * we need to ensure that the seperator is not in the value anywhere, as it
128: * would cause problems later when reading the log
129: */
130: $value = str_replace($this->seperator, '', $line[$i]);
131: if ($i == ($colCount-1))
132: $mergedLine .= $value.$this->seperator."\n";
133: else
134: $mergedLine .= $value.$this->seperator;
135: }
136:
137: try{
138: file_put_contents($this->path, $mergedLine, FILE_APPEND|LOCK_EX);
139:
140: if($this->checkFileSize() >= $this->MaxSize) {
141: $this->backupFile();
142: }
143: }catch(Exception $e) {
144: try {
145: $logsDir = $config->get('app.file.store.dir').'logs';
146:
147: if(!file_exists($logsDir))
148: mkdir($logsDir, 0766);
149:
150: file_put_contents($this->path, $mergedLine, FILE_APPEND|LOCK_EX);
151:
152: if($this->checkFileSize() >= $this->MaxSize) {
153: $this->backupFile();
154: }
155: }catch(Exception $e) {
156: echo '<p class="error"><br>Unable to write to the log file ['.$this->path.'], error ['.$e->getMessage().']</p>';
157: exit;
158: }
159: }
160: }
161:
162: /**
163: * Returns the size in megabytes of the log file on disc
164: *
165: * @return float
166: * @since 1.0
167: */
168: private function checkFileSize() {
169: $size = filesize($this->path);
170:
171: return ($size/1024)/1024;
172: }
173:
174: /**
175: * Creates a backup of the log file, which has the same file name and location as the
176: * current file plus a timestamp appended
177: *
178: * @since 1.0
179: */
180: private function backupFile() {
181: // generate the name of the backup file name to contain a timestampe
182: $backName = str_replace('.log', '-backup-'.date("y-m-d H.i.s").'.log', $this->path);
183:
184: // renames the logfile as the value of $backName
185: rename($this->path, $backName);
186: //creates a new log file, and sets it's permission for writting!
187: $fp = fopen($this->path, 'a+'); // remember set directory permissons to allow creation!
188: fclose($fp);
189: //sets the new permission to rw+:rw+:rw+
190: chmod($this->path, 0666);
191: }
192:
193: /**
194: * Renders a log file as a HTML table
195: *
196: * $param array $cols The headings to use when rendering the log file
197: * @since 1.0
198: */
199: public function renderLog($cols) {
200: // render the start of the table
201: echo '<table class="log_file">';
202: echo '<tr>';
203: foreach($cols as $heading)
204: echo '<th>'.$heading.'</th>';
205: echo '</tr>';
206:
207: // now read the file and render the data
208: $LogFile = file_get_contents($this->path);
209: $fields = explode($this->seperator, $LogFile);
210: $totalLines = (count($fields)-1)/count($cols);
211:
212: for($line = 0; $line < $totalLines; $line++) {
213: $count = count($cols);
214:
215: for($col = 0; $col < $count; $col++) {
216: $index = ($line*count($cols))+$col;
217:
218: // if it is an error log, render the error types field in different colours
219: if($col == 1 && $cols[1] == 'Level'){
220: switch($fields[$index]) {
221: case 'DEBUG':
222: echo '<td class="debug"><pre>'.htmlentities($fields[$index]).'</pre></td>';
223: break;
224: case 'INFO':
225: echo '<td class="info"><pre>'.htmlentities($fields[$index]).'</pre></td>';
226: break;
227: case 'WARN':
228: echo '<td class="warn"><pre>'.htmlentities($fields[$index]).'</pre></td>';
229: break;
230: case 'ERROR':
231: echo '<td class="error"><pre>'.htmlentities($fields[$index]).'</pre></td>';
232: break;
233: case 'FATAL':
234: echo '<td class="fatal"><pre>'.htmlentities($fields[$index]).'</pre></td>';
235: break;
236: case 'SQL':
237: echo '<td class="sql"><pre>'.htmlentities($fields[$index]).'</pre></td>';
238: break;
239: default:
240: echo '<td><pre>'.htmlentities($fields[$index]).'</pre></td>';
241: break;
242: }
243: }else{
244: echo '<td><pre>'.htmlentities($fields[$index]).'</pre></td>';
245: }
246: }
247:
248: echo '</tr>';
249: }
250:
251: echo '</table>';
252: }
253: }
254: ?>