Location: PHPKode > projects > crVCL PHP Framework > logger.lib.php
<?PHP


/*

The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html or see MPL-1.1.txt in directory "license"

Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for
the specific language governing rights and limitations under the License.

The Initial Developers of the Original Code are: 
Copyright (c) 2003-2012, CR-Solutions (http://www.cr-solutions.net), Ricardo Cescon
All Rights Reserved.

Contributor(s): Ricardo Cescon

crVCL PHP Framework Version 2.4
*/


############################################################
if(!defined("LOGGER_LIB")){
   define ("LOGGER_LIB", 1);
############################################################
class Logger{
   private $m_level = 0;
   private $m_pathname = "";
   private $m_filename = "";
	private $m_critical_section_id = null;
	private $m_critical_section_path = "./";
	private $m_critical_section_maxlock_ms = 100;
	private $m_critical_section_sleep_ms = 10;
	private $m_delAfter = "1M";
	private $m_header = true;
	private $m_file = false;
	private $m_display = false;
	private $m_trigger = false;
	private $m_trigger_type = E_USER_NOTICE;
	private $m_h_timestamp = true;
	private $m_h_tickcount = false;
	private $m_h_scriptinfo = true;
	private $m_h_meminfo = true;
   private $m_sqlite = false;
   private $m_sqlite3 = false;
   private $m_sqlite_obj = array();
   private $m_cache = array();
   private $m_write_cache = false;
//-------------------------------------------------------------------------------------------------------------------------------------
   private function writeFile(&$msg, &$header_str, $header, $forceCache){ 
		$path = $this->m_pathname; 
		$file = $this->m_filename;				
			
		$bytes = false;
		if(!empty($path) || !empty($file)){
		   $msg = str_replace("<br>", CRLF, $msg);
		   $header_str = str_replace("<br>", CRLF, $header_str);
		
		   $memlimit = ini_get('memory_limit');
		   if(!is_numeric($memlimit))
		      $memlimit = str2byteInt($memlimit);
		      
		   $max_cache_items = 20;   
		   $max_cache_size_per_item = str2byteInt('100K');   
		   if($memlimit > str2byteInt('20M')){
		      $max_cache_items = 50;
		      $max_cache_size_per_item = str2byteInt('200K');   
		   }   
		   if($memlimit = -1 || $memlimit > str2byteInt('50M')){
		      $max_cache_items = 100;   
		      $max_cache_size_per_item = str2byteInt('300K');   
		   }
		   		      
		   if($forceCache || strlen($msg) > $max_cache_size_per_item){
		      $this->m_write_cache = true;
		   }
		         
		   if(!$this->m_write_cache && acount($this->m_cache) < $max_cache_items){
		      $this->m_cache[] = array($msg, $header_str, $header);
		      return;
		   }		
		   		   
         clearstatcache();
         
         if(empty($path)){
				$path = $_SERVER['DOCUMENT_ROOT'];
			}
			if(empty($file)){
				$file = "logger.".extractFileName($_SERVER["SCRIPT_FILENAME"]).".log";
			}
			$path = fixpath($path);
         
			// Textfile
			if($this->m_sqlite !== true){
			
   		   $crit = null;
   		   if($this->m_critical_section_id !== null){
               $crit = new CriticalSection($this->m_critical_section_path);
               $crit->enter($this->m_critical_section_id, $this->m_critical_section_sleep_ms, $this->m_critical_section_maxlock_ms);
            }
   			
            $fbuf  = @file_get_contents($path."/".$file);
            $fbuf_len = strlen($fbuf);
            
            $seperator = CRLF.CRLF."\t\t\t\t".CRLF;
            
   			if(!empty($this->m_delAfter)){
   			   $bytes = str2byteInt($this->m_delAfter);
   			   if(is_file($path."/".$file) && @filesize($path."/".$file) > $bytes){
   			      $fbuf = substr($fbuf, round($fbuf_len/2, 0));
   			      $next = strpos($fbuf, $seperator);
   			      if($next !== false){
   			         $fbuf = substr($fbuf, $next+strlen($seperator));
   			      }
   			   }
   			}

   			// add the last 
   			if($msg != ""){
   			   $this->m_cache[] = array($msg, $header_str, $header);           
   			}   
   			for($i = 0; $i < acount($this->m_cache); $i++){
   			   $msg = $this->m_cache[$i][0];
   			   $header_str = $this->m_cache[$i][1];
   			   $header = $this->m_cache[$i][2];
   			   $fbuf .= $header_str.$msg.$seperator;  
   			}   			  			
   
   			if(strlen($fbuf) > 0)
         	   $bytes = file_put_contents($path."/".$file, $fbuf);
   
         	if($this->m_critical_section_id !== null){
               $crit->leave($this->m_critical_section_id);
            }
			
            $this->m_cache = array(); // reset
            $this->m_write_cache = false;
            
         // SQLite   
			}else{
			   $sqlite = null;
			   
			   if(strtolower(right($file, 4)) != ".sql"){
			      $file .= ".db";
			   }
			   $iobj = trim_specialchars($path."/".$file, "_");
			   
			   
			   // used also by sqlite for fix problems on filesystems like NFS
			   $crit = null;
   		   if($this->m_critical_section_id !== null){
               $crit = new CriticalSection($this->m_critical_section_path);
               $crit->enter($this->m_critical_section_id, $this->m_critical_section_sleep_ms, $this->m_critical_section_maxlock_ms);
            }
			   
            while(true){
			   
   			   if(!isset($this->m_sqlite_obj[$iobj])){
      			   $sqlite = new SQLiteDB($this->m_sqlite3);			   
      			   $sqlite->m_persistent_connection = true;
      			   $sqlite->connect("logger", $path."/".$file);
      
      			   // increase performance
      			   $sqlite->sql("logger", "PRAGMA default_cache_size=8192;");	
      			   $sqlite->sql("logger", "PRAGMA cache_size=8192;");				   			   
      			   $sqlite->sql("logger", "PRAGMA synchronous=OFF;");		         			   
      			   $sqlite->sql("logger", "PRAGMA temp_store=MEMORY;");	
      			   $sqlite->sql("logger", "PRAGMA journal_mode=MEMORY;");
      			   
      			   
      			   $sql = 'SELECT * FROM sqlite_master WHERE type = "table" AND name = "log"';			   
      			   $res = $sqlite->sql("logger", $sql);
      			         			   		   
      			   if($res->rows() == 0){
         			   $sql = 'CREATE TABLE log (
                                id INTEGER PRIMARY KEY,                          
                                date DATETIME,
                                hmsg TEXT,
                                mem INTEGER,
                                script TEXT,                          
                                qstr TEXT,   
                                msg TEXT
                             );';
         		      $sqlite->sql("logger", $sql);
         		      
         		      if($sqlite->getError("logger") != 0){
         			      trigger_error("error create log table", E_USER_NOTICE);
         			      break;
         			   }
         			   
         			   $sql = 'CREATE INDEX [IDX_DATE] ON [log]([date]  ASC);';
         			   $sqlite->sql("logger", $sql);
         			   if($sqlite->getError("logger") != 0){
         			      trigger_error("error create log index IDX_DATE", E_USER_NOTICE);
         			      break;
         			   }   			   
      			   }
      			   
      			   $this->m_sqlite_obj[$iobj] = $sqlite;
   			   } // if check obj
   
   			   $sqlite = $this->m_sqlite_obj[$iobj];
   			   			      
   		      if(!empty($this->m_delAfter)){
      			   $bytes = str2byteInt($this->m_delAfter);
      			   $retry = 2;
      			   for($i = 1; $i <= $retry; $i++){
         			   if(is_file($path."/".$file) && @filesize($path."/".$file) > $bytes){
         			      $sql = 'SELECT id AS last_id FROM log ORDER BY id DESC LIMIT 1';
            			   $res = $sqlite->sql("logger", $sql);
            			   if($sqlite->getError("logger") != 0){
            			      if($i < $retry){
            			         clearstatcache();
            			         continue;
            			      }else{            			      
            			         trigger_error("error read log last row id", E_USER_NOTICE);            			         
            			      }
            			   }else{
               			   $last_id = $res->getField("last_id");
            			         			      
            			      $sql = 'DELETE FROM log WHERE id < '.($last_id/2);
         			         $ret = $sqlite->sql("logger", $sql);
         			         			         
         			         $ret = $sqlite->sql("logger", 'VACUUM');			         
            			   }   
         			   }
      			   }   
      			}      		
      			
      			
      			
       			// add the last 
      			if($msg != ""){
      			   $this->m_cache[] = array($msg, $header_str, $header);           
      			}   
      			$sqlite->begin("logger");
      			for($i = 0; $i < acount($this->m_cache); $i++){
      			   $msg = $this->m_cache[$i][0];
      			   $header_str = $this->m_cache[$i][1];
      			   $header = $this->m_cache[$i][2];
      			
      			       			         			   
      			   $msg = str_replace('"', '\'', $msg);
      			  			
         			$build = new SQLiteDBBuilder(SQLITE_INSERT, "log");
         			$build->m_escape_strings = true;
         			if(isset($header["date"]))
         			   $build->addValue("date", $header["date"], "D");
         			if(isset($header["hmsg"]))   
         			   $build->addValue("hmsg", $header["hmsg"], "S");
         			if(isset($header["mem"]))
         			   $build->addValue("mem", $header["mem"], "I");
         			if(isset($header["script"]))
         			   $build->addValue("script", $header["script"], "S");
         			if(isset($header["qstr"]))
         			   $build->addValue("qstr", $header["qstr"], "S");
         			$build->addValue("msg", $msg, "S");
         			$ret = $sqlite->sql("logger", $build);
         			if($sqlite->getError("logger") != 0){      			            			   
         			   $err = '';
         			   if(stripos($sqlite->getErrorMsg("logger"), 'syntax error') !== false){
      			         $err = ': '.$build->query();
      			      }
      			      trigger_error("error write log ".$path."/".$file." (".$sqlite->getErrorMsg("logger").$err.")", E_USER_NOTICE);      			            			        
      			      $sqlite->rollback("logger");
      			      break;
      			   }         			         			            			   
      			}
               $sqlite->commit("logger");      			     			      			      			
   			   
   			   break;
            }// while
			   if($this->m_critical_section_id !== null){
               $crit->leave($this->m_critical_section_id);
            }
			   
            $this->m_cache = array(); // reset
            $this->m_write_cache = false;
			}// else (mode)
                           
		}
		return;
	}
//-------------------------------------------------------------------------------------------------------------------------------------
   function __construct(){
      
   }
//-------------------------------------------------------------------------------------------------------------------------------------
   function __destruct(){
      $msg = $header_str = $header = '';
      $this->m_write_cache = true;
      if($this->m_file){
         $this->writeFile($msg, $header_str, $header, true);   
      }
      free($msg);
      free($header_str);
      free($header);
      gc_collect_cycles_overX($GLOBALS['CRVCL']['GC_COLLECT_CYCLES_PERCENT']);
   }
//-------------------------------------------------------------------------------------------------------------------------------------
   /**
    * set the log level, sample: LOG_LEVEL_DEBUG
    *
    * @param int $LOG_LEVEL
    */
   function setLevel($LOG_LEVEL){
      $this->m_level = $LOG_LEVEL;
   }
//-------------------------------------------------------------------------------------------------------------------------------------   
   /**
    * log a message
    *
    * @param int $level
    * @param string $msg
    * @param string $header_msg
    * @param bool $forceCache
    */
   function log($level, $msg, $header_msg="", $forceCache=false){
      if($level >= $this->m_level){
         $header = array();
         $header_str = '';
         
         if($this->m_header){                        
            if($this->m_h_timestamp){
               $tick = '';
               if($this->m_h_tickcount){
                  $tick = '.'.gettickcount();
               }
               $header["date"] = date("Y-m-d H:i:s").$tick;
            }
            
            if($header_msg != ""){
               $header["hmsg"] = $header_msg;
            }   
            
            if($this->m_h_meminfo && function_exists('memory_get_usage')){
               $header["mem"] =  memory_get_usage(true);
            }
            
            if($this->m_h_scriptinfo){               
               if(isset($_SERVER["SCRIPT_FILENAME"])){
                  $header["script"] = $_SERVER["SCRIPT_FILENAME"];
               }
               if(isset($_SERVER["QUERY_STRING"])){
                  $header["qstr"] = $_SERVER["QUERY_STRING"];
               }                              
            }         
         }

         $header_str .= (isset($header["date"])?$header["date"]." - ":"");        
         $header_str .= (isset($header["hmsg"])?$header["hmsg"]." - ":"");        
         $header_str .= (isset($header["mem"])?"mem: ".$header["mem"]." bytes - ":"");        
         $header_str .= (isset($header["script"])?"called from: ".$header["script"]:"");        
         $header_str .= (isset($header["qstr"])?"?".$header["qstr"]:"");        
         $header_str .= (isset($header["script"]) || isset($header["qstr"])?" - ":"");        
         if(!empty($header_str)){
            $header_str .= BR;
         }
         
         if($this->m_display){
            print $header_str.$msg.BR.BR;
            flush();
         }
         
         if($this->m_file){
            $this->writeFile($msg, $header_str, $header, $forceCache);
         }
                              
         if($this->m_trigger){
            trigger_error($header_str.$msg, $this->m_trigger_type);
         }
      }   
   }
//-------------------------------------------------------------------------------------------------------------------------------------   
   /**
    * enable/disable the header output
    *
    * @param bool $enable
    */
   function enableHeaderOutput($enable){
      $this->m_header = $enable;
   }
//-------------------------------------------------------------------------------------------------------------------------------------
   /**
    * enable/disable the display output
    *
    * @param bool $enable
    */
   function enableOutputDisplay($enable){
      $this->m_display = $enable;
   }
//-------------------------------------------------------------------------------------------------------------------------------------
   /**
    * enable/disable the trigger output (do not use, if you work with exclusive exceptions)
    *
    * @param bool $enable
    * @param int $trigger_type
    */
   function enableOutputTrigger($enable, $trigger_type=null){
      $this->m_trigger = $enable;
      if($trigger_type !== null)
         $this->m_trigger_type = $trigger_type;   
   }
//-------------------------------------------------------------------------------------------------------------------------------------
   /**
    * enable/disable the file output
    *
    * @param bool $enable
    * @param bool $useSQLite
    */
   function enableOutputFile($enable, $useSQLite=null){      
      $this->m_file = $enable;      
      if($useSQLite !== null){
         $this->m_sqlite = $useSQLite;
      }
   }
//-------------------------------------------------------------------------------------------------------------------------------------
   /**
    * set the output file 
    *
    * @param string $path
    * @param string $file
    * @param mixed $delAfter
    */
   function setOutputFile($path, $file, $delAfter="1M"){
      $this->m_pathname = $path;
      $this->m_filename = $file;
      $this->m_delAfter = $delAfter;
   }   
//-------------------------------------------------------------------------------------------------------------------------------------   
   /**
    * enable/disable additional header infos
    *
    * @param bool $timestamp
    * @param bool $scriptinfo
    * @param bool $meminfo
    * @param bool $tickcount
    */
   function setHeaderSettings($timestamp, $scriptinfo, $meminfo, $tickcount=false){
      $this->m_h_timestamp = $timestamp;
      $this->m_h_scriptinfo = $scriptinfo;
      $this->m_h_meminfo = $meminfo;
      $this->m_h_tickcount = $tickcount;
   }
//-------------------------------------------------------------------------------------------------------------------------------------   
   /**
    * set the settings to use a critical section for the writing of the output file<br>set the id null, to disable
    *
    * @param string $id
    * @param string $path path to share the critical section
    * @param int $maxlock_ms max lock time for zombie processes
    * @param int $sleep_ms sleep for retry enter critical section
    */
   function setOutputFile_CriticalSectionId($id, $path="./", $maxlock_ms=1000, $sleep_ms=10){
     	$this->m_critical_section_id = $id;
	   $this->m_critical_section_path = $path;
	   $this->m_critical_section_maxlock_ms = $maxlock_ms;
	   $this->m_critical_section_sleep_ms = $sleep_ms;      
   }
//-------------------------------------------------------------------------------------------------------------------------------------   
}

/*
require("tools.lib.php");
require("sqlite.lib.php");

$logger = new Logger();
$logger->setOutputFile("C:/Temp", "test.trc");
$logger->enableOutputFile(true, true);
$logger->enableHeaderOutput(true);
$logger->log(LOG_LEVEL_DEBUG, "test", "xyz");


$logger->setOutputFile("C:/Temp", "test.trc");
$logger->enableOutputFile(true, true);
$logger->enableHeaderOutput(true);
$logger->log(LOG_LEVEL_DEBUG, "test", "zxy");
*/
############################################################
}
############################################################
?>
Return current item: crVCL PHP Framework