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