Code

Updated stats handling fixed float problem
[gosa.git] / gosa-core / include / class_stats.inc
1 <?php
3 class stats 
4 {
6     static protected $lastCpuLoad = "";
7     static protected $lastCpuLoadTimestamp = 0;
9     static protected $tableName = "stats";
11     static protected $lastHandle = NULL;
12     static protected $statsEnabled = FALSE;
15     /*! \brief     This method tries to connect the GOsa-stats database and 
16      *              then returns a database handle on success else NULL.
17      *
18      *             (The GOsa-stats database has to be enabled : statsDatabaseEnabled/statsDatabaseDirectory)
19      *
20      *             This database will then contain information about the use of GOsa,
21      *              no customer data will be stored.
22      *
23      *  @return     handle      Returns a sqlite database handle.
24      */
25     static function getDatabaseHandle($filename = '')
26     {
27         // We cannot log while the path to store logs in is empty.
28         global $config;
29         $path = $config->get_cfg_value('core', 'statsDatabaseDirectory');
30         if(empty($path)){
31             return(NULL);
32         }
34         // Check if path exists, if not try to create it
35         if(!is_dir($path) ){
36             $res = @mkdir($path);
37             if(!$res){
38                 return(NULL);
39             }
40         }
42         // Append a date suffix to the database file, to prevent huge and unusable database files.
43         if($filename == ''){
44             $filename = date('Y-m-d');
45         }
46         $tableFile = $path.'/'.$filename;
48         // Try to return last valid handle.
49         if(file_exists($tableFile) && 
50                 isset(stats::$lastHandle[$filename]) && 
51                 stats::$lastHandle[$filename] != NULL &&
52                 is_resource(stats::$lastHandle[$filename])){
53             return(stats::$lastHandle[$filename]);
54         }
56         // Check if Logging is enabled
57         if(!is_object($config) || ! $config instanceOf config){
58             return(NULL);
59         }
61         // Get statsFile property 
62         stats::$statsEnabled = $config->boolValueIsTrue('core', 'statsDatabaseEnabled');
63         if(!stats::$statsEnabled){
64             return(NULL);
65         }
67         // Check for SQLite extension
68         if(!stats::checkSQLiteExtension()){
69             return(NULL);
70         }
72         // Check if we are able to read/write the given database file.
73         if(!is_writeable($path) && !is_writeable(dirname($tableFile))){
74             return(NULL);
75         }
77         // Try to create database, if it exists just open it.
78         $handle = sqlite_open($tableFile, 0666, $error);
79         if($handle){
80             stats::createDatabaseOnDemand($handle);
81         }
82         stats::$lastHandle[$filename] = $handle;
83         return($handle);
84     }
87     /*! \brief      Returns a list of all created stat files
88      *  @return     Array   A list of all currently stored stat files.
89      */
90     static function getLocalStatFiles()
91     {
92         $res = array();
94         // Check if we're creating logs right now.
95         if(stats::getDatabaseHandle()){
96             global $config;
98             // Walk through all files found in the storage path
99             $path = $config->get_cfg_value('core', 'statsDatabaseDirectory');
100             $dir = opendir($path);
101             while($file = readdir($dir)){
102                 if(is_file($path.'/'.$file)) $res[] = $file;
103             }
104         } 
105         return($res);   
106     }
109     /*! \brief      Check whether the qlite extension is available or not.
110      *  @return     boolean     TRUE on success else FALSE
111      */  
112     static function checkSQLiteExtension()
113     {
114         return(function_exists('sqlite_popen'));
115     }
118     /*! \brief      Drops the current stats table and thus enforces a recreation.
119      *  @param      handle      The database handle to use.
120      */  
121     static function dropTable($handle)
122     {
123         $TABLE_NAME = stats::$tableName;
124         $query = "DROP TABLE '{$TABLE_NAME}'";
125         $ret = sqlite_query($query, $handle);
126         stats::$lastHandle = NULL;
127         stats::getDatabaseHandle();
128     }
131     /*! \brief      Returns the currently used amount of memory form the PHP process.
132      *  @return     int     The amount of bytes used for the PHP process.
133      */  
134     static function get_memory_usage()
135     {
136         return(memory_get_usage());
137     }
140     /*! \brief      Returns the current CPU load. 
141      *              The result will be cached and one updated every 5 seconds.
142      *  @return     float       The current 'cpu_load'.
143      */  
144     static function get_cpu_load()
145     {
146         $cur = time();
147         if(empty(stats::$lastCpuLoad) || (($cur - stats::$lastCpuLoadTimestamp) >= 5 )){
148             list($one, $five, $ten) =preg_split("/ /",shell_exec('cat /proc/loadavg'));
149             stats::$lastCpuLoad = $one;
150             stats::$lastCpuLoadTimestamp = $cur;
151         }
152         return(stats::$lastCpuLoad);
153     }
156     /*! \brief      This method checks if the 'stats' table is already present,
157      *               if it is not then it will be created.
158      *  @param      handle      The sqlite database handle
159      */  
160     static function createDatabaseOnDemand($handle)
161     {
162         $TABLE_NAME = stats::$tableName;
164         // List Tables an check if there is already everything we need,
165         //  if not create it.
166         $query = "SELECT name FROM sqlite_master WHERE type='table' and name='{$TABLE_NAME}'";
167         $ret = sqlite_query($query, $handle);
168         if(!count(sqlite_fetch_all($ret))){
169             $query = "
170                 CREATE TABLE {$TABLE_NAME} (
171                         ID              INTEGER PRIMARY KEY,
172                         ACTID           INTEGER,
173                         TYPE            TEXT,
174                         PLUGIN          TEXT,
175                         CATEGORY        TEXT,
176                         ACTION          TEXT,
177                         UUID            TEXT,
178                         TIMESTAMP       INTEGER,
179                         MTIMESTAMP      REAL,
180                         DURATION        REAL,
181                         RENDER_TIME     REAL,
182                         AMOUNT          INTEGER,
183                         MEMORY_USAGE    INTEGER,
184                         CPU_LOAD        FLOAT,
185                         INFO            BLOB
186                         )";
187             $ret = sqlite_query($query, $handle);
188         }
189     }
192     /*! \brief      Creates a new 'stats' table entry.
193      *              -> Logs a GOsa action/activity in the sqlite stats table.
194      *  @param      string  type        The action type, e.g. ldap/plugin/management
195      *  @param      string  plugin      The plugin name, e.g. userManagement/user/posixAccount
196      *  @param      string  category    The plugin category e.g. users/servers/groups
197      *  @param      string  action      The action done e.g. edit/view/open/move
198      *  @param      int     amount      The amount, e.g. for multiple edit
199      *  @param      float   duration    The elapsed time.
200      *  @param      string  info        Some infos form the action, e.g. the used hashing mehtod for pwd changes.
201      */  
202     static function log($type, $plugin, $category, $action, $amount = 1, $duration = 0, $info ='')
203     {
204         global $config;
205         global $clicks;
206         global $overallRenderTimer;
208         // Get database handle, if it is invalid (NULL) return without creating stats
209         $res = stats::getDatabaseHandle();
210         if(!$res) return;
212         // Ensure that 'clicks' and 'overallRenderTimer' are present and set correctly, 
213         //  if not simply create them with dummy values... 
214         //   -- 'clicks' is a counter wich is set in main.php -> Number of page reloads 
215         //   -- 'overallRenderTimer' is set in main.php -> timestamp of rendering start.
216         if(!isset($clicks) || empty($clicks)) $clicks = 0;
217         if(!isset($overallRenderTimer)){
218             $renderTime = 0;
219         }else{
220             $renderTime = microtime(TRUE) - $overallRenderTimer;
222             // Now set the overallRenderTimer to the current timestamp - else 
223             //  we will not be able to sum up the render time in a single SQL statement.
224             $overallRenderTimer = microtime(TRUE);
225         }
227         // Prepare values to be useable within a database
228         $uuid = $config->getGOsaUUID();
229         $type           = sqlite_escape_string($type);
230         $plugin         = sqlite_escape_string($plugin);
231         $action         = sqlite_escape_string($action);
232         $timestamp      = time();
233         $mtimestamp     = number_format(microtime(TRUE), 4,'.','');
234         $amount         = sqlite_escape_string($amount);
235         $duration       = sqlite_escape_string(number_format($duration, 4,'.',''));
236         $renderTime     = sqlite_escape_string(number_format($renderTime, 4,'.',''));
237         $info           = sqlite_escape_string($info);
238         $clicks         = sqlite_escape_string($clicks);
239         $memory_usage   = sqlite_escape_string(stats::get_memory_usage());
240         $cpu_load       = sqlite_escape_string(number_format(stats::get_cpu_load(),4,'.',''));
242         // Clean up category, which usally comes from acl_category and may still contain 
243         //  some special chars like /
244         $tmp = array();
245         foreach($category as $cat){
246             $tmp[] = trim($cat, '\/,; ');
247         }
248         $category = sqlite_escape_string(implode($tmp, ', '));
250         // Create insert statement.
251         $TABLE_NAME = stats::$tableName;
252         $query = "
253             INSERT INTO {$TABLE_NAME}
254         (ACTID, TYPE, PLUGIN, CATEGORY, ACTION, UUID, MTIMESTAMP, TIMESTAMP, 
255          AMOUNT, DURATION, RENDER_TIME, MEMORY_USAGE, CPU_LOAD, INFO) 
256             VALUES 
257             ('{$clicks}','{$type}','{$plugin}','{$category}','{$action}','{$uuid}',
258              '{$mtimestamp}','{$timestamp}','{$amount}','{$duration}','{$renderTime}',
259              '{$memory_usage}','{$cpu_load}','{$info}')";
260         sqlite_query($query, $res);
261     }
264     /*! \brief      Closes all sqlite handles opened by this class
265      */ 
266     static function closeHandles()
267     {
268         foreach(stats::lastHandle as $handle){
269             if($handle && is_resource($handle)){
270                 sqlite_close($handle);
271             }
272         }
273     }
276     /*! \brief      This method returns all entries of the GOsa-stats table.
277      *              You can limit the result by setting the from/to parameter (timestamp).
278      *  @param      int     from    The timestamp to start the result from. 
279      *  @param      int     to      The timestamp to end the request.
280      *  @return     array           An array containing the requested entries.
281      */  
282     static function dumpTables($filename)
283     {
284         // Get database connection
285         $TABLE_NAME = stats::$tableName;
286         $handle = stats::getDatabaseHandle($filename);
287         if(!$handle) return;        
289         // Create Filter and start query
290         $filter = "SELECT * FROM {$TABLE_NAME} ORDER BY ID";
291         $ret = sqlite_array_query($filter, $handle, SQLITE_ASSOC);
292         return($ret);
293     }
296     static function removeStatsFile($filename)
297     {
298         // Get statsFile property 
299         global $config;
300         $path = $config->get_cfg_value('core', 'statsDatabaseDirectory');
301         stats::$statsEnabled = $config->boolValueIsTrue('core', 'statsDatabaseEnabled');
302         if(!stats::$statsEnabled){
303             return(NULL);
304         }
306         // We cannot log while the path to store logs in is empty.
307         if(empty($path)){
308             return(NULL);
309         }
311         // Check if file exists and then remove it
312         if(isset(stats::$lastHandle[$filename]) && is_resource(stats::$lastHandle[$filename])){
313             sqlite_close(stats::$lastHandle[$filename]);
314         }
315         if(file_exists($path.'/'.$filename)){
316             unlink($path.'/'.$filename);
317         }
318     }
321 ?>