PageRenderTime 47ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/atk4/lib/Logger.php

https://github.com/mahimarathore/mahi
PHP | 735 lines | 496 code | 62 blank | 177 comment | 86 complexity | 3a42ed17d816a895247cb44cadac50cf MD5 | raw file
Possible License(s): AGPL-3.0, MPL-2.0-no-copyleft-exception
  1. <?php // vim:ts=4:sw=4:et:fdm=marker
  2. /*
  3. * Undocumented
  4. *
  5. * @link http://agiletoolkit.org/
  6. *//*
  7. ==ATK4===================================================
  8. This file is part of Agile Toolkit 4
  9. http://agiletoolkit.org/
  10. (c) 2008-2013 Agile Toolkit Limited <info@agiletoolkit.org>
  11. Distributed under Affero General Public License v3 and
  12. commercial license.
  13. See LICENSE or LICENSE_COM for more information
  14. =====================================================ATK4=*/
  15. class Logger extends AbstractController {
  16. /**
  17. * Logger class is implemented a more sophisticated and usable error handling.
  18. *
  19. * Normally all error messages are sent through the API class by using one of two
  20. * ways:
  21. *
  22. * 1. throwing exception, which is caught by Api class (unless you catch it yourself)
  23. * 2. calling $this->fatal() which would throw exception for you (useful when calling
  24. * from PHP4 compatible components)
  25. *
  26. * additionally there are way to pass info and warnings by calling:
  27. *
  28. * $this->warning();
  29. * $this->info();
  30. * $this->debug();
  31. *
  32. * Debug information will reach logs only if debug mode is set.
  33. *
  34. * ==[ Controling debug info ]===================================================
  35. *
  36. * AModules3 support two ways to set debug mode. Global and Local.
  37. *
  38. * Global mode is set by setting $api->debug to true. Local mode is set by setting
  39. * particular object's $this->debug to true.
  40. *
  41. * In local mode only debug info generated by particular object will be sent
  42. * through
  43. *
  44. * This class does not manage debug modes, which is a task for Api and all other
  45. * objects.
  46. *
  47. * $config['debug']=true; // turns on global debug mode.
  48. *
  49. *
  50. * ==[ Severity ]================================================================
  51. *
  52. * AModules3 have 4 severity types: debug, info, warning and fatal.
  53. *
  54. * Expected exceptions might be reported as warnings and uncaught exceptions
  55. * are fatal. Forms have their own validation and errors on the form is
  56. * completely separate system.
  57. *
  58. * Sample fatal errors:
  59. * - unable to execute query
  60. * - method not found
  61. * - mandatory data is not specified
  62. * fatal errors will automatically terminate application execution.
  63. *
  64. * Warnings messages:
  65. * - duplicate child added to the object with same name
  66. * - method is called without specifying important argument
  67. *
  68. * Sample info messages:
  69. * - user tried to access restricted page
  70. * - monthly log rotation routine finished successfuly
  71. * - user information is automatically converted (from format used in previous version)
  72. * Info messages are passed as strings without extended information.
  73. *
  74. * Debug messages
  75. * - called some function which might contain errors or being debuged
  76. * - user logged in or logged out
  77. * - when debuging, even more messages might be sent as debug()
  78. * WARNING: please keep debug information to the minimum. Debug information is
  79. * accompanied with extended information. Debug information is always saved into
  80. * logs or is available on the screen.
  81. *
  82. * AModules3 core tries not to produce any debug or info messages to keep files
  83. * clean. Even if it does, those calls might eventually be cleaned out.
  84. *
  85. *
  86. * ==[ Configuring logger ]======================================================
  87. * Logger uses 2 output destinations, and 3 ways to restrict output information.
  88. *
  89. * Output destination: stdout (webpage), logs
  90. * Restrict options: 'full', 'light', null
  91. *
  92. * stdout is different for ApiCLI and ApiWeb classes. Web output might contact
  93. * tags or even some AJAX elements. Logs and ApiCLI uses the same output
  94. * format.
  95. *
  96. * Web output for info, warning and debug messages relies on templates
  97. * but fatal messages are template independent.
  98. *
  99. * ==[ Output restriction ]======================================================
  100. * null: this option will surpress all output. When used with logs, you won't
  101. * even need an empty directory. Web output will be clean of any messages.
  102. *
  103. * If fatal error occurs, if 'null' is used with web output, you will see
  104. * message (public_error_message), instead of the actual error.
  105. *
  106. *
  107. * light: only message is outputed. Even if debug mode is on, backtraces and
  108. * additional information is stripped off. This method is best if you are using
  109. * application for intranet and know uses or if you are doing beta testing.
  110. * This output won't contain any sensitive information such as table names, field
  111. * names, actual data)
  112. *
  113. * when used with logs, each message takes one line.
  114. *
  115. * full: this will output all the information available including:
  116. * error message
  117. * line/file/function where error message occured (guessed)
  118. * additional information (such as last_query and error_message from DBlite)
  119. * complete backtrace.
  120. *
  121. * when used with logs, each message takes several lines.
  122. *
  123. * ==[ Activation ]==================================================================
  124. * To start using this class in your applicaion you should:
  125. *
  126. * $api->add('Logger');
  127. *
  128. * If you do not activate Logger, output will be similar to:
  129. * web_output='full';
  130. * log_output=null;
  131. *
  132. * ==[ Extending ]====================================================================
  133. *
  134. * You can extend this class to add additional features. Please notify me if you think
  135. * something essential is missing out
  136. *
  137. * romans@adevel.com
  138. *
  139. * Debug functions were contributed my mvs@adevel.com
  140. */
  141. // AModules3 compatibility
  142. public $owner;
  143. public $api;
  144. // Configuration;
  145. public $web_output='full'; // $config['logger']['web_output']
  146. public $log_output=null; // $config['logger']['log_output']
  147. public $public_error_message=null;
  148. // This message will be outputed to user in case of
  149. // fatal error. When running in production mode, you
  150. // shouldn't show any debug info to user, but log them
  151. // instead
  152. public $log_dir; // Directory where logs are created. It should be
  153. // used solely by AModules3. If not set, then
  154. // /var/log/atk4/<realm> will be used.
  155. //
  156. // You can change in $config['logger']['log_dir']
  157. protected $log_error_file; // File we are currently logging errors to
  158. protected $log_debug_file; // File we are currently logging errors to
  159. protected $log_info_file; // File we are currently logging errors to
  160. public $details=array();
  161. private $html_stdout=false;
  162. private $header_sent=0;
  163. private $debug_log=''; // Will be outputed at the end of the page
  164. private $debug_added=false; // no debug messages added yet
  165. function init(){
  166. parent::init();
  167. $this->debug_log=session_id()?$this->recall('debug_log',''):'';
  168. if(session_id())$this->forget('debug_log');
  169. $this->debug_log.="[<font color=red>Debug log from ".date("d.m.Y H:m:s")." to ".$_SERVER['QUERY_STRING']."</font>] - debug started<br>\n";
  170. $this->debug_added=false;
  171. register_shutdown_function(array($this,'showDebugInfo'));
  172. $this->log_output=$this->api->getConfig('logger/log_output',null);
  173. $this->web_output=$this->api->getConfig('logger/web_output','full');
  174. if(!$this->web_output){
  175. $this->public_error_message=$this->api
  176. ->getConfig('debug_public_error_message',
  177. 'We are currently having some technical difficulties. '.
  178. 'Please retry later.');
  179. }
  180. $this->log_dir=$this->api->getConfig('logger/log_dir',
  181. "/var/log/atk4/".$this->api->name);
  182. if($this->log_output){
  183. $this->openLogFile('error');
  184. $this->openLogFile('debug');
  185. $this->openLogFile('info');
  186. if(rand(1,50)==1)$this->cleanupLogDirectory();
  187. }
  188. if($this->log_output=='full'||$this->web_output=='full'){
  189. // Full logging will require some preparations
  190. $this->gatherDetails();
  191. }
  192. if($this->api instanceof ApiWeb){
  193. $this->html_stdout=true;
  194. }
  195. $this->api->addHook('caught-exception',array($this,'caughtException'));
  196. $this->api->addHook('output-fatal',array($this,'outputFatal'));
  197. $this->api->addHook('output-warning',array($this,'outputWarning'));
  198. $this->api->addHook('output-info',array($this,'outputInfo'));
  199. $this->api->addHook('output-debug',array($this,'outputDebug'));
  200. $this->api->debug('Logger is initialized');
  201. }
  202. function showDebugInfo(){
  203. if(!$this->debug_added)return;
  204. if(@$this->api->not_html){
  205. // We may not output anything, because this will screw up. Save debug output to session
  206. if(session_id())$this->memorize('debug_log',$this->debug_log);
  207. }else{
  208. echo $this->debug_log;
  209. }
  210. }
  211. function gatherDetails(){
  212. // Get IP address
  213. if (isset($_SERVER['REMOTE_ADDR'])) {
  214. //FIXME: generates warning - array_shift wants variable
  215. //$this->details['IP Address']=(isset($_SERVER["HTTP_X_FORWARDED_FOR"]) ? array_shift(explode(',', $_SERVER["HTTP_X_FORWARDED_FOR"])) : $_SERVER["REMOTE_ADDR"]);
  216. }
  217. if (isset($_SERVER['QUERY_STRING'])) {
  218. $this->details['Query String']=$_SERVER['QUERY_STRING'];
  219. }
  220. if (isset($_SERVER['REDIRECT_SCRIPT_URI'])) {
  221. $this->details['Original Query']=$_SERVER['REDIRECT_SCRIPT_URI'];
  222. }
  223. if (isset($_SERVER['HTTP_REFERER'])) {
  224. $this->details['Referer']=$_SERVER['HTTP_REFERER'];
  225. }
  226. if (isset($_SERVER['HTTP_USER_AGENT'])) {
  227. $this->details['Version']=$_SERVER['HTTP_USER_AGENT'];
  228. }
  229. if (isset($_SERVER['SERVER_PORT'])) {
  230. $this->details['Port']=$_SERVER['SERVER_PORT'];
  231. }
  232. }
  233. function findFrame($function_name,$shift=0){
  234. $backtrace=debug_backtrace();
  235. while($bt=array_shift($backtrace)){
  236. if($bt['function']==$function_name){
  237. while($shift--)$bt=array_shift($backtrace);
  238. return $bt;
  239. }
  240. }
  241. return array();
  242. }
  243. public $recskip=array();
  244. function showRenderTree($e,$obj){
  245. if(in_array($obj->name,$this->recskip)){
  246. echo '..recursion('.$obj.')';
  247. return;
  248. };
  249. $this->recskip[]=$obj;
  250. if($e->owner==$obj || $e->owner->owner == $obj || $e->owner->owner->owner == $obj){
  251. echo '<font color="red">'.$obj->__toString()."</font>";
  252. }else echo $obj->__toString();
  253. if($obj->elements){
  254. echo '<ul>';
  255. foreach($obj->elements as $name=>$object){
  256. echo '<li>'.$name.': ';
  257. $this->showRenderTree($e,$object);
  258. echo '</li>';
  259. }
  260. echo '</ul>';
  261. }
  262. }
  263. function logCaughtException($e){
  264. if(!$this->log_output)return;
  265. if(method_exists($e,'getMyTrace'))$trace=$e->getMyTrace();
  266. else $trace=$e->getTrace();
  267. $frame=$e->my_backtrace[$e->shift];
  268. $this->logLine($this->txtLine(get_class($e).": ".$e->getMessage(),$frame),2,'error',$trace);
  269. if(method_exists($e,'getAdditionalMessage'))
  270. $this->logLine($e->getAdditionalMessage(),2,'error');
  271. if($e->more_info){
  272. $this->logLine("Additional information:\n".
  273. $this->print_r(
  274. $e->more_info,'','','* ',"\n",' '),2,'error');
  275. }
  276. }
  277. function caughtException($caller,$e){
  278. $this->logCaughtException($e);
  279. if(!$this->web_output){
  280. echo $this->public_error_message;
  281. exit;
  282. }
  283. if($_GET[$this->name.'_debug']=='rendertree'){
  284. echo '<h2>Object Tree</h2>';
  285. try{
  286. $this->showRenderTree($e,$this->api);
  287. }catch(Exception $e){
  288. echo '<h1>Exception while trying to render tree:</h1>';
  289. //unset($_GET[$htis->name.'_debug']);
  290. //$this->api->caughtException($e);
  291. }
  292. }
  293. echo "<h2>Application Error: ". htmlspecialchars($e->getMessage()) ."</h2>\n";
  294. echo '<p><font color=red>' . get_class($e) . ', code: '.$e->getCode().'</font></p>';
  295. if($e->more_info){
  296. echo '<p>Additional information:';
  297. echo $this->print_r($e->more_info,'<ul>','</ul>','<li>','</li>',' ');
  298. echo '</p>';
  299. }
  300. if($e->actions){
  301. echo '<p>Possible Actions: <ul>';
  302. foreach($e->actions as $key=>$val){
  303. echo '<li><a href="'.$this->api->url(null,$val).'">'.$key.'</a></li>';
  304. }
  305. echo '</ul></p>';
  306. }
  307. if(method_exists($e,'getMyFile'))echo '<p><font color=blue>' . $e->getMyFile() . ':' . $e->getMyLine() . '</font></p>';
  308. if(method_exists($e,'getMyTrace'))echo $this->backtrace(3,$e->getMyTrace());
  309. else echo $this->backtrace($e->shift,$e->getTrace());
  310. echo "<p>Note: To hide this information from your users, add \$config['logger']['web_output']=false to your config.php file. Refer to documentation on 'Logger' for alternative logging options</p>";
  311. exit;
  312. }
  313. function print_r($key,$gs,$ge,$ls,$le,$ind=' '){
  314. $o='';
  315. if(strlen($ind)>3)return;
  316. if(is_array($key)){
  317. $o=$gs;
  318. foreach($key as $a=>$b){
  319. $o.= $ind.$ls.$a.': '.$this->print_r($b,$gs,$ge,$ls,$le,$ind.' ').$le;
  320. }
  321. $o.=$ge;
  322. }else{
  323. $o.=$gs?htmlspecialchars($key):$key;
  324. }
  325. return $o;
  326. }
  327. function outputWarning($caller,$msg,$shift=0){
  328. // first, let's see if we should log this
  329. $frame=$this->findFrame('warning',$shift);
  330. if($this->log_output){
  331. $this->logLine($this->txtLine("warning: $msg",$frame),'warning','error');
  332. }
  333. if(!$this->web_output){
  334. return true;
  335. }else{
  336. echo $this->html_stdout?
  337. $this->htmlLine("$msg",$frame,'warning'):
  338. $this->txtLine("$msg",$frame,'warning');
  339. return true;
  340. }
  341. }
  342. function outputDebug($caller,$msg,$shift=0){
  343. // first, let's see if we should log this
  344. $frame=$this->findFrame('debug');
  345. if($this->log_output){
  346. $this->logLine($this->txtLine("info: $msg",$frame),'fatal','debug');
  347. }
  348. if(!$this->web_output){
  349. return true;
  350. }else{
  351. $this->debug_added=true;
  352. $this->debug_log.=$this->html_stdout?
  353. $this->htmlLine("$msg",$frame,'debug'):
  354. $this->txtLine("$msg",$frame,'debug');
  355. return true;
  356. }
  357. }
  358. function outputInfo($caller,$msg,$shift=0,$nohtml=false){
  359. if($this->log_output){
  360. $this->logLine($this->txtLine("info: $msg"),null,'info');
  361. }
  362. if($this->web_output && !$nohtml){
  363. echo $this->html_stdout?
  364. $this->htmlLine("$msg",null,'info'):
  365. $this->txtLine("$msg",null,'info');
  366. }
  367. return true;
  368. }
  369. function outputFatal($caller,$msg,$shift=0){
  370. // first, let's see if we should log this
  371. $frame=$this->findFrame('fatal');
  372. if($this->log_output){
  373. $this->logLine($this->txtLine("fatal: $msg",$frame),'fatal','error');
  374. }
  375. if(!$this->web_output){
  376. echo $this->public_error_message;
  377. }else{
  378. if($this->html_stdout){
  379. echo "<h2>Fatal error</h2>\n";
  380. }
  381. echo $this->html_stdout?
  382. $this->htmlLine("$msg",$frame,'fatal'):
  383. $this->txtLine("$msg",$frame,'fatal');
  384. if($this->html_stdout){
  385. echo $this->backtrace('fatal');
  386. }else{
  387. echo "Stack trace:\n";
  388. echo $this->txtBacktrace('fatal');
  389. }
  390. }
  391. exit;
  392. }
  393. function htmlLine($msg,$frame=array(),$prefix=null){
  394. if(!$frame){
  395. return "<font style='font-family: verdana; font-size:10px'><font color=blue>warning: </font> <font color=red><b>$msg</b></font></font><br>";
  396. }else{
  397. $errfile=dirname($frame['file']).'/<b>'.basename($frame['file']).'</b>';
  398. return "<font style='font-family: verdana; font-size:10px'><font color=blue>$errfile:".$frame['line']."</font> <font color=red>".($prefix?"$prefix: ":"")."<b>$msg</b></font></font><br>";
  399. }
  400. }
  401. function txtLine($msg,$frame=array(),$prefix=null){
  402. if(!$frame){
  403. return "$prefix: $msg\n";
  404. }else{
  405. return basename($frame['file'])." on line ".$frame['line'].', path: '.dirname($frame['file'])."\n\n".
  406. ($prefix?"$prefix: ":"")."$msg\n\n";
  407. }
  408. }
  409. function logLine($msg,$shiftfunc=null,$severity='info',$trace=null){
  410. $log_file='log_'.$severity.'_file';
  411. if(!isset($this->$log_file))$this->openLogFile($severity);
  412. if($this->log_output==='full' && $severity=='error'){
  413. if(!$this->header_sent++){
  414. fputs($this->$log_file,"\n\n".
  415. "============================================================\n".
  416. "$msg".
  417. "------------------------------------------------------------\n".
  418. "Date: ".date("d-M-Y H:i:s")."\n");
  419. foreach($this->details as $key=>$val){
  420. fputs($this->$log_file,"$key: $val\n");
  421. }
  422. fputs($this->$log_file,
  423. "------------------------------------------------------------\n".
  424. " Stack trace\n".
  425. $this->txtBacktrace($shiftfunc,$trace).
  426. "\n"
  427. );
  428. }else{
  429. fputs($this->$log_file,$msg);
  430. }
  431. }elseif($this->log_output){
  432. fputs($this->$log_file,"[".date("d-M-Y H:i:s")."] $msg");
  433. }else{
  434. return;
  435. }
  436. fflush($this->$log_file);
  437. }
  438. function logVar($var,$msg="",$shiftfunc=null,$severity='debug'){
  439. //creating an $msg from variable
  440. $msg.="(".gettype($var)."):";
  441. if(is_array($var)||is_object($var))$msg .= print_r($var, true);
  442. else$msg .= $var;
  443. $this->logLine($msg."\n", $shiftfunc,$severity);
  444. }
  445. function logException($e){
  446. // logs exception from the catch statement
  447. // contains code from Logger::caughtException(), as this code won't launch
  448. // if exception is caught
  449. $frame=$e->my_backtrace[$e->shift-1];
  450. $this->logLine($this->txtLine(get_class($e).": (".$e->getCode().") ".$e->getMessage(),$frame),2,'error');
  451. return $this;
  452. }
  453. function openLogFile($severity='error'){
  454. if(!is_dir($this->log_dir)){
  455. // Directory is not writable, let's first try to create it
  456. if(!mkdir($this->log_dir,0750)){
  457. throw new BaseException("Unable to create $this->log_dir for log output");
  458. }
  459. }
  460. $filename='am3_'.$severity.'_log';
  461. $full_filename=$this->log_dir.DIRECTORY_SEPARATOR.$filename;
  462. if(!is_writable($full_filename) && !is_writable($this->log_dir))throw new BaseException("Log file is not writable and seems I won't be able to create it: $full_filename");
  463. if(is_link($full_filename))throw new BaseException("Log file is a symlink. Are you trying to make me overwrite somethingn?");
  464. ini_set($severity.'_log',$full_filename);
  465. //$full_filename=tempnam($this->log_dir,$filename);
  466. $new_file = (file_exists($full_filename))?false:true;
  467. $log_file="log_$severity"."_file";
  468. $this->$log_file=fopen($full_filename,"a");
  469. if(!$this->$log_file)throw new IOException("Cannot open $severity log file");
  470. if ($new_file) chmod($full_filename,0777); //
  471. }
  472. function writeLogMessage(){
  473. }
  474. function backtrace($sh=null,$backtrace=null){
  475. $output = "<div >\n";
  476. // TODO: allow extending backtrace option, so that
  477. $output .= "<b>Stack trace:</b><br /><table style='border: 1px solid black; padding: 3px; text-align: left; font-family: verdana; font-size: 10px' width=100% cellspacing=0 cellpadding=0 border=0>\n";
  478. $output .= "<tr><th align='right'>File</th><th>&nbsp;</th><th>Object Name</th><th>Stack Trace</th></tr>";
  479. if(!isset($backtrace)) $backtrace=debug_backtrace();
  480. $n=0;
  481. foreach($backtrace as $bt){
  482. $n++;
  483. $args = '';
  484. if(!isset($bt['args']))continue;
  485. foreach($bt['args'] as $a){
  486. if(!empty($args)){
  487. $args .= ', ';
  488. }
  489. switch (gettype($a)) {
  490. case 'integer':
  491. case 'double':
  492. $args .= $a;
  493. break;
  494. case 'string':
  495. $a = htmlspecialchars(substr($a, 0, 128)).((strlen($a) > 128) ? '...' : '');
  496. $args .= "\"$a\"";
  497. break;
  498. case 'array':
  499. $args .= "Array(".count($a).")";
  500. break;
  501. case 'object':
  502. $args .= "Object(".get_class($a).")";
  503. break;
  504. case 'resource':
  505. $args .= "Resource(".strstr($a, '#').")";
  506. break;
  507. case 'boolean':
  508. $args .= $a ? 'True' : 'False';
  509. break;
  510. case 'NULL':
  511. $args .= 'Null';
  512. break;
  513. default:
  514. $args .= 'Unknown';
  515. }
  516. }
  517. if(($sh==null && strpos($bt['file'],'/atk4/lib/')===false) || (!is_int($sh) && $bt['function']==$sh)){
  518. $sh=$n;
  519. }
  520. $output .= "<tr><td valign=top align=right><font color=".($sh==$n?'red':'blue').">".htmlspecialchars(dirname($bt['file']))."/".
  521. "<b>".htmlspecialchars(basename($bt['file']))."</b></font></td>";
  522. $output .= "<td valign=top nowrap><font color=".($sh==$n?'red':'blue').">:{$bt['line']}</font>&nbsp;</td>";
  523. $name=(!isset($bt['object']->name))?get_class($bt['object']):$bt['object']->name;
  524. if($bt['object'])$output .= "<td>".$name."</td>";else $output.="<td></td>";
  525. $output .= "<td valign=top><font color=".($sh==$n?'red':'green').">".get_class($bt['object'])."{$bt['type']}<b>{$bt['function']}</b>($args)</font></td></tr>\n";
  526. }
  527. $output .= "</table></div>\n";
  528. return $output;
  529. }
  530. function cleanupLogDirectory(){
  531. // we should try to take care of our own log file cleanup
  532. }
  533. function txtBacktrace($sh=null,$backtrace=null){
  534. if(!isset($backtrace)) $backtrace=debug_backtrace();
  535. $output='';
  536. $n=0;
  537. foreach($backtrace as $bt){
  538. $n++;
  539. $args = '';
  540. if(!isset($bt['args']))$bt['args']=array();
  541. foreach($bt['args'] as $a){
  542. if(!empty($args)){
  543. $args .= ', ';
  544. }
  545. switch (gettype($a)) {
  546. case 'integer':
  547. case 'double':
  548. $args .= $a;
  549. break;
  550. case 'string':
  551. $a = (substr($a, 0, 128)).((strlen($a) > 128) ? '...' : '');
  552. $args .= "\"$a\"";
  553. break;
  554. case 'array':
  555. $args .= "Array(".count($a).")";
  556. break;
  557. case 'object':
  558. $args .= "Object(".get_class($a).")";
  559. break;
  560. case 'resource':
  561. $args .= "Resource(".strstr($a, '#').")";
  562. break;
  563. case 'boolean':
  564. $args .= $a ? 'True' : 'False';
  565. break;
  566. case 'NULL':
  567. $args .= 'Null';
  568. break;
  569. default:
  570. $args .= 'Unknown';
  571. }
  572. }
  573. if($sh){
  574. if(is_int($sh)){
  575. if($sh>0){
  576. $sh--;
  577. continue;
  578. }
  579. }elseif($bt['function']!=$sh){
  580. $sh=null;
  581. continue;
  582. }
  583. }
  584. $output .= $bt['file'].":".$bt['line']." ";
  585. $output .= "{$bt['class']}{$bt['type']}{$bt['function']}($args)\n";
  586. }
  587. return $output;
  588. }
  589. /*
  590. // Debug functions
  591. var $filename;
  592. var $err_message;
  593. var $_current_ip;
  594. var $_prev_exec_time;
  595. */
  596. function Debug($filename) {
  597. if (is_null($filename))
  598. $filename = dirname(__FILE__).DIRECTORY_SEPARATOR.'debug.log';
  599. $this->filename = $filename;
  600. if (isset($_SERVER['REMOTE_ADDR'])) {
  601. $this->_current_ip = (isset($_SERVER["HTTP_X_FORWARDED_FOR"]) ? array_shift(explode(',', $_SERVER["HTTP_X_FORWARDED_FOR"])) : $_SERVER["REMOTE_ADDR"]);
  602. }
  603. }
  604. function _sec2time($sec) {
  605. $res = '';
  606. if ($sec<0) {
  607. $sec = -$sec;
  608. $res = '-'.$res;
  609. }
  610. if ($sec!=floor($sec)) {
  611. $msec = round(($sec - floor($sec))*1000);
  612. $msec = '.'.str_pad($msec,3,'0', STR_PAD_LEFT);
  613. $sec = floor($sec);
  614. }
  615. $hours = floor($sec/3600);
  616. $min = floor(($sec - $hours*3600)/60);
  617. $sec = $sec - $hours*3600 - $min*60;
  618. if ($hours > 0)
  619. $res .= str_pad($hours,2,'0', STR_PAD_LEFT).':';
  620. if (($hours > 0) or ($min > 0))
  621. $res .= str_pad($min,2,'0', STR_PAD_LEFT).':';
  622. $res .= str_pad($sec,2,'0', STR_PAD_LEFT).$msec;
  623. return $res;
  624. }
  625. function _microtime_float() {
  626. list($usec, $sec) = explode(" ", microtime());
  627. return ((float)$usec + (float)$sec);
  628. }
  629. // print
  630. function p($message, $file = null, $line = null) {
  631. $res = true;
  632. $time_diff_str = '';
  633. if (!empty($this->_prev_exec_time)) {
  634. $time_diff = $this->_microtime_float() - $this->_prev_exec_time;
  635. if ($time_diff < 1) $time_diff_str = $this->_sec2time($time_diff);
  636. }
  637. $details = ((empty($this->_current_ip))?'':$this->_current_ip.' - ').
  638. ((!empty($file))?basename($file).' (line '.$line.')':'');
  639. if (!empty($details)) $details = ' ***** '.$details.' *****';
  640. $message = '['.date('d-M-Y H:i:s') . '] '.$time_diff_str.$details.
  641. "\n\n". $message . "\n\n";
  642. $new_file = (file_exists($this->filename))?false:true;
  643. $fh = @fopen($this->filename,'a');
  644. if (($fh !== false) and (is_resource($fh))) {
  645. @flock($fh, LOCK_EX);
  646. if (!@fwrite($fh, $message)) {
  647. $this->err_message = "Cannot write to file ($this->filename)";
  648. error_log($this->err_message.' in '.__FILE__.' on line '.__LINE__,0);
  649. $res = false;
  650. }
  651. @flock($fh, LOCK_UN);
  652. @fclose($fh);
  653. if ($new_file) chmod($this->filename,0777);
  654. }
  655. else {
  656. $this->err_message = 'Cannot open file ('.$this->filename.')'.
  657. ' in '.__FILE__.' on line '.__LINE__.' for save message: '."\n".$message;
  658. error_log($this->err_message,0);
  659. $res = false;
  660. }
  661. $this->_prev_exec_time = $this->_microtime_float();
  662. return $res;
  663. }
  664. }