diff --git a/README.md b/README.md index 621ff38..229e42e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ php artisan stat:mem The available stat commands are: +- `stat:cpu` - `stat:disk` - `stat:load` - `stat:mem` @@ -20,7 +21,8 @@ The available stat commands are: Forge Monitor provides alerting for several monitor types: -- `cpu_load` - CPU Load (%) +- `cpu_load` - System Load (%) +- `cpu` - CPU Usage (%) - `disk` - Free Disk Space (%) - `free_memory` - Free Memory (%) - `used_memory` - Used Memory (%) diff --git a/app/Console/Commands/CpuStatCommand.php b/app/Console/Commands/CpuStatCommand.php new file mode 100644 index 0000000..619fa92 --- /dev/null +++ b/app/Console/Commands/CpuStatCommand.php @@ -0,0 +1,65 @@ +monitors->isEmpty()) { + $this->verboseInfo("No CPU Load monitors configured..."); + + return; + } + + $this->monitors->each(function ($monitor) { + if (!$this->sampleTaken) { + $monitor->stat()->sample(); + + $this->sampleTaken = true; + } + })->each(function ($monitor) { + $this->verboseInfo("Testing {$monitor->key}..."); + + $monitor->stat()->test(); + }); + } +} diff --git a/app/CpuUsage.php b/app/CpuUsage.php new file mode 100644 index 0000000..a54f47b --- /dev/null +++ b/app/CpuUsage.php @@ -0,0 +1,22 @@ +type) { + case 'cpu': return new CpuUsage($this); case 'disk': return new DiskSpace($this); case 'cpu_load': return new LoadAvg($this); case 'free_memory': return new FreeMemory($this); diff --git a/app/Stats/CpuUsage.php b/app/Stats/CpuUsage.php new file mode 100644 index 0000000..aa0e8be --- /dev/null +++ b/app/Stats/CpuUsage.php @@ -0,0 +1,92 @@ +monitor = $monitor; + } + + /** + * Sample the stat. + * + * @return void + */ + public function sample() + { + /* + |-------------------------------------------------------------------------- + | /proc/stat + |-------------------------------------------------------------------------- + | + | /proc/stat is actually the Linux kernel's system statistics pool, where + | the kernel writes immediate statistics about its behavior. The first + | line of the file has the format + | cpuN user nice system idle io_wait irq soft_irq steal guest guest_nice + | awk: $2 $3 $4 $5 $6 $7 $8 $9 + | + | Where user, nice, and so forth are expressed in number of jiffies + | (approx 1/100th of a second) spent running processes in those + | categories. + | + | The first line of /proc/stat is an aggregation of all CPU cores, so we + | can use the one line as opposed to reading x lines and summing them + | ourselves. + | + | We calculate how much the CPU is idle from the stats + | thus we can determine how busy the CPU really is. + | + | See man 5 proc for more information. + | + */ + + if (is_readable("/proc/stat")) { + $cpuIdle = (float) $this->executeCommand("grep 'cpu ' /proc/stat | awk '{idle=($5*100)/($2+$3+$4+$5+$6+$7+$8+$9)} END {print idle}'"); + $cpuUsage = (float) 100 - $cpuIdle; + + CpuUsageModel::create([ + 'used' => $cpuUsage, + 'idle' => $cpuIdle, + ]); + } + } + + /** + * Test the stat. + * + * @return bool + */ + public function test() + { + $op = $this->getOperator(); + + $results = DB::select("SELECT + CASE WHEN used {$op} ? THEN 'ALERT' ELSE 'OK' END AS currentState, + IFNULL(alerts.monitor_state, 'UNKNOWN') AS lastState +FROM ( + SELECT * FROM cpu_usage WHERE created_at >= DATETIME('NOW', ?) ORDER BY created_at DESC LIMIT ? +) _samples +LEFT JOIN (SELECT * FROM alerts WHERE monitor_id = ? AND monitor_type = ? ORDER BY created_at DESC LIMIT 1) alerts", [ + $this->monitor->threshold, + '-'.($this->monitor->minutes + 1).' minutes', + $this->monitor->minutes + 1, + $this->monitor->key, + $this->monitor->type, + ]); + + return $this->testResults($results); + } + +} diff --git a/database/migrations/2022_07_26_183629_create_cpu_usage_table.php b/database/migrations/2022_07_26_183629_create_cpu_usage_table.php new file mode 100644 index 0000000..c2d5e6c --- /dev/null +++ b/database/migrations/2022_07_26_183629_create_cpu_usage_table.php @@ -0,0 +1,35 @@ +id(); + $table->float('used', 5, 2); + $table->float('idle', 5, 2); + $table->timestamps(); + + $table->index('created_at'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('cpu_usages'); + } +}