Code

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