File modules/cms/status/Metrics.class.php

Last commit: Thu Jun 5 01:33:56 2025 +0200	Jan Dankert	New: Metric for cpu load
1 <?php 2 3 namespace cms\status; 4 5 6 use cms\base\Startup; 7 use configuration\Config; 8 use configuration\ConfigurationLoader; 9 use database\Database; 10 use util\cache\FileCache; 11 use util\FileUtils; 12 use util\Http; 13 14 /** 15 * Create metrics for Prometheus/OpenMetrics. 16 */ 17 class Metrics 18 { 19 private static $metrics = []; 20 21 /** 22 * prefix for all metrics. 23 */ 24 const METRIC_PREFIX = 'cms'; 25 26 /** 27 * Creating the metrics 28 */ 29 public static function execute() 30 { 31 // get interpreter metrics 32 self::addGauge('interpreter_version','version of PHP Interpreter',PHP_VERSION_ID ,['type'=>'all' ]); 33 self::addGauge('interpreter_version','version of PHP Interpreter',PHP_MAJOR_VERSION ,['type'=>'major' ]); 34 self::addGauge('interpreter_version','version of PHP Interpreter',PHP_MINOR_VERSION ,['type'=>'minor' ]); 35 self::addGauge('interpreter_version','version of PHP Interpreter',PHP_RELEASE_VERSION,['type'=>'release']); 36 37 // get system metrics 38 foreach( getrusage() as $item=>$value ) 39 self::addGauge('resource_usage','current resource usage',$value,['usage'=>$item] ); 40 41 # get memory metrics 42 self::addGauge('memory','amount of memory, in bytes, thats currently being allocated', memory_get_usage(true),['usage'=>'allocated']); 43 self::addGauge('memory','amount of memory, in bytes, thats currently being allocated', memory_get_usage() ,['usage'=>'used'] ); 44 self::addGauge('memory_peak','peak of memory, thats been allocated', memory_get_peak_usage(true),['usage'=>'allocated'] ); 45 self::addGauge('memory_peak','peak of memory, thats been allocated', memory_get_peak_usage() ,['usage'=>'used' ] ); 46 47 # get load 48 if (function_exists('sys_getloadavg')) { 49 if ( $load = sys_getloadavg() ) { 50 self::addGauge('load','load average',$load[0] ,['minutes'=>'1']); 51 self::addGauge('load','load average',$load[1] ,['minutes'=>'5']); 52 self::addGauge('load','load average',$load[2] ,['minutes'=>'15']); 53 } 54 } 55 // get metrics from databases 56 $config = new Config( self::getConfiguration() ); 57 foreach( $config->subset('database')->subsets() as $dbName => $dbConfig ) { 58 59 if ($dbConfig->is('enabled', true)) { 60 61 try { 62 $db = new Database($dbConfig->subset('read')->getConfig() + $dbConfig->getConfig()); 63 $dbLabel = ['db'=>$dbName]; 64 $sql = $db->sql( 'SELECT COUNT(*) FROM {{object}}' ); 65 self::addGauge('db_objects','count of all nodes', $sql->getOne(),$dbLabel ); 66 67 $sql = $db->sql( 'SELECT COUNT(*) FROM {{value}}' ); 68 self::addGauge('db_values','count of all values', $sql->getOne(),$dbLabel ); 69 70 $sql = $db->sql( 'SELECT COUNT(*) FROM {{user}}' ); 71 self::addGauge('db_users','count of all users', $sql->getOne(),$dbLabel ); 72 73 $sql = $db->sql( 'SELECT COUNT(*) FROM {{group}}' ); 74 self::addGauge('db_groups','count of all groups', $sql->getOne(),$dbLabel ); 75 76 $sql = $db->sql( 'SELECT COUNT(*) FROM {{acl}}' ); 77 self::addGauge('db_acl','count of all rights', $sql->getOne(),$dbLabel ); 78 79 $sql = $db->sql( 'SELECT SUM(size) FROM {{file}}'); 80 self::addGauge('db_blob_size','size of all files', $sql->getOne(),$dbLabel ); 81 82 $db->disconnect(); 83 84 } catch (\Exception $e) { 85 echo '# Error: '.$e->getMessage()."\n"; 86 } 87 88 } 89 90 } 91 92 // Get metrics for cache dir 93 $cacheDir = FileUtils::getTempDir(); 94 95 if ( $files = scandir($cacheDir) ) 96 { 97 $count = 0; 98 $size = 0; 99 foreach ( $files as $file ) { 100 if ( substr($file,0,strlen(FileCache::CACHE_FILENAME_PREFIX))==FileCache::CACHE_FILENAME_PREFIX ) { 101 $count ++; 102 $size += filesize($cacheDir.'/'.$file); 103 } 104 } 105 106 self::addGauge('cache_files','Files in cache dir',$count); 107 self::addGauge('cache_bytes','Sum file size in cache dir',$size); 108 } 109 110 // if client accepts OpenMetrics format, we will serve it. 111 if ( in_array('application/openmetrics-text',Http::getAccept() ) ) 112 header('Content-Type: application/openmetrics-text; version=1.0.0; charset=UTF-8'); 113 else 114 header('Content-Type: text/plain; charset=UTF-8'); 115 116 $output = self::renderMetrics(); 117 118 if (!headers_sent()) { 119 // HTTP Spec: 120 // "Applications SHOULD use this field to indicate the transfer-length of the 121 // message-body, unless this is prohibited by the rules in section 4.4." 122 // 123 // And the overhead of 'Transfer-Encoding: chunked' is eliminated... 124 header('HTTP/1.0 200 OK' ); 125 header('Content-Length: ' . strlen($output)); 126 } 127 128 echo $output; 129 } 130 131 132 /** 133 * Creating an openmetrics gauge. 134 * 135 * @param $name string Name of the metric 136 * @param $description string short description for tooltips 137 * @param $value int a numeric value 138 */ 139 private static function addGauge($name, $description, $value, $label=[]) 140 { 141 if ( ! array_key_exists($name,self::$metrics) ) { 142 self::$metrics[$name] = ['name'=>$name,'description'=>$description,'values'=>[]]; 143 } 144 self::$metrics[$name]['values'][] = ['labels'=>$label,'value'=>$value]; 145 } 146 147 /** 148 * Creating an openmetrics gauge. 149 * 150 * @param $name string Name of the metric 151 * @param $description string short description for tooltips 152 * @param $value int a numeric value 153 * @return string 154 */ 155 private static function renderMetrics() 156 { 157 $output = ''; 158 foreach ( self::$metrics as $name => $metric ) { 159 $metricName = '' . self::METRIC_PREFIX . '_' .$name; // prefix all metrics because they are app related. 160 161 $output .= "# TYPE ".$metricName." gauge\n"; 162 $output .= "# HELP ".$metricName." ".$metric['description']."\n"; 163 foreach ( $metric['values'] as $value ) { 164 $output .= $metricName; 165 if ($value['labels']) { 166 $output .= "{"; 167 $output .= implode(',',array_map( function($key,$val) { 168 return $key."=\"".$val."\""; 169 },array_keys($value['labels']),$value['labels'])); 170 $output .= "}"; 171 } 172 $output .= ' '.$value['value']."\n"; 173 174 } 175 176 177 } 178 return $output; 179 180 } 181 182 private static function getConfiguration() { 183 $configFile = getenv( 'CMS_CONFIG_FILE' ); 184 if ( ! $configFile ) 185 $configFile = Startup::DEFAULT_CONFIG_FILE; 186 187 $configLoader = new ConfigurationLoader( $configFile ); 188 return $configLoader->load(); 189 } 190 }
Download modules/cms/status/Metrics.class.php
History Thu, 5 Jun 2025 01:33:56 +0200 Jan Dankert New: Metric for cpu load Thu, 5 Jun 2025 01:10:53 +0200 Jan Dankert New: More Metrics from the connected databases Mon, 28 Oct 2024 22:27:13 +0100 Jan Dankert New: Metrics in the Openmetrics text format. For now only some system infos, maybe some special cms metrics in the future.