Subversion-Projekte lars-tiefland.prado

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * TLogRouter, TLogRoute, TFileLogRoute, TEmailLogRoute class file
4
 *
5
 * @author Qiang Xue <qiang.xue@gmail.com>
6
 * @link http://www.pradosoft.com/
7
 * @copyright Copyright &copy; 2005-2008 PradoSoft
8
 * @license http://www.pradosoft.com/license/
9
 * @version $Id: TLogRouter.php 2563 2008-11-11 10:03:41Z haertl.mike $
10
 * @package System.Util
11
 */
12
 
13
Prado::using('System.Data.TDbConnection');
14
 
15
/**
16
 * TLogRouter class.
17
 *
18
 * TLogRouter manages routes that record log messages in different media different ways.
19
 * For example, a file log route {@link TFileLogRoute} records log messages
20
 * in log files. An email log route {@link TEmailLogRoute} sends log messages
21
 * to email addresses.
22
 *
23
 * Log routes may be configured in application or page folder configuration files
24
 * or an external configuration file specified by {@link setConfigFile ConfigFile}.
25
 * The format is as follows,
26
 * <code>
27
 *   <route class="TFileLogRoute" Categories="System.Web.UI" Levels="Warning" />
28
 *   <route class="TEmailLogRoute" Categories="Application" Levels="Fatal" Emails="admin@pradosoft.com" />
29
 * </code>
30
 * You can specify multiple routes with different filtering conditions and different
31
 * targets, even if the routes are of the same type.
32
 *
33
 * @author Qiang Xue <qiang.xue@gmail.com>
34
 * @version $Id: TLogRouter.php 2563 2008-11-11 10:03:41Z haertl.mike $
35
 * @package System.Util
36
 * @since 3.0
37
 */
38
class TLogRouter extends TModule
39
{
40
	/**
41
	 * File extension of external configuration file
42
	 */
43
	const CONFIG_FILE_EXT='.xml';
44
	/**
45
	 * @var array list of routes available
46
	 */
47
	private $_routes=array();
48
	/**
49
	 * @var string external configuration file
50
	 */
51
	private $_configFile=null;
52
 
53
	/**
54
	 * Initializes this module.
55
	 * This method is required by the IModule interface.
56
	 * @param TXmlElement configuration for this module, can be null
57
	 * @throws TConfigurationException if {@link getConfigFile ConfigFile} is invalid.
58
	 */
59
	public function init($config)
60
	{
61
		if($this->_configFile!==null)
62
		{
63
 			if(is_file($this->_configFile))
64
 			{
65
				$dom=new TXmlDocument;
66
				$dom->loadFromFile($this->_configFile);
67
				$this->loadConfig($dom);
68
			}
69
			else
70
				throw new TConfigurationException('logrouter_configfile_invalid',$this->_configFile);
71
		}
72
		$this->loadConfig($config);
73
		$this->getApplication()->attachEventHandler('OnEndRequest',array($this,'collectLogs'));
74
	}
75
 
76
	/**
77
	 * Loads configuration from an XML element
78
	 * @param TXmlElement configuration node
79
	 * @throws TConfigurationException if log route class or type is not specified
80
	 */
81
	private function loadConfig($xml)
82
	{
83
		foreach($xml->getElementsByTagName('route') as $routeConfig)
84
		{
85
			$properties=$routeConfig->getAttributes();
86
			if(($class=$properties->remove('class'))===null)
87
				throw new TConfigurationException('logrouter_routeclass_required');
88
			$route=Prado::createComponent($class);
89
			if(!($route instanceof TLogRoute))
90
				throw new TConfigurationException('logrouter_routetype_invalid');
91
			foreach($properties as $name=>$value)
92
				$route->setSubproperty($name,$value);
93
			$this->_routes[]=$route;
94
			$route->init($routeConfig);
95
		}
96
	}
97
 
98
	/**
99
	 * Adds a TLogRoute instance to the log router.
100
	 *
101
	 * @param TLogRoute $route
102
	 * @throws TInvalidDataTypeException if the route object is invalid
103
	 */
104
	public function addRoute($route)
105
	{
106
		if(!($route instanceof TLogRoute))
107
			throw new TInvalidDataTypeException('logrouter_routetype_invalid');
108
		$this->_routes[]=$route;
109
		$route->init(null);
110
	}
111
 
112
	/**
113
	 * @return string external configuration file. Defaults to null.
114
	 */
115
	public function getConfigFile()
116
	{
117
		return $this->_configFile;
118
	}
119
 
120
	/**
121
	 * @param string external configuration file in namespace format. The file
122
	 * must be suffixed with '.xml'.
123
	 * @throws TConfigurationException if the file is invalid.
124
	 */
125
	public function setConfigFile($value)
126
	{
127
		if(($this->_configFile=Prado::getPathOfNamespace($value,self::CONFIG_FILE_EXT))===null)
128
			throw new TConfigurationException('logrouter_configfile_invalid',$value);
129
	}
130
 
131
	/**
132
	 * Collects log messages from a logger.
133
	 * This method is an event handler to application's EndRequest event.
134
	 * @param mixed event parameter
135
	 */
136
	public function collectLogs($param)
137
	{
138
		$logger=Prado::getLogger();
139
		foreach($this->_routes as $route)
140
			$route->collectLogs($logger);
141
	}
142
}
143
 
144
/**
145
 * TLogRoute class.
146
 *
147
 * TLogRoute is the base class for all log route classes.
148
 * A log route object retrieves log messages from a logger and sends it
149
 * somewhere, such as files, emails.
150
 * The messages being retrieved may be filtered first before being sent
151
 * to the destination. The filters include log level filter and log category filter.
152
 *
153
 * To specify level filter, set {@link setLevels Levels} property,
154
 * which takes a string of comma-separated desired level names (e.g. 'Error, Debug').
155
 * To specify category filter, set {@link setCategories Categories} property,
156
 * which takes a string of comma-separated desired category names (e.g. 'System.Web, System.IO').
157
 *
158
 * Level filter and category filter are combinational, i.e., only messages
159
 * satisfying both filter conditions will they be returned.
160
 *
161
 * @author Qiang Xue <qiang.xue@gmail.com>
162
 * @version $Id: TLogRouter.php 2563 2008-11-11 10:03:41Z haertl.mike $
163
 * @package System.Util
164
 * @since 3.0
165
 */
166
abstract class TLogRoute extends TApplicationComponent
167
{
168
	/**
169
	 * @var array lookup table for level names
170
	 */
171
	protected static $_levelNames=array(
172
		TLogger::DEBUG=>'Debug',
173
		TLogger::INFO=>'Info',
174
		TLogger::NOTICE=>'Notice',
175
		TLogger::WARNING=>'Warning',
176
		TLogger::ERROR=>'Error',
177
		TLogger::ALERT=>'Alert',
178
		TLogger::FATAL=>'Fatal'
179
	);
180
	/**
181
	 * @var array lookup table for level values
182
	 */
183
	protected static $_levelValues=array(
184
		'debug'=>TLogger::DEBUG,
185
		'info'=>TLogger::INFO,
186
		'notice'=>TLogger::NOTICE,
187
		'warning'=>TLogger::WARNING,
188
		'error'=>TLogger::ERROR,
189
		'alert'=>TLogger::ALERT,
190
		'fatal'=>TLogger::FATAL
191
	);
192
	/**
193
	 * @var integer log level filter (bits)
194
	 */
195
	private $_levels=null;
196
	/**
197
	 * @var array log category filter
198
	 */
199
	private $_categories=null;
200
 
201
	/**
202
	 * Initializes the route.
203
	 * @param TXmlElement configurations specified in {@link TLogRouter}.
204
	 */
205
	public function init($config)
206
	{
207
	}
208
 
209
	/**
210
	 * @return integer log level filter
211
	 */
212
	public function getLevels()
213
	{
214
		return $this->_levels;
215
	}
216
 
217
	/**
218
	 * @param integer|string integer log level filter (in bits). If the value is
219
	 * a string, it is assumed to be comma-separated level names. Valid level names
220
	 * include 'Debug', 'Info', 'Notice', 'Warning', 'Error', 'Alert' and 'Fatal'.
221
	 */
222
	public function setLevels($levels)
223
	{
224
		if(is_integer($levels))
225
			$this->_levels=$levels;
226
		else
227
		{
228
			$this->_levels=null;
229
			$levels=strtolower($levels);
230
			foreach(explode(',',$levels) as $level)
231
			{
232
				$level=trim($level);
233
				if(isset(self::$_levelValues[$level]))
234
					$this->_levels|=self::$_levelValues[$level];
235
			}
236
		}
237
	}
238
 
239
	/**
240
	 * @return array list of categories to be looked for
241
	 */
242
	public function getCategories()
243
	{
244
		return $this->_categories;
245
	}
246
 
247
	/**
248
	 * @param array|string list of categories to be looked for. If the value is a string,
249
	 * it is assumed to be comma-separated category names.
250
	 */
251
	public function setCategories($categories)
252
	{
253
		if(is_array($categories))
254
			$this->_categories=$categories;
255
		else
256
		{
257
			$this->_categories=null;
258
			foreach(explode(',',$categories) as $category)
259
			{
260
				if(($category=trim($category))!=='')
261
					$this->_categories[]=$category;
262
			}
263
		}
264
	}
265
 
266
	/**
267
	 * @param integer level value
268
	 * @return string level name
269
	 */
270
	protected function getLevelName($level)
271
	{
272
		return isset(self::$_levelNames[$level])?self::$_levelNames[$level]:'Unknown';
273
	}
274
 
275
	/**
276
	 * @param string level name
277
	 * @return integer level value
278
	 */
279
	protected function getLevelValue($level)
280
	{
281
		return isset(self::$_levelValues[$level])?self::$_levelValues[$level]:0;
282
	}
283
 
284
	/**
285
	 * Formats a log message given different fields.
286
	 * @param string message content
287
	 * @param integer message level
288
	 * @param string message category
289
	 * @param integer timestamp
290
	 * @return string formatted message
291
	 */
292
	protected function formatLogMessage($message,$level,$category,$time)
293
	{
294
		return @gmdate('M d H:i:s',$time).' ['.$this->getLevelName($level).'] ['.$category.'] '.$message."\n";
295
	}
296
 
297
	/**
298
	 * Retrieves log messages from logger to log route specific destination.
299
	 * @param TLogger logger instance
300
	 */
301
	public function collectLogs(TLogger $logger)
302
	{
303
		$logs=$logger->getLogs($this->getLevels(),$this->getCategories());
304
		if(!empty($logs))
305
			$this->processLogs($logs);
306
	}
307
 
308
	/**
309
	 * Processes log messages and sends them to specific destination.
310
	 * Derived child classes must implement this method.
311
	 * @param array list of messages.  Each array elements represents one message
312
	 * with the following structure:
313
	 * array(
314
	 *   [0] => message
315
	 *   [1] => level
316
	 *   [2] => category
317
	 *   [3] => timestamp);
318
	 */
319
	abstract protected function processLogs($logs);
320
}
321
 
322
/**
323
 * TFileLogRoute class.
324
 *
325
 * TFileLogRoute records log messages in files.
326
 * The log files are stored under {@link setLogPath LogPath} and the file name
327
 * is specified by {@link setLogFile LogFile}. If the size of the log file is
328
 * greater than {@link setMaxFileSize MaxFileSize} (in kilo-bytes), a rotation
329
 * is performed, which renames the current log file by suffixing the file name
330
 * with '.1'. All existing log files are moved backwards one place, i.e., '.2'
331
 * to '.3', '.1' to '.2'. The property {@link setMaxLogFiles MaxLogFiles}
332
 * specifies how many files to be kept.
333
 *
334
 * @author Qiang Xue <qiang.xue@gmail.com>
335
 * @version $Id: TLogRouter.php 2563 2008-11-11 10:03:41Z haertl.mike $
336
 * @package System.Util
337
 * @since 3.0
338
 */
339
class TFileLogRoute extends TLogRoute
340
{
341
	/**
342
	 * @var integer maximum log file size
343
	 */
344
	private $_maxFileSize=512; // in KB
345
	/**
346
	 * @var integer number of log files used for rotation
347
	 */
348
	private $_maxLogFiles=2;
349
	/**
350
	 * @var string directory storing log files
351
	 */
352
	private $_logPath=null;
353
	/**
354
	 * @var string log file name
355
	 */
356
	private $_logFile='prado.log';
357
 
358
	/**
359
	 * @return string directory storing log files. Defaults to application runtime path.
360
	 */
361
	public function getLogPath()
362
	{
363
		if($this->_logPath===null)
364
			$this->_logPath=$this->getApplication()->getRuntimePath();
365
		return $this->_logPath;
366
	}
367
 
368
	/**
369
	 * @param string directory (in namespace format) storing log files.
370
	 * @throws TConfigurationException if log path is invalid
371
	 */
372
	public function setLogPath($value)
373
	{
374
		if(($this->_logPath=Prado::getPathOfNamespace($value))===null || !is_dir($this->_logPath) || !is_writable($this->_logPath))
375
			throw new TConfigurationException('filelogroute_logpath_invalid',$value);
376
	}
377
 
378
	/**
379
	 * @return string log file name. Defaults to 'prado.log'.
380
	 */
381
	public function getLogFile()
382
	{
383
		return $this->_logFile;
384
	}
385
 
386
	/**
387
	 * @param string log file name
388
	 */
389
	public function setLogFile($value)
390
	{
391
		$this->_logFile=$value;
392
	}
393
 
394
	/**
395
	 * @return integer maximum log file size in kilo-bytes (KB). Defaults to 1024 (1MB).
396
	 */
397
	public function getMaxFileSize()
398
	{
399
		return $this->_maxFileSize;
400
	}
401
 
402
	/**
403
	 * @param integer maximum log file size in kilo-bytes (KB).
404
	 * @throws TInvalidDataValueException if the value is smaller than 1.
405
	 */
406
	public function setMaxFileSize($value)
407
	{
408
		$this->_maxFileSize=TPropertyValue::ensureInteger($value);
409
		if($this->_maxFileSize<=0)
410
			throw new TInvalidDataValueException('filelogroute_maxfilesize_invalid');
411
	}
412
 
413
	/**
414
	 * @return integer number of files used for rotation. Defaults to 2.
415
	 */
416
	public function getMaxLogFiles()
417
	{
418
		return $this->_maxLogFiles;
419
	}
420
 
421
	/**
422
	 * @param integer number of files used for rotation.
423
	 */
424
	public function setMaxLogFiles($value)
425
	{
426
		$this->_maxLogFiles=TPropertyValue::ensureInteger($value);
427
		if($this->_maxLogFiles<1)
428
			throw new TInvalidDataValueException('filelogroute_maxlogfiles_invalid');
429
	}
430
 
431
	/**
432
	 * Saves log messages in files.
433
	 * @param array list of log messages
434
	 */
435
	protected function processLogs($logs)
436
	{
437
		$logFile=$this->getLogPath().DIRECTORY_SEPARATOR.$this->getLogFile();
438
		if(@filesize($logFile)>$this->_maxFileSize*1024)
439
			$this->rotateFiles();
440
		foreach($logs as $log)
441
			error_log($this->formatLogMessage($log[0],$log[1],$log[2],$log[3]),3,$logFile);
442
	}
443
 
444
	/**
445
	 * Rotates log files.
446
	 */
447
	protected function rotateFiles()
448
	{
449
		$file=$this->getLogPath().DIRECTORY_SEPARATOR.$this->getLogFile();
450
		for($i=$this->_maxLogFiles;$i>0;--$i)
451
		{
452
			$rotateFile=$file.'.'.$i;
453
			if(is_file($rotateFile))
454
			{
455
				if($i===$this->_maxLogFiles)
456
					unlink($rotateFile);
457
				else
458
					rename($rotateFile,$file.'.'.($i+1));
459
			}
460
		}
461
		if(is_file($file))
462
			rename($file,$file.'.1');
463
	}
464
}
465
 
466
/**
467
 * TEmailLogRoute class.
468
 *
469
 * TEmailLogRoute sends selected log messages to email addresses.
470
 * The target email addresses may be specified via {@link setEmails Emails} property.
471
 * Optionally, you may set the email {@link setSubject Subject} and the
472
 * {@link setSentFrom SentFrom} address.
473
 *
474
 * @author Qiang Xue <qiang.xue@gmail.com>
475
 * @version $Id: TLogRouter.php 2563 2008-11-11 10:03:41Z haertl.mike $
476
 * @package System.Util
477
 * @since 3.0
478
 */
479
class TEmailLogRoute extends TLogRoute
480
{
481
	/**
482
	 * Regex pattern for email address.
483
	 */
484
	const EMAIL_PATTERN='/^([0-9a-zA-Z]+[-._+&])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$/';
485
	/**
486
	 * Default email subject.
487
	 */
488
	const DEFAULT_SUBJECT='Prado Application Log';
489
	/**
490
	 * @var array list of destination email addresses.
491
	 */
492
	private $_emails=array();
493
	/**
494
	 * @var string email subject
495
	 */
496
	private $_subject='';
497
	/**
498
	 * @var string email sent from address
499
	 */
500
	private $_from='';
501
 
502
	/**
503
	 * Initializes the route.
504
	 * @param TXmlElement configurations specified in {@link TLogRouter}.
505
	 * @throws TConfigurationException if {@link getSentFrom SentFrom} is empty and
506
	 * 'sendmail_from' in php.ini is also empty.
507
	 */
508
	public function init($config)
509
	{
510
		if($this->_from==='')
511
			$this->_from=ini_get('sendmail_from');
512
		if($this->_from==='')
513
			throw new TConfigurationException('emaillogroute_sentfrom_required');
514
	}
515
 
516
	/**
517
	 * Sends log messages to specified email addresses.
518
	 * @param array list of log messages
519
	 */
520
	protected function processLogs($logs)
521
	{
522
		$message='';
523
		foreach($logs as $log)
524
			$message.=$this->formatLogMessage($log[0],$log[1],$log[2],$log[3]);
525
		$message=wordwrap($message,70);
526
		$returnPath = ini_get('sendmail_path') ? "Return-Path:{$this->_from}\r\n" : '';
527
		foreach($this->_emails as $email)
528
			mail($email,$this->getSubject(),$message,"From:{$this->_from}\r\n{$returnPath}");
529
 
530
	}
531
 
532
	/**
533
	 * @return array list of destination email addresses
534
	 */
535
	public function getEmails()
536
	{
537
		return $this->_emails;
538
	}
539
 
540
	/**
541
	 * @return array|string list of destination email addresses. If the value is
542
	 * a string, it is assumed to be comma-separated email addresses.
543
	 */
544
	public function setEmails($emails)
545
	{
546
		if(is_array($emails))
547
			$this->_emails=$emails;
548
		else
549
		{
550
			$this->_emails=array();
551
			foreach(explode(',',$emails) as $email)
552
			{
553
				$email=trim($email);
554
				if(preg_match(self::EMAIL_PATTERN,$email))
555
					$this->_emails[]=$email;
556
			}
557
		}
558
	}
559
 
560
	/**
561
	 * @return string email subject. Defaults to TEmailLogRoute::DEFAULT_SUBJECT
562
	 */
563
	public function getSubject()
564
	{
565
		if($this->_subject===null)
566
			$this->_subject=self::DEFAULT_SUBJECT;
567
		return $this->_subject;
568
	}
569
 
570
	/**
571
	 * @param string email subject.
572
	 */
573
	public function setSubject($value)
574
	{
575
		$this->_subject=$value;
576
	}
577
 
578
	/**
579
	 * @return string send from address of the email
580
	 */
581
	public function getSentFrom()
582
	{
583
		return $this->_from;
584
	}
585
 
586
	/**
587
	 * @param string send from address of the email
588
	 */
589
	public function setSentFrom($value)
590
	{
591
		$this->_from=$value;
592
	}
593
}
594
 
595
/**
596
 * TBrowserLogRoute class.
597
 *
598
 * TBrowserLogRoute prints selected log messages in the response.
599
 *
600
 * @author Xiang Wei Zhuo <weizhuo[at]gmail[dot]com>
601
 * @version $Id: TLogRouter.php 2563 2008-11-11 10:03:41Z haertl.mike $
602
 * @package System.Util
603
 * @since 3.0
604
 */
605
class TBrowserLogRoute extends TLogRoute
606
{
607
	public function processLogs($logs)
608
	{
609
		if(empty($logs) || $this->getApplication()->getMode()==='Performance') return;
610
		$first = $logs[0][3];
611
		$even = true;
612
		$response = $this->getApplication()->getResponse();
613
		$response->write($this->renderHeader());
614
		for($i=0,$n=count($logs);$i<$n;++$i)
615
		{
616
			if ($i<$n-1)
617
			{
618
				$timing['delta'] = $logs[$i+1][3] - $logs[$i][3];
619
				$timing['total'] = $logs[$i+1][3] - $first;
620
			}
621
			else
622
			{
623
				$timing['delta'] = '?';
624
				$timing['total'] = $logs[$i][3] - $first;
625
			}
626
			$timing['even'] = !($even = !$even);
627
			$response->write($this->renderMessage($logs[$i],$timing));
628
		}
629
		$response->write($this->renderFooter());
630
	}
631
 
632
	protected function renderHeader()
633
	{
634
		$string = <<<EOD
635
<table cellspacing="0" cellpadding="2" border="0" width="100%">
636
	<tr>
637
		<th style="background-color: black; color:white;" colspan="11">
638
			Application Log
639
		</th>
640
	</tr><tr style="background-color: #ccc;">
641
	    <th>&nbsp;</th>
642
		<th>Category</th><th>Message</th><th>Time Spent (s)</th><th>Cumulated Time Spent (s)</th>
643
	</tr>
644
EOD;
645
		return $string;
646
	}
647
 
648
	protected function renderMessage($log, $info)
649
	{
650
		$bgcolor = $info['even'] ? "#fff" : "#eee";
651
		$total = sprintf('%0.6f', $info['total']);
652
		$delta = sprintf('%0.6f', $info['delta']);
653
		$color = $this->getColorLevel($log[1]);
654
		$msg = preg_replace('/\(line[^\)]+\)$/','',$log[0]); //remove line number info
655
		$msg = THttpUtility::htmlEncode($msg);
656
		$string = <<<EOD
657
	<tr style="background-color: {$bgcolor};">
658
		<td style="border:1px solid silver;background-color: $color;">&nbsp;</td>
659
		<td>{$log[2]}</td>
660
		<td>{$msg}</td>
661
		<td style="text-align:center">{$delta}</td>
662
		<td style="text-align:center">{$total}</td>
663
	</tr>
664
EOD;
665
		return $string;
666
	}
667
 
668
	protected function getColorLevel($level)
669
	{
670
		switch($level)
671
		{
672
			case TLogger::DEBUG: return 'green';
673
			case TLogger::INFO: return 'black';
674
			case TLogger::NOTICE: return '#3333FF';
675
			case TLogger::WARNING: return '#33FFFF';
676
			case TLogger::ERROR: return '#ff9933';
677
			case TLogger::ALERT: return '#ff00ff';
678
			case TLogger::FATAL: return 'red';
679
		}
680
		return '';
681
	}
682
 
683
	protected function renderFooter()
684
	{
685
		$string = "<tr><td colspan=\"11\" style=\"text-align:center; border-top: 1px solid #ccc; padding:0.2em;\">";
686
		foreach(self::$_levelValues as $name => $level)
687
		{
688
			$string .= "<span style=\"color:white;background-color:".$this->getColorLevel($level);
689
			$string .= ";margin: 0.5em;\">".strtoupper($name)."</span>";
690
		}
691
		$string .= "</td></tr></table>";
692
		return $string;
693
	}
694
}
695
 
696
 
697
/**
698
 * TDbLogRoute class
699
 *
700
 * TDbLogRoute stores log messages in a database table.
701
 * To specify the database table, set {@link setConnectionID ConnectionID} to be
702
 * the ID of a {@link TDataSourceConfig} module and {@link setLogTableName LogTableName}.
703
 * If they are not setting, an SQLite3 database named 'sqlite3.log' will be created and used
704
 * under the runtime directory.
705
 *
706
 * By default, the database table name is 'pradolog'. It has the following structure:
707
 * <code>
708
 *	CREATE TABLE pradolog
709
 *  (
710
 *		log_id INTEGER NOT NULL PRIMARY KEY,
711
 *		level INTEGER,
712
 *		category VARCHAR(128),
713
 *		logtime VARCHAR(20),
714
 *		message VARCHAR(255)
715
 *   );
716
 * </code>
717
 *
718
 * @author Qiang Xue <qiang.xue@gmail.com>
719
 * @version $Id: TLogRouter.php 2563 2008-11-11 10:03:41Z haertl.mike $
720
 * @package System.Util
721
 * @since 3.1.2
722
 */
723
class TDbLogRoute extends TLogRoute
724
{
725
	/**
726
	 * @var string the ID of TDataSourceConfig module
727
	 */
728
	private $_connID='';
729
	/**
730
	 * @var TDbConnection the DB connection instance
731
	 */
732
	private $_db;
733
	/**
734
	 * @var string name of the DB log table
735
	 */
736
	private $_logTable='pradolog';
737
	/**
738
	 * @var boolean whether the log DB table should be created automatically
739
	 */
740
	private $_autoCreate=true;
741
 
742
	/**
743
	 * Destructor.
744
	 * Disconnect the db connection.
745
	 */
746
	public function __destruct()
747
	{
748
		if($this->_db!==null)
749
			$this->_db->setActive(false);
750
	}
751
 
752
	/**
753
	 * Initializes this module.
754
	 * This method is required by the IModule interface.
755
	 * It initializes the database for logging purpose.
756
	 * @param TXmlElement configuration for this module, can be null
757
	 * @throws TConfigurationException if the DB table does not exist.
758
	 */
759
	public function init($config)
760
	{
761
		$db=$this->getDbConnection();
762
		$db->setActive(true);
763
 
764
		$sql='SELECT * FROM '.$this->_logTable.' WHERE 0';
765
		try
766
		{
767
			$db->createCommand($sql)->execute();
768
		}
769
		catch(Exception $e)
770
		{
771
			// DB table not exists
772
			if($this->_autoCreate)
773
				$this->createDbTable();
774
			else
775
				throw new TConfigurationException('db_logtable_inexistent',$this->_logTable);
776
		}
777
 
778
		parent::init($config);
779
	}
780
 
781
	/**
782
	 * Stores log messages into database.
783
	 * @param array list of log messages
784
	 */
785
	protected function processLogs($logs)
786
	{
787
		$sql='INSERT INTO '.$this->_logTable.'(level, category, logtime, message) VALUES (:level, :category, :logtime, :message)';
788
		$command=$this->getDbConnection()->createCommand($sql);
789
		foreach($logs as $log)
790
		{
791
			$command->bindValue(':level',$log[0]);
792
			$command->bindValue(':category',$log[1]);
793
			$command->bindValue(':logtime',$log[2]);
794
			$command->bindValue(':message',$log[3]);
795
			$command->execute();
796
		}
797
	}
798
 
799
	/**
800
	 * Creates the DB table for storing log messages.
801
	 */
802
	protected function createDbTable()
803
	{
804
		$sql='CREATE TABLE '.$this->_logTable.' (
805
			log_id INTEGER NOT NULL PRIMARY KEY,
806
			level INTEGER,
807
			category VARCHAR(128),
808
			logtime VARCHAR(20),
809
			message VARCHAR(255))';
810
		$this->getDbConnection()->createCommand($sql)->execute();
811
	}
812
 
813
	/**
814
	 * Creates the DB connection.
815
	 * @param string the module ID for TDataSourceConfig
816
	 * @return TDbConnection the created DB connection
817
	 * @throws TConfigurationException if module ID is invalid or empty
818
	 */
819
	protected function createDbConnection()
820
	{
821
		if($this->_connID!=='')
822
		{
823
			$config=$this->getApplication()->getModule($this->_connID);
824
			if($config instanceof TDataSourceConfig)
825
				return $config->getDbConnection();
826
			else
827
				throw new TConfigurationException('dblogroute_connectionid_invalid',$this->_connID);
828
		}
829
		else
830
		{
831
			$db=new TDbConnection;
832
			// default to SQLite3 database
833
			$dbFile=$this->getApplication()->getRuntimePath().'/sqlite3.log';
834
			$db->setConnectionString('sqlite:'.$dbFile);
835
			return $db;
836
		}
837
	}
838
 
839
	/**
840
	 * @return TDbConnection the DB connection instance
841
	 */
842
	public function getDbConnection()
843
	{
844
		if($this->_db===null)
845
			$this->_db=$this->createDbConnection();
846
		return $this->_db;
847
	}
848
 
849
	/**
850
	 * @return string the ID of a {@link TDataSourceConfig} module. Defaults to empty string, meaning not set.
851
	 */
852
	public function getConnectionID()
853
	{
854
		return $this->_connID;
855
	}
856
 
857
	/**
858
	 * Sets the ID of a TDataSourceConfig module.
859
	 * The datasource module will be used to establish the DB connection for this log route.
860
	 * @param string ID of the {@link TDataSourceConfig} module
861
	 */
862
	public function setConnectionID($value)
863
	{
864
		$this->_connID=$value;
865
	}
866
 
867
	/**
868
	 * @return string the name of the DB table to store log content. Defaults to 'pradolog'.
869
	 * @see setAutoCreateLogTable
870
	 */
871
	public function getLogTableName()
872
	{
873
		return $this->_logTable;
874
	}
875
 
876
	/**
877
	 * Sets the name of the DB table to store log content.
878
	 * Note, if {@link setAutoCreateLogTable AutoCreateLogTable} is false
879
	 * and you want to create the DB table manually by yourself,
880
	 * you need to make sure the DB table is of the following structure:
881
	 * (key CHAR(128) PRIMARY KEY, value BLOB, expire INT)
882
	 * @param string the name of the DB table to store log content
883
	 * @see setAutoCreateLogTable
884
	 */
885
	public function setLogTableName($value)
886
	{
887
		$this->_logTable=$value;
888
	}
889
 
890
	/**
891
	 * @return boolean whether the log DB table should be automatically created if not exists. Defaults to true.
892
	 * @see setAutoCreateLogTable
893
	 */
894
	public function getAutoCreateLogTable()
895
	{
896
		return $this->_autoCreate;
897
	}
898
 
899
	/**
900
	 * @param boolean whether the log DB table should be automatically created if not exists.
901
	 * @see setLogTableName
902
	 */
903
	public function setAutoCreateLogTable($value)
904
	{
905
		$this->_autoCreate=TPropertyValue::ensureBoolean($value);
906
	}
907
 
908
}
909
 
910
/**
911
 * TFirebugLogRoute class.
912
 *
913
 * TFirebugLogRoute prints selected log messages in the firebug log console.
914
 *
915
 * {@link http://www.getfirebug.com/ FireBug Website}
916
 *
917
 * @author Enrico Stahn <mail@enricostahn.com>, Christophe Boulain <Christophe.Boulain@gmail.com>
918
 * @version $Id: TLogRouter.php 2563 2008-11-11 10:03:41Z haertl.mike $
919
 * @package System.Util
920
 * @since 3.1.2
921
 */
922
class TFirebugLogRoute extends TBrowserLogRoute
923
{
924
	protected function renderHeader ()
925
	{
926
		$string = <<<EOD
927
 
928
<script type="text/javascript">
929
/*<![CDATA[*/
930
if (typeof(console) == 'object')
931
{
932
	console.log ("[Cumulated Time] [Time] [Level] [Category] [Message]");
933
 
934
EOD;
935
 
936
		return $string;
937
	}
938
 
939
	protected function renderMessage ($log, $info)
940
	{
941
		$logfunc = $this->getFirebugLoggingFunction($log[1]);
942
		$total = sprintf('%0.6f', $info['total']);
943
		$delta = sprintf('%0.6f', $info['delta']);
944
		$msg = trim($this->formatLogMessage($log[0],$log[1],$log[2],''));
945
		$msg = preg_replace('/\(line[^\)]+\)$/','',$msg); //remove line number info
946
		$msg = "[{$total}] [{$delta}] ".$msg; // Add time spent and cumulated time spent
947
		$string = $logfunc . '(\'' . addslashes($msg) . '\');' . "\n";
948
 
949
		return $string;
950
	}
951
 
952
 
953
	protected function renderFooter ()
954
	{
955
		$string = <<<EOD
956
 
957
}
958
</script>
959
 
960
EOD;
961
 
962
		return $string;
963
	}
964
 
965
	protected function getFirebugLoggingFunction($level)
966
	{
967
		switch ($level)
968
		{
969
			case TLogger::DEBUG:
970
			case TLogger::INFO:
971
			case TLogger::NOTICE:
972
				return 'console.log';
973
			case TLogger::WARNING:
974
				return 'console.warn';
975
			case TLogger::ERROR:
976
			case TLogger::ALERT:
977
			case TLogger::FATAL:
978
				return 'console.error';
979
		}
980
		return 'console.log';
981
	}
982
 
983
}
984
?>