X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=gosa-core%2Finclude%2Fclass_stats.inc;h=b311baf0f454130e1e34c4240d10e040749ba01c;hb=9b5f55ae87da782b052b78313e063c2e864b4a3f;hp=e2d64986c57e73df1ad188e36c3164687182138f;hpb=34da290273bd0eda5ea52528e0f6a0a832958988;p=gosa.git diff --git a/gosa-core/include/class_stats.inc b/gosa-core/include/class_stats.inc index e2d64986c..b311baf0f 100644 --- a/gosa-core/include/class_stats.inc +++ b/gosa-core/include/class_stats.inc @@ -2,35 +2,171 @@ 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,294 +179,156 @@ class stats TIMESTAMP INTEGER, MTIMESTAMP REAL, DURATION REAL, + RENDER_TIME REAL, AMOUNT INTEGER, MEMORY_USAGE INTEGER, - CPU_LOAD FLOAT + CPU_LOAD FLOAT, + INFO BLOB )"; - $ret = sqlite_query($query, $res); + $ret = sqlite_query($query, $handle); } - - return($res); } - - static function log($type, $plugin, $category, $action, $amount = 1, $duration = 0) + /*! \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) + (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}')"; + ('{$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 "
";
-
-        $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 
-            FROM {$TABLE_NAME}
-            WHERE plugin != 'LDAP' 
-            GROUP BY PLUGIN 
-            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 
-            FROM {$TABLE_NAME}
-            WHERE   TYPE = 'plugin'
-             AND    PLUGIN != 'LDAP'
-             AND    ACTION = 'open'
-            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";
+        if(file_exists($path.'/'.$filename)){
+            unlink($path.'/'.$filename);
         }
-        echo "
"; - echo sqlite_error_string($ret); } } - ?>