Skip to content
Snippets Groups Projects
Select Git revision
  • faea12442b573acb37dc60c71ca66a3edea56510
  • master default protected
  • update-renderer-class
3 results

cronlog.class.php

Blame
  • cronlog.class.php 11.64 KiB
    <?php
    /**
     * ______________________________________________________________________
     * 
     *  _____                 _       _             _                        
     * /  __ \               (_)     | |           (_)                       
     * | /  \/_ __ ___  _ __  _  ___ | |__   __   ___  _____      _____ _ __ 
     * | |   | '__/ _ \| '_ \| |/ _ \| '_ \  \ \ / / |/ _ \ \ /\ / / _ \ '__|
     * | \__/\ | | (_) | | | | | (_) | |_) |  \ V /| |  __/\ V  V /  __/ |   
     *  \____/_|  \___/|_| |_| |\___/|_.__/    \_/ |_|\___| \_/\_/ \___|_|   
     *                      _/ |                                             
     *                     |__/                                              
     * ______________________________________________________________________
     * 
     * The cronjob viewer for centralized monitoring of cronjobs using
     * Axels cronrwapper (see <https://github.com/axelhahn/cronwrapper>).
     * 
     * You can browse all servers to see 
     * - the last status of all cronjobs
     * - results an execution times of running jonbs
     * - a timeline for all jobs running > 60s
     * 
     * 
     * Free software. Open Source. GNU GPL 3.
     * SOURCE: <https://git-repo.iml.unibe.ch/iml-open-source/cronlog-viewer>
     * 
     * ______________________________________________________________________
     * 
     * The cronlog class contains non visual methods
     * @see cronlog-renderer.class.php
     *
     * @license GNU GPL 3.0
     * @author Axel Hahn <axel.hahn@iml.unibe.ch>
     */
    
    class cronlog {
        
        
        protected $_sDataDir = "__APPDIR__/data";
        protected $_iTtlCache = 60; // in sec
    
        /**
         * when show an error for expired jobs (latency to execute job and sync logs)
         * @var integer
         */
        protected $_iExpiredJobsFailAfter = 60*30; // in sec
        protected $_iMinTtl = 0; // in sec
        protected $_aSkipJoblogs = array();
        
        protected $_aInstances = array();
    
        protected $_aServers = array();
        protected $_sActiveServer = false;
        
        protected $_sFileFilter_serverjoblog = '*joblog*.done';
        protected $_sFileFilter_serverlog = '*.log';
    
        // ----------------------------------------------------------------------
        // MAIN
        // ----------------------------------------------------------------------
    
        /**
         * init
         * @return boolean
         */
        public function __construct() {
    
            // read config
            if (file_exists(__DIR__.'/../config/inc_cronlog.php')){
                $aCfgTemp=include(__DIR__.'/../config/inc_cronlog.php');
                $this->_sDataDir = isset($aCfgTemp['sDatadir']) ? $aCfgTemp['sDatadir'] : $this->_sDataDir;
                $this->_iTtlCache = isset($aCfgTemp['iTtlCache']) ? (int)$aCfgTemp['iTtlCache'] : $this->_iTtlCache;
                $this->_iMinTtl = isset($aCfgTemp['iMinTtl']) ? (int)$aCfgTemp['iMinTtl'] : $this->_iMinTtl;
                $this->_iExpiredJobsFailAfter = isset($aCfgTemp['iExpiredJobsFailAfter']) ? (int)$aCfgTemp['iExpiredJobsFailAfter'] : $this->_iExpiredJobsFailAfter;
                $this->_aSkipJoblogs = isset($aCfgTemp['aHidelogs']) && is_array($aCfgTemp['aHidelogs']) ? $aCfgTemp['aHidelogs'] : $this->_aSkipJoblogs;        
                $this->_aInstances = isset($aCfgTemp['instances']) ? $aCfgTemp['instances'] : [];
    
            }
            $this->_sDataDir = str_replace("__APPDIR__", dirname(dirname(__FILE__)), $this->_sDataDir);
            $this->_sDataDir = str_replace('\\', '/', $this->_sDataDir);
            
            $this->getServers();
                    
            return true;
        }
        
        // ----------------------------------------------------------------------
        // private
        // ----------------------------------------------------------------------
    
        
        /**
         * chaching: get the full path of directory for caching
         * @return type
         */
        protected function _getCacheDir(){
            return $this->_sDataDir.'/__cache';
        }
    
        /**
         * caching: get full path of a caching item
         * @param type $sTaskId
         * @return type
         */
        protected function _getCacheFile($sTaskId){
            return $this->_getCacheDir().'/'.$sTaskId;
        }
    
        /**
         * read logs: get full path to a servers cronjob logdata
         * @return type
         */
        protected function _getServerlogDir(){
            return $this->_sDataDir.'/'.$this->_sActiveServer;
        }
    
        /**
         * caching: get cached data if they exist and aren't expired
         * @param  string  $sTaskId
         * @return boolean|array
         */
        protected function _getCacheData($sTaskId){
            // DISABLE CACHE return false;
            $sFile=$this->_getCacheFile($sTaskId);
            if(file_exists($sFile)){
                if (filemtime($sFile)>(date('U')-$this->_iTtlCache)){
                    // echo "USE cache $sFile<br>";
                    return unserialize(file_get_contents($sFile));
                } else {
                    // echo "DELETE cache $sFile<br>";
                    unlink($sFile);
                }
            }
            return false;
        }
        /**
         * read logs: parse a single line in the joblog and return has with all key value items
         * @param  string  $sLine  single line in the log
         * @return array
         */
        protected function _parseJoblogLine($sLine){
            $aReturn=array();
            // echo "DEBUG $sLine<br>";
            // job=dok-kvm-instances:host=kalium:start=1538358001:end=1538358001:exectime=0:ttl=60:rc=0
            $sLine=str_replace("\n", '', $sLine);
            $sLine=str_replace(':', '", "', $sLine);
            $sLine=str_replace('=', '": "', $sLine);
            $sLine='{"'.$sLine.'"}';
            // echo "DEBUG $sLine<br><br>";
            $aReturn=json_decode($sLine, 1);
            if(!is_array($aReturn)){
                echo "not a JSON string<br>";
                echo "DEBUG $sLine<br><br>";
                die();
            }
            return $aReturn;
        }
    
        /**
         * read logs: parse the whole cronwrapper logfile and return a hash
         * @param  string  $sFile  filename with full path
         */
        protected function _parseLogfile($sFile) {
            $aReturn=array(
                'SCRIPTNAME'=>false,
                'SCRIPTTTL'=>false,
                'SCRIPTSTARTTIME'=>false,
                'SCRIPTLABEL'=>false,
                'SCRIPTENDTIME'=>false,
                'SCRIPTEXECTIME'=>false,
                'SCRIPTRC'=>false,
                // 'SCRIPTOUT'=>array(),
            );
            $fileHandle = fopen($sFile, "r");
            while (($line = fgets($fileHandle)) !== false) {
                // get key ... the part before "="
                $sKey=trim(preg_replace('/=.*/', '', $line));
                if($sKey && isset($aReturn[$sKey])){
                    // add value ... the part behind "="
                    $aReturn[$sKey]=preg_replace('/^([A-Z]*\=)/', '', $line);
                }
            }
            fclose($fileHandle);
            
            // fetch unit timestamp from date values (they are like "2018-09-30 03:40:05, 1538278805")
            $aReturn['SCRIPTSTARTTIME']=(int)preg_replace('/.*,\ /', '', $aReturn['SCRIPTSTARTTIME']);
            $aReturn['SCRIPTENDTIME']=(int)preg_replace('/.*,\ /', '', $aReturn['SCRIPTSTARTTIME']);
            
            // remove " s" from exec time value
            $aReturn['SCRIPTEXECTIME']=preg_replace('/\ s$/', '', $aReturn['SCRIPTEXECTIME']);
            
            return $aReturn;
            
        }
        
        /**
         * caching: write new data; it returns the success of write operation as bool
         * @param  string  $sTaskId
         * @param  [any]   $data      data to store; can be any serializable value
         * @return boolean
         */
        protected function _writeCacheData($sTaskId, $data){
            $sFile=$this->_getCacheFile($sTaskId);
            // echo "WRITE cache $sFile<br>";
            return file_put_contents($sFile, serialize($data));
        }
    
        // ----------------------------------------------------------------------
        // public getter
        // ----------------------------------------------------------------------
        
        /**
         * get currently selected server
         * @return string
         */
        public function getServer(){
            return $this->_sActiveServer;
        }
        
        /**
         * get array with existing servers in data dir
         * @return array
         */
        public function getServers(){
            
            if(is_array($this->_aServers) && count($this->_aServers)){
                return $this->_aServers;
            }
            
            $this->_aServers=array();
            // echo "DEBUG DATADIR: " . $this->_sDataDir."<br>";
            if (!is_dir($this->_sDataDir)){
                echo "WARNING: no data. Check sDatadir in the config and set it to an existing directory.<br>";
                die();
            }
    
            if ($handle = opendir($this->_sDataDir)) {
                while (false !== ($entry = readdir($handle))) {
                    if ($entry != "." && $entry != ".." && $entry != "__cache" && is_dir($this->_sDataDir.'/'.$entry)) {
                        // echo "DEBUG $entry<br>\n";
                        $this->_aServers[$entry]=array();
                    }
                }
                closedir($handle);
            }
            ksort($this->_aServers);
            return $this->_aServers;
        }
        
        /**
         * get logs from jobfilea of the current or given server
         * @param  boolean  $bUseSkip  hide jobs if their label matches the skip list; default: true
         * @return array
         */
        public function getServerJobHistory($bUseSkip=true){
            $aReturn=array();
            $sTaskId=__FUNCTION__.'-'.$this->_sActiveServer;
    
            $aData=$this->_getCacheData($sTaskId);
            if($aData){
                return $aData;
            }
    
            $aData=array();
                
            foreach(glob($this->_getServerlogDir().'/'.$this->_sFileFilter_serverjoblog) as $sMyJobfile){
                // echo "DEBUG: $sMyJobfile<br>";
                $fileHandle = fopen($sMyJobfile, "r");
                while (($line = fgets($fileHandle)) !== false) {
                    // send the current file part to the browser
                    $aData=$this->_parseJoblogLine($line);
                    if(!$bUseSkip || array_search($aData['job'], $this->_aSkipJoblogs)===false){
                        $aReturn[$aData['start']]=$aData;
                    }
                }
                fclose($fileHandle);
            }
            krsort($aReturn);
            $this->_writeCacheData($sTaskId, $aReturn);
            return $aReturn;
        }
        
    
        /**
         * get logs from jobfilea of the current or given server
         * @return array
         */
        public function getServersLastLog(){
            $aReturn=array();
            $aData=array();
            foreach(glob($this->_getServerlogDir().'/'.$this->_sFileFilter_serverlog) as $sMyJobfile){
                // echo "DEBUG: log file $sMyJobfile<br>";
                $aData=$this->_parseLogfile($sMyJobfile);
                $aData['server']=$this->_sActiveServer;
                $aData['logfile']= $this->_sActiveServer.'/'.basename($sMyJobfile);
                $aReturn[$aData['SCRIPTSTARTTIME'].$sMyJobfile]=$aData;
            }
            rsort($aReturn);
            return $aReturn;
        }
        
        // ----------------------------------------------------------------------
        // public setter
        // ----------------------------------------------------------------------
       
        /**
         * set which server is selected
         * The given server must exist as directory (that contains its logs)
         * @param  string  $sServer  server name
         * @return string
         */
        public function setServer($sServer){
            $this->_sActiveServer=false;
            if($sServer==='ALL'){
                return false;
            }
            if($sServer && !array_key_exists($sServer, $this->_aServers)){
                echo "WARNING: server [$sServer] does not exist<br>";
                return false;
            }
            $this->_sActiveServer=$sServer;
            
            return $this->_sActiveServer;
        }
        
        
    }