PageRenderTime 56ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/atk4/lib/Logger.php

https://github.com/git86/todo
PHP | 658 lines | 439 code | 58 blank | 161 comment | 73 complexity | 86290401fd7746870d08d78191c5141f 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. function caughtException($e){
  231. $e->shift-=1;
  232. if($this->log_output){
  233. //$frame=$this->findFrame('warning',$shift);
  234. if(method_exists($e,'getMyTrace'))$trace=$e->getMyTrace();
  235. else $trace=$e->getTrace();
  236. $frame=$e->my_backtrace[$e->shift];
  237. $this->logLine($this->txtLine(get_class($e).": ".$e->getMessage(),$frame),2,'error',$trace);
  238. }
  239. if(!$this->web_output){
  240. echo $this->public_error_message;
  241. exit;
  242. }
  243. echo "<h2>".get_class($e)."</h2>\n";
  244. echo '<p><font color=red>' . $e->getMessage() . '</font></p>';
  245. if(method_exists($e,'getAdditionalMessage'))echo '<p><font color=red>' . $e->getAdditionalMessage() . '</font></p>';
  246. if($e->more_info){
  247. echo '<p>Additional information: <ul>';
  248. foreach($e->more_info as $key=>$info){
  249. if(is_array($info))$info=print_r($info,true);
  250. echo '<li>'.$key.': '.$info.'</li>';
  251. }
  252. echo '</ul></p>';
  253. }
  254. if(method_exists($e,'getMyFile'))echo '<p><font color=blue>' . $e->getMyFile() . ':' . $e->getMyLine() . '</font></p>';
  255. if(method_exists($e,'getMyTrace'))echo $this->backtrace($e->shift,$e->getMyTrace());
  256. else echo $this->backtrace($e->shift,$e->getTrace());
  257. exit;
  258. }
  259. function outputWarning($msg,$shift=0){
  260. // first, let's see if we should log this
  261. $frame=$this->findFrame('warning',$shift);
  262. if($this->log_output){
  263. $this->logLine($this->txtLine("warning: $msg",$frame),'warning','error');
  264. }
  265. if(!$this->web_output){
  266. return true;
  267. }else{
  268. echo $this->html_stdout?
  269. $this->htmlLine("$msg",$frame,'warning'):
  270. $this->txtLine("$msg",$frame,'warning');
  271. return true;
  272. }
  273. }
  274. function outputDebug($msg,$shift=0){
  275. // first, let's see if we should log this
  276. $frame=$this->findFrame('debug');
  277. if($this->log_output){
  278. $this->logLine($this->txtLine("info: $msg",$frame),'fatal','debug');
  279. }
  280. if(!$this->web_output){
  281. return true;
  282. }else{
  283. $this->debug_added=true;
  284. $this->debug_log.=$this->html_stdout?
  285. $this->htmlLine("$msg",$frame,'debug'):
  286. $this->txtLine("$msg",$frame,'debug');
  287. return true;
  288. }
  289. }
  290. function outputInfo($msg,$shift=0,$nohtml=false){
  291. if($this->log_output){
  292. $this->logLine($this->txtLine("info: $msg"),null,'info');
  293. }
  294. if($this->web_output && !$nohtml){
  295. echo $this->html_stdout?
  296. $this->htmlLine("$msg",null,'info'):
  297. $this->txtLine("$msg",null,'info');
  298. }
  299. return true;
  300. }
  301. function outputFatal($msg,$shift=0){
  302. // first, let's see if we should log this
  303. $frame=$this->findFrame('fatal');
  304. if($this->log_output){
  305. $this->logLine($this->txtLine("fatal: $msg",$frame),'fatal','error');
  306. }
  307. if(!$this->web_output){
  308. echo $this->public_error_message;
  309. }else{
  310. if($this->html_stdout){
  311. echo "<h2>Fatal error</h2>\n";
  312. }
  313. echo $this->html_stdout?
  314. $this->htmlLine("$msg",$frame,'fatal'):
  315. $this->txtLine("$msg",$frame,'fatal');
  316. if($this->html_stdout){
  317. echo $this->backtrace('fatal');
  318. }else{
  319. echo "Stack trace:\n";
  320. echo $this->txtBacktrace('fatal');
  321. }
  322. }
  323. exit;
  324. }
  325. function htmlLine($msg,$frame=array(),$prefix=null){
  326. if(!$frame){
  327. return "<font style='font-family: verdana; font-size:10px'><font color=blue>warning: </font> <font color=red><b>$msg</b></font></font><br>";
  328. }else{
  329. $errfile=dirname($frame['file']).'/<b>'.basename($frame['file']).'</b>';
  330. 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>";
  331. }
  332. }
  333. function txtLine($msg,$frame=array(),$prefix=null){
  334. if(!$frame){
  335. return "$prefix: $msg\n";
  336. }else{
  337. return basename($frame['file'])." on line ".$frame['line'].', path: '.dirname($frame['file'])."\n\n".
  338. ($prefix?"$prefix: ":"")."$msg\n\n";
  339. }
  340. }
  341. function logLine($msg,$shiftfunc=null,$severity='info',$trace=null){
  342. $log_file='log_'.$severity.'_file';
  343. if($this->log_output==='full' && $severity=='error'){
  344. if(!$this->header_sent++){
  345. fputs($this->$log_file,"\n\n".
  346. "============================================================\n".
  347. "$msg".
  348. "------------------------------------------------------------\n".
  349. "Date: ".date("d-M-Y H:i:s")."\n");
  350. foreach($this->details as $key=>$val){
  351. fputs($this->$log_file,"$key: $val\n");
  352. }
  353. fputs($this->$log_file,
  354. "------------------------------------------------------------\n".
  355. " Stack trace\n".
  356. $this->txtBacktrace($shiftfunc,$trace).
  357. "\n"
  358. );
  359. }else{
  360. fputs($this->$log_file,$msg);
  361. }
  362. }elseif($this->log_output){
  363. fputs($this->$log_file,"[".date("d-M-Y H:i:s")."] $msg");
  364. }else{
  365. return;
  366. }
  367. fflush($this->$log_file);
  368. }
  369. function logVar($var,$msg="",$shiftfunc=null,$severity='debug'){
  370. //creating an $msg from variable
  371. $msg.="(".gettype($var)."):";
  372. if(is_array($var)||is_object($var))$msg .= print_r($var, true);
  373. else$msg .= $var;
  374. $this->logLine($msg."\n", $shiftfunc,$severity);
  375. }
  376. function logException($e){
  377. // logs exception from the catch statement
  378. // contains code from Logger::caughtException(), as this code won't launch
  379. // if exception is caught
  380. $frame=$e->my_backtrace[$e->shift-1];
  381. $this->logLine($this->txtLine(get_class($e).": (".$e->getCode().") ".$e->getMessage(),$frame),2,'error');
  382. return $this;
  383. }
  384. function openLogFile($severity='error'){
  385. if(!is_dir($this->log_dir)){
  386. // Directory is not writable, let's first try to create it
  387. if(!mkdir($this->log_dir,0750)){
  388. throw new BaseException("Unable to create $this->log_dir for log output");
  389. }
  390. }
  391. $filename='am3_'.$severity.'_log';
  392. $full_filename=$this->log_dir.DIRECTORY_SEPARATOR.$filename;
  393. 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");
  394. if(is_link($full_filename))throw new BaseException("Log file is a symlink. Are you trying to make me overwrite somethingn?");
  395. ini_set($severity.'_log',$full_filename);
  396. //$full_filename=tempnam($this->log_dir,$filename);
  397. $new_file = (file_exists($full_filename))?false:true;
  398. $log_file="log_$severity"."_file";
  399. $this->$log_file=fopen($full_filename,"a");
  400. if(!$this->$log_file)throw new IOException("Cannot open $severity log file");
  401. if ($new_file) chmod($full_filename,0777); //
  402. }
  403. function writeLogMessage(){
  404. }
  405. function backtrace($sh=null,$backtrace=null){
  406. $output = "<div >\n";
  407. $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";
  408. if(!isset($backtrace)) $backtrace=debug_backtrace();
  409. $n=0;
  410. foreach($backtrace as $bt){
  411. $n++;
  412. $args = '';
  413. if(!isset($bt['args']))continue;
  414. foreach($bt['args'] as $a){
  415. if(!empty($args)){
  416. $args .= ', ';
  417. }
  418. switch (gettype($a)) {
  419. case 'integer':
  420. case 'double':
  421. $args .= $a;
  422. break;
  423. case 'string':
  424. $a = htmlspecialchars(substr($a, 0, 128)).((strlen($a) > 128) ? '...' : '');
  425. $args .= "\"$a\"";
  426. break;
  427. case 'array':
  428. $args .= "Array(".count($a).")";
  429. break;
  430. case 'object':
  431. $args .= "Object(".get_class($a).")";
  432. break;
  433. case 'resource':
  434. $args .= "Resource(".strstr($a, '#').")";
  435. break;
  436. case 'boolean':
  437. $args .= $a ? 'True' : 'False';
  438. break;
  439. case 'NULL':
  440. $args .= 'Null';
  441. break;
  442. default:
  443. $args .= 'Unknown';
  444. }
  445. }
  446. if(($sh==null && strpos($bt['file'],'/atk4/lib/')===false) || (!is_int($sh) && $bt['function']==$sh)){
  447. $sh=$n;
  448. }
  449. $output .= "<tr><td valign=top align=right><font color=".($sh==$n?'red':'blue').">".dirname($bt['file'])."/".
  450. "<b>".basename($bt['file'])."</b></font></td>";
  451. $output .= "<td valign=top nowrap><font color=".($sh==$n?'red':'blue').">:{$bt['line']}</font>&nbsp;</td>";
  452. $name=(!isset($bt['object']->name))?get_class($bt['object']):$bt['object']->name;
  453. if($bt['object'])$output .= "<td>".$name."</td>";else $output.="<td></td>";
  454. $output .= "<td valign=top><font color=".($sh==$n?'red':'green').">{$bt['class']}{$bt['type']}<b>{$bt['function']}</b>($args)</font></td></tr>\n";
  455. }
  456. $output .= "</table></div>\n";
  457. return $output;
  458. }
  459. function cleanupLogDirectory(){
  460. // we should try to take care of our own log file cleanup
  461. }
  462. function txtBacktrace($sh=null,$backtrace=null){
  463. if(!isset($backtrace)) $backtrace=debug_backtrace();
  464. $output='';
  465. $n=0;
  466. foreach($backtrace as $bt){
  467. $n++;
  468. $args = '';
  469. if(!isset($bt['args']))$bt['args']=array();
  470. foreach($bt['args'] as $a){
  471. if(!empty($args)){
  472. $args .= ', ';
  473. }
  474. switch (gettype($a)) {
  475. case 'integer':
  476. case 'double':
  477. $args .= $a;
  478. break;
  479. case 'string':
  480. $a = (substr($a, 0, 128)).((strlen($a) > 128) ? '...' : '');
  481. $args .= "\"$a\"";
  482. break;
  483. case 'array':
  484. $args .= "Array(".count($a).")";
  485. break;
  486. case 'object':
  487. $args .= "Object(".get_class($a).")";
  488. break;
  489. case 'resource':
  490. $args .= "Resource(".strstr($a, '#').")";
  491. break;
  492. case 'boolean':
  493. $args .= $a ? 'True' : 'False';
  494. break;
  495. case 'NULL':
  496. $args .= 'Null';
  497. break;
  498. default:
  499. $args .= 'Unknown';
  500. }
  501. }
  502. if($sh){
  503. if(is_int($sh)){
  504. if($sh>0){
  505. $sh--;
  506. continue;
  507. }
  508. }elseif($bt['function']!=$sh){
  509. $sh=null;
  510. continue;
  511. }
  512. }
  513. $output .= $bt['file'].":".$bt['line']." ";
  514. $output .= "{$bt['class']}{$bt['type']}{$bt['function']}($args)\n";
  515. }
  516. return $output;
  517. }
  518. /*
  519. // Debug functions
  520. var $filename;
  521. var $err_message;
  522. var $_current_ip;
  523. var $_prev_exec_time;
  524. */
  525. function Debug($filename) {
  526. if (is_null($filename))
  527. $filename = dirname(__FILE__).DIRECTORY_SEPARATOR.'debug.log';
  528. $this->filename = $filename;
  529. if (isset($_SERVER['REMOTE_ADDR'])) {
  530. $this->_current_ip = (isset($_SERVER["HTTP_X_FORWARDED_FOR"]) ? array_shift(explode(',', $_SERVER["HTTP_X_FORWARDED_FOR"])) : $_SERVER["REMOTE_ADDR"]);
  531. }
  532. }
  533. function _sec2time($sec) {
  534. $res = '';
  535. if ($sec<0) {
  536. $sec = -$sec;
  537. $res = '-'.$res;
  538. }
  539. if ($sec!=floor($sec)) {
  540. $msec = round(($sec - floor($sec))*1000);
  541. $msec = '.'.str_pad($msec,3,'0', STR_PAD_LEFT);
  542. $sec = floor($sec);
  543. }
  544. $hours = floor($sec/3600);
  545. $min = floor(($sec - $hours*3600)/60);
  546. $sec = $sec - $hours*3600 - $min*60;
  547. if ($hours > 0)
  548. $res .= str_pad($hours,2,'0', STR_PAD_LEFT).':';
  549. if (($hours > 0) or ($min > 0))
  550. $res .= str_pad($min,2,'0', STR_PAD_LEFT).':';
  551. $res .= str_pad($sec,2,'0', STR_PAD_LEFT).$msec;
  552. return $res;
  553. }
  554. function _microtime_float() {
  555. list($usec, $sec) = explode(" ", microtime());
  556. return ((float)$usec + (float)$sec);
  557. }
  558. // print
  559. function p($message, $file = null, $line = null) {
  560. $res = true;
  561. $time_diff_str = '';
  562. if (!empty($this->_prev_exec_time)) {
  563. $time_diff = $this->_microtime_float() - $this->_prev_exec_time;
  564. if ($time_diff < 1) $time_diff_str = $this->_sec2time($time_diff);
  565. }
  566. $details = ((empty($this->_current_ip))?'':$this->_current_ip.' - ').
  567. ((!empty($file))?basename($file).' (line '.$line.')':'');
  568. if (!empty($details)) $details = ' ***** '.$details.' *****';
  569. $message = '['.date('d-M-Y H:i:s') . '] '.$time_diff_str.$details.
  570. "\n\n". $message . "\n\n";
  571. $new_file = (file_exists($this->filename))?false:true;
  572. $fh = @fopen($this->filename,'a');
  573. if (($fh !== false) and (is_resource($fh))) {
  574. @flock($fh, LOCK_EX);
  575. if (!@fwrite($fh, $message)) {
  576. $this->err_message = "Cannot write to file ($this->filename)";
  577. error_log($this->err_message.' in '.__FILE__.' on line '.__LINE__,0);
  578. $res = false;
  579. }
  580. @flock($fh, LOCK_UN);
  581. @fclose($fh);
  582. if ($new_file) chmod($this->filename,0777);
  583. }
  584. else {
  585. $this->err_message = 'Cannot open file ('.$this->filename.')'.
  586. ' in '.__FILE__.' on line '.__LINE__.' for save message: '."\n".$message;
  587. error_log($this->err_message,0);
  588. $res = false;
  589. }
  590. $this->_prev_exec_time = $this->_microtime_float();
  591. return $res;
  592. }
  593. }