PageRenderTime 70ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/atk4/lib/Logger.php

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