Code

Updated dumpTable function
[gosa.git] / gosa-core / include / class_stats.inc
index 06cf3f85ae0f07707528ae173e728caa8270606a..b311baf0f454130e1e34c4240d10e040749ba01c 100644 (file)
 
 class stats 
 {
-    
-    static protected $last_cpu_load = "";
-    static protected $last_cpu_load_stamp = 0;
 
+    static protected $lastCpuLoad = "";
+    static protected $lastCpuLoadTimestamp = 0;
 
-    static function checkDatabase()
+    static protected $tableName = "stats";
+
+    static protected $lastHandle = NULL;
+    static protected $statsEnabled = FALSE;
+
+
+    /*! \brief     This method tries to connect the GOsa-stats database and 
+     *              then returns a database handle on success else NULL.
+     *
+     *             (The GOsa-stats database has to be enabled : statsDatabaseEnabled/statsDatabaseDirectory)
+     *
+     *             This database will then contain information about the use of GOsa,
+     *              no customer data will be stored.
+     *
+     *  @return     handle      Returns a sqlite database handle.
+     */
+    static function getDatabaseHandle($filename = '')
     {
-        $TABLE_NAME = 'stats';
-    
-        // Check for modules
-        // php5-sqlite
+        // We cannot log while the path to store logs in is empty.
+        global $config;
+
+        // Check if Logging is enabled
+        if(!is_object($config) || ! $config instanceOf config){
+            return(NULL);
+        }
+
+        $path = $config->get_cfg_value('core', 'statsDatabaseDirectory');
+        if(empty($path)){
+            return(NULL);
+        }
+
+        // Check if path exists, if not try to create it
+        if(!is_dir($path) ){
+            $res = @mkdir($path);
+            if(!$res){
+                return(NULL);
+            }
+        }
+
+        // Append a date suffix to the database file, to prevent huge and unusable database files.
+        if($filename == ''){
+            $filename = date('Y-m-d');
+        }
+        $tableFile = $path.'/'.$filename;
+
+        // Try to return last valid handle.
+        if(file_exists($tableFile) && 
+                isset(stats::$lastHandle[$filename]) && 
+                stats::$lastHandle[$filename] != NULL &&
+                is_resource(stats::$lastHandle[$filename])){
+            return(stats::$lastHandle[$filename]);
+        }
+
+        // Get statsFile property 
+        stats::$statsEnabled = $config->boolValueIsTrue('core', 'statsDatabaseEnabled');
+        if(!stats::$statsEnabled){
+            return(NULL);
+        }
 
+        // Check for SQLite extension
+        if(!stats::checkSQLiteExtension()){
+            return(NULL);
+        }
+
+        // Check if we are able to read/write the given database file.
+        if(!is_writeable($path) && !is_writeable(dirname($tableFile))){
+            return(NULL);
+        }
 
         // Try to create database, if it exists just open it.
-        $res = sqlite_open('/var/spool/gosa/stats', 0666, $error);
-        if(!$res){
-            return($res);
+        $handle = sqlite_open($tableFile, 0666, $error);
+        if($handle){
+            stats::createDatabaseOnDemand($handle);
         }
+        stats::$lastHandle[$filename] = $handle;
+        return($handle);
+    }
 
-        // Delete Table 
+
+    /*! \brief      Returns a list of all created stat files
+     *  @return     Array   A list of all currently stored stat files.
+     */
+    static function getLocalStatFiles()
+    {
+        $res = array();
+
+        // Check if we're creating logs right now.
+        if(stats::getDatabaseHandle()){
+            global $config;
+
+            // Walk through all files found in the storage path
+            $path = $config->get_cfg_value('core', 'statsDatabaseDirectory');
+            $dir = opendir($path);
+            while($file = readdir($dir)){
+                if(is_file($path.'/'.$file)) $res[] = $file;
+            }
+        } 
+        return($res);   
+    }
+
+
+    /*! \brief      Check whether the qlite extension is available or not.
+     *  @return     boolean     TRUE on success else FALSE
+     */  
+    static function checkSQLiteExtension()
+    {
+        return(function_exists('sqlite_popen'));
+    }
+
+
+    /*! \brief      Drops the current stats table and thus enforces a recreation.
+     *  @param      handle      The database handle to use.
+     */  
+    static function dropTable($handle)
+    {
+        $TABLE_NAME = stats::$tableName;
         $query = "DROP TABLE '{$TABLE_NAME}'";
-#        $ret = sqlite_query($query, $res);
+        $ret = sqlite_query($query, $handle);
+        stats::$lastHandle = NULL;
+        stats::getDatabaseHandle();
+    }
+
+
+    /*! \brief      Returns the currently used amount of memory form the PHP process.
+     *  @return     int     The amount of bytes used for the PHP process.
+     */  
+    static function get_memory_usage()
+    {
+        return(memory_get_usage());
+    }
+
+
+    /*! \brief      Returns the current CPU load. 
+     *              The result will be cached and one updated every 5 seconds.
+     *  @return     float       The current 'cpu_load'.
+     */  
+    static function get_cpu_load()
+    {
+        $cur = time();
+        if(empty(stats::$lastCpuLoad) || (($cur - stats::$lastCpuLoadTimestamp) >= 5 )){
+            list($one, $five, $ten) =preg_split("/ /",shell_exec('cat /proc/loadavg'));
+            stats::$lastCpuLoad = $one;
+            stats::$lastCpuLoadTimestamp = $cur;
+        }
+        return(stats::$lastCpuLoad);
+    }
+
+
+    /*! \brief      This method checks if the 'stats' table is already present,
+     *               if it is not then it will be created.
+     *  @param      handle      The sqlite database handle
+     */  
+    static function createDatabaseOnDemand($handle)
+    {
+        $TABLE_NAME = stats::$tableName;
 
-        // List Tables an check if there is already everything we need.
+        // List Tables an check if there is already everything we need,
+        //  if not create it.
         $query = "SELECT name FROM sqlite_master WHERE type='table' and name='{$TABLE_NAME}'";
-        $ret = sqlite_query($query, $res);
+        $ret = sqlite_query($query, $handle);
         if(!count(sqlite_fetch_all($ret))){
-
-            // Check for table existance    
             $query = "
                 CREATE TABLE {$TABLE_NAME} (
                         ID              INTEGER PRIMARY KEY,
@@ -43,296 +179,156 @@ class stats
                         TIMESTAMP       INTEGER,
                         MTIMESTAMP      REAL,
                         DURATION        REAL,
+                        RENDER_TIME     REAL,
                         AMOUNT          INTEGER,
                         MEMORY_USAGE    INTEGER,
                         CPU_LOAD        FLOAT,
                         INFO            BLOB
                         )";
-            $ret = sqlite_query($query, $res);
+            $ret = sqlite_query($query, $handle);
         }
-
-        return($res);
     }
 
 
-
+    /*! \brief      Creates a new 'stats' table entry.
+     *              -> Logs a GOsa action/activity in the sqlite stats table.
+     *  @param      string  type        The action type, e.g. ldap/plugin/management
+     *  @param      string  plugin      The plugin name, e.g. userManagement/user/posixAccount
+     *  @param      string  category    The plugin category e.g. users/servers/groups
+     *  @param      string  action      The action done e.g. edit/view/open/move
+     *  @param      int     amount      The amount, e.g. for multiple edit
+     *  @param      float   duration    The elapsed time.
+     *  @param      string  info        Some infos form the action, e.g. the used hashing mehtod for pwd changes.
+     */  
     static function log($type, $plugin, $category, $action, $amount = 1, $duration = 0, $info ='')
     {
         global $config;
         global $clicks;
+        global $overallRenderTimer;
+
+        // Get database handle, if it is invalid (NULL) return without creating stats
+        $res = stats::getDatabaseHandle();
+        if(!$res) return;
+
+        // Ensure that 'clicks' and 'overallRenderTimer' are present and set correctly, 
+        //  if not simply create them with dummy values... 
+        //   -- 'clicks' is a counter wich is set in main.php -> Number of page reloads 
+        //   -- 'overallRenderTimer' is set in main.php -> timestamp of rendering start.
+        if(!isset($clicks) || empty($clicks)) $clicks = 0;
+        if(!isset($overallRenderTimer)){
+            $renderTime = 0;
+        }else{
+            $renderTime = microtime(TRUE) - $overallRenderTimer;
+
+            // Now set the overallRenderTimer to the current timestamp - else 
+            //  we will not be able to sum up the render time in a single SQL statement.
+            $overallRenderTimer = microtime(TRUE);
+        }
+
+        // Prepare values to be useable within a database
+        $uuid = $config->getGOsaUUID();
         $type           = sqlite_escape_string($type);
         $plugin         = sqlite_escape_string($plugin);
         $action         = sqlite_escape_string($action);
         $timestamp      = time();
-        $mtimestamp     = microtime(TRUE);
-        $uuid           = $config->getGOsaUUID();
+        $mtimestamp     = number_format(microtime(TRUE), 4,'.','');
         $amount         = sqlite_escape_string($amount);
-        $duration       = sqlite_escape_string($duration);
+        $duration       = sqlite_escape_string(number_format($duration, 4,'.',''));
+        $renderTime     = sqlite_escape_string(number_format($renderTime, 4,'.',''));
         $info           = sqlite_escape_string($info);
         $clicks         = sqlite_escape_string($clicks);
         $memory_usage   = sqlite_escape_string(stats::get_memory_usage());
-        $cpu_load       = sqlite_escape_string(stats::get_cpu_load());
+        $cpu_load       = sqlite_escape_string(number_format(stats::get_cpu_load(),4,'.',''));
 
+        // Clean up category, which usally comes from acl_category and may still contain 
+        //  some special chars like /
         $tmp = array();
         foreach($category as $cat){
             $tmp[] = trim($cat, '\/,; ');
         }
         $category = sqlite_escape_string(implode($tmp, ', '));
 
-        $res = stats::checkDatabase();
-        $TABLE_NAME = 'stats';
+        // Create insert statement.
+        $TABLE_NAME = stats::$tableName;
         $query = "
             INSERT INTO {$TABLE_NAME}
-                (ACTID, TYPE, PLUGIN, CATEGORY, ACTION, UUID, MTIMESTAMP, TIMESTAMP, AMOUNT, DURATION, MEMORY_USAGE, CPU_LOAD, INFO) 
+        (ACTID, TYPE, PLUGIN, CATEGORY, ACTION, UUID, MTIMESTAMP, TIMESTAMP, 
+         AMOUNT, DURATION, RENDER_TIME, MEMORY_USAGE, CPU_LOAD, INFO) 
             VALUES 
-                ('{$clicks}','{$type}','{$plugin}','{$category}','{$action}','{$uuid}',
-                    '{$mtimestamp}','{$timestamp}','{$amount}','{$duration}','{$memory_usage}','{$cpu_load}','{$info}')";
+            ('{$clicks}','{$type}','{$plugin}','{$category}','{$action}','{$uuid}',
+             '{$mtimestamp}','{$timestamp}','{$amount}','{$duration}','{$renderTime}',
+             '{$memory_usage}','{$cpu_load}','{$info}')";
         sqlite_query($query, $res);
     }
 
-    static function get_memory_usage()
-    {
-        return(memory_get_usage());
-    }
 
-    static function get_cpu_load()
-    {
-        $cur = time();
-        if(empty(stats::$last_cpu_load) || (($cur - stats::$last_cpu_load_stamp) >=2 )){
-            list($one, $five, $ten) =preg_split("/ /",shell_exec('cat /proc/loadavg'));
-            stats::$last_cpu_load = $one;
-            stats::$last_cpu_load_stamp = $cur;
-        }
-        return(stats::$last_cpu_load);
-    }
-    
-    static function show()
+    /*! \brief      Closes all sqlite handles opened by this class
+     */ 
+    static function closeHandles()
     {
-        $res = stats::checkDatabase();
-        $TABLE_NAME = 'stats';
-        $query = "SELECT * FROM {$TABLE_NAME} ORDER BY MTIMESTAMP";
-        $query = "SELECT PLUGIN, ACTION, MAX(DURATION) as 'DURATION' FROM {$TABLE_NAME} WHERE ACTION='modify' GROUP BY PLUGIN,ACTION ";
-        $query = "SELECT * FROM {$TABLE_NAME} ORDER BY ID DESC LIMIT 30";
-        $query = "SELECT * FROM {$TABLE_NAME} WHERE plugin != 'LDAP' ORDER BY ID DESC LIMIT 30";
-        $ret = sqlite_query($query, $res);
-        echo "<pre>";
-
-        $colSize = 16;
-
-        $title = FALSE;
-        foreach(sqlite_fetch_all($ret) as $entry){
-            if(!$title){
-                foreach($entry as $key => $str) {
-                    if(is_numeric($key)) continue;
-                    echo str_pad($key,$colSize,' ')."|";
-                }
-                echo "\n";
-                foreach($entry as $key => $str) {
-                    if(is_numeric($key)) continue;
-                    echo str_pad('',$colSize,'-')."-";
-                }
-                echo "\n";
-                $title = TRUE;
+        foreach(stats::lastHandle as $handle){
+            if($handle && is_resource($handle)){
+                sqlite_close($handle);
             }
-            foreach($entry as $key => $str){
-                if(is_numeric($key)) continue;
-                if($key == "DURATION" || $key == "MTIMESTAMP" || $key == "CPU_LOAD"){
-                    $str = sprintf("%0.4f", preg_replace("/,/",".",$str));
-                    echo str_pad($str,$colSize,' ', STR_PAD_LEFT)."|"; 
-                }else{
-                    echo str_pad($str,$colSize,' ')."|"; 
-                }
-            }
-            echo "\n";
         }
-        echo sqlite_error_string($ret);
-
-        echo "\n------ \n";
-        echo "Time spent per plugin-category \n";
-        echo "------ \n";
-
-        $query = "
-            SELECT SUM(DURATION) AS DUR, CATEGORY 
-            FROM {$TABLE_NAME}
-            WHERE plugin != 'LDAP' 
-            GROUP BY CATEGORY 
-            ORDER BY DUR DESC LIMIT 10";
-        $ret = sqlite_query($query, $res);
-
-        $colSize = 16;
-        $title = FALSE;
-        foreach(sqlite_fetch_all($ret) as $entry){
-            foreach($entry as $key => $str){
-                if(is_numeric($key)) continue;
-                echo str_pad($str,$colSize,' ')."|"; 
-            }
-            echo "\n";
-        }
-
-
-        echo sqlite_error_string($ret);
-
-        echo "\n------ \n";
-        echo "Time spent per plugin \n";
-        echo "------ \n";
-
-        $query = "
-            SELECT SUM(DURATION) AS DUR, PLUGIN, UUID 
-            FROM {$TABLE_NAME}
-            WHERE plugin != 'LDAP' 
-            GROUP BY PLUGIN, UUID 
-            ORDER BY DUR DESC LIMIT 10";
-        $ret = sqlite_query($query, $res);
-
-        $colSize = 16;
-        $title = FALSE;
-        foreach(sqlite_fetch_all($ret) as $entry){
-            foreach($entry as $key => $str){
-                if(is_numeric($key)) continue;
-                echo str_pad($str,$colSize,' ')."|"; 
-            }
-            echo "\n";
-        }
-        echo sqlite_error_string($ret);
-
- # * Anzahl von Objekttypen
- # * Anzahl der Löschungen pro Objekttyp
- # * Anzahl der Erzeugungen pro Objekttyp
- # * Anzahl der Bearbeitungen pro Objekttyp
- # * Anzahl der Verschiebungen pro Objekttyp
- # * Anzahl der Mehrfachbearbeitungen pro Objekttyp
- # * Antwortzeiten pro aktion
- # * Anzahl der Passwortänderungen
- # * Anzahl der unterschiedlichen Anmeldungen
-
+    }
 
-        echo "\n------ \n";
-        echo "Actions done per plugin \n";
-        echo "------ \n";
 
-        $query = "
-            SELECT COUNT(ACTION) as CNT, ACTION, PLUGIN 
-            FROM {$TABLE_NAME}
-            WHERE   TYPE = 'plugin'
-             AND    PLUGIN != 'LDAP'
-            GROUP BY ACTION,PLUGIN 
-            ORDER BY CNT DESC LIMIT 30";
-        $ret = sqlite_query($query, $res);
-
-        $colSize = 16;
-        $title = FALSE;
-        foreach(sqlite_fetch_all($ret) as $entry){
-            foreach($entry as $key => $str){
-                if(is_numeric($key)) continue;
-                echo str_pad($str,$colSize,' ')."|"; 
-            }
-            echo "\n";
-        }
-        echo sqlite_error_string($ret);
+    /*! \brief      This method returns all entries of the GOsa-stats table.
+     *              You can limit the result by setting the from/to parameter (timestamp).
+     *  @param      int     from    The timestamp to start the result from. 
+     *  @param      int     to      The timestamp to end the request.
+     *  @return     array           An array containing the requested entries.
+     */  
+    static function dumpTables($filename)
+    {
+        // Get database connection
+        $TABLE_NAME = stats::$tableName;
+        $handle = stats::getDatabaseHandle($filename);
+        if(!$handle) return;        
+
+        $query = 
+            "  SELECT ".
+            "      TYPE, PLUGIN, CATEGORY, ACTION, ".
+            "      UUID, DATE(TIMESTAMP, 'unixepoch') as date, ".
+            "      AVG(DURATION), AVG(RENDER_TIME), SUM(AMOUNT), ".
+            "      AVG(MEMORY_USAGE), AVG(CPU_LOAD), INFO ".
+            "  FROM ".
+            "      stats ".
+            "  GROUP BY ".
+            "      TYPE, PLUGIN, CATEGORY, ACTION, UUID, date, INFO ".
+            "  ORDER BY ".
+            "      ID ";
+
+        // Create Filter and start query
+        $ret = sqlite_array_query($query, $handle, SQLITE_ASSOC);
+        return($ret);
+    }
 
-        echo "\n------ \n";
-        echo "'create' actions done per plugin  (5 most)\n";
-        echo "------ \n";
 
-        $query = "
-            SELECT COUNT(ACTION) as CNT, ACTION, PLUGIN 
-            FROM {$TABLE_NAME}
-            WHERE   TYPE = 'plugin'
-             AND    PLUGIN != 'LDAP'
-             AND    ACTION = 'create'
-            GROUP BY ACTION,PLUGIN 
-            ORDER BY CNT DESC LIMIT 5";
-        $ret = sqlite_query($query, $res);
-
-        $colSize = 16;
-        $title = FALSE;
-        foreach(sqlite_fetch_all($ret) as $entry){
-            foreach($entry as $key => $str){
-                if(is_numeric($key)) continue;
-                echo str_pad($str,$colSize,' ')."|"; 
-            }
-            echo "\n";
+    static function removeStatsFile($filename)
+    {
+        // Get statsFile property 
+        global $config;
+        $path = $config->get_cfg_value('core', 'statsDatabaseDirectory');
+        stats::$statsEnabled = $config->boolValueIsTrue('core', 'statsDatabaseEnabled');
+        if(!stats::$statsEnabled){
+            return(NULL);
         }
-        echo sqlite_error_string($ret);
-
-        echo "\n------ \n";
-        echo "'move' actions done per plugin  (5 most)\n";
-        echo "------ \n";
 
-        $query = "
-            SELECT COUNT(ACTION) as CNT, ACTION, PLUGIN 
-            FROM {$TABLE_NAME}
-            WHERE   TYPE = 'plugin'
-             AND    PLUGIN != 'LDAP'
-             AND    ACTION = 'move'
-            GROUP BY ACTION,PLUGIN 
-            ORDER BY CNT DESC LIMIT 5";
-        $ret = sqlite_query($query, $res);
-
-        $colSize = 16;
-        $title = FALSE;
-        foreach(sqlite_fetch_all($ret) as $entry){
-            foreach($entry as $key => $str){
-                if(is_numeric($key)) continue;
-                echo str_pad($str,$colSize,' ')."|"; 
-            }
-            echo "\n";
+        // We cannot log while the path to store logs in is empty.
+        if(empty($path)){
+            return(NULL);
         }
-        echo sqlite_error_string($ret);
-
-        echo "\n------ \n";
-        echo "'view' actions done per plugin  (5 most)\n";
-        echo "------ \n";
 
-        $query = "
-            SELECT COUNT(ACTION) as CNT, ACTION, PLUGIN 
-            FROM {$TABLE_NAME}
-            WHERE   TYPE = 'plugin'
-             AND    PLUGIN != 'LDAP'
-             AND    ACTION = 'view'
-            GROUP BY ACTION,PLUGIN 
-            ORDER BY CNT DESC LIMIT 5";
-        $ret = sqlite_query($query, $res);
-
-        $colSize = 16;
-        $title = FALSE;
-        foreach(sqlite_fetch_all($ret) as $entry){
-            foreach($entry as $key => $str){
-                if(is_numeric($key)) continue;
-                echo str_pad($str,$colSize,' ')."|"; 
-            }
-            echo "\n";
+        // Check if file exists and then remove it
+        if(isset(stats::$lastHandle[$filename]) && is_resource(stats::$lastHandle[$filename])){
+            sqlite_close(stats::$lastHandle[$filename]);
         }
-        echo sqlite_error_string($ret);
-
-        echo "\n------ \n";
-        echo "'open' actions done per plugin  (5 most)\n";
-        echo "------ \n";
-
-        $query = "
-            SELECT COUNT(ACTION) as CNT, ACTION, PLUGIN, UUID 
-            FROM {$TABLE_NAME}
-            WHERE   TYPE = 'plugin'
-             AND    PLUGIN != 'LDAP'
-             AND    ACTION = 'open'
-            GROUP BY ACTION,PLUGIN, UUID
-            ORDER BY CNT DESC LIMIT 5";
-        $ret = sqlite_query($query, $res);
-
-        $colSize = 16;
-        $title = FALSE;
-        foreach(sqlite_fetch_all($ret) as $entry){
-            foreach($entry as $key => $str){
-                if(is_numeric($key)) continue;
-                echo str_pad($str,$colSize,' ')."|"; 
-            }
-            echo "\n";
+        if(file_exists($path.'/'.$filename)){
+            unlink($path.'/'.$filename);
         }
-        echo "</pre>";
-        echo sqlite_error_string($ret);
     }
 }
 
-
 ?>