Use Measurements for all statistic collection (#13333)

* Use Measurements for all statistic collection

* fix space

* Use colors
cleanup

* fix style

* manually fix license notice

* add return times and new line

* add return times and new line

* fix mistake in copyright template

* fix style
This commit is contained in:
Tony Murray 2021-10-06 17:09:54 -05:00 committed by GitHub
parent f28802bb2b
commit 8d5bc3fc41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 251 additions and 292 deletions

View File

@ -26,6 +26,7 @@
namespace LibreNMS\Data\Source; namespace LibreNMS\Data\Source;
use App\Models\Device; use App\Models\Device;
use App\Polling\Measure\Measurement;
use DeviceCache; use DeviceCache;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -215,15 +216,6 @@ class SnmpQuery
return $this->exec('snmptranslate', $this->parseOid($oid)); return $this->exec('snmptranslate', $this->parseOid($oid));
} }
private function recordStatistic(string $type, float $start_time): void
{
global $snmp_stats;
$runtime = microtime(true) - $start_time;
$snmp_stats['ops'][$type] = isset($snmp_stats['ops'][$type]) ? $snmp_stats['ops'][$type] + 1 : 0;
$snmp_stats['time'][$type] = isset($snmp_stats['time'][$type]) ? $snmp_stats['time'][$type] + $runtime : $runtime;
}
private function buildCli(string $command, array $oids): array private function buildCli(string $command, array $oids): array
{ {
$cmd = $this->initCommand($command); $cmd = $this->initCommand($command);
@ -285,7 +277,7 @@ class SnmpQuery
private function exec(string $command, array $oids): SnmpResponse private function exec(string $command, array $oids): SnmpResponse
{ {
$time_start = microtime(true); $measure = Measurement::start($command);
$proc = new Process($this->buildCli($command, $oids)); $proc = new Process($this->buildCli($command, $oids));
$proc->setTimeout(Config::get('snmp.exec_timeout', 1200)); $proc->setTimeout(Config::get('snmp.exec_timeout', 1200));
@ -301,7 +293,7 @@ class SnmpQuery
$this->checkExitCode($exitCode, $stderr); $this->checkExitCode($exitCode, $stderr);
$this->logOutput($output, $stderr); $this->logOutput($output, $stderr);
$this->recordStatistic($command, $time_start); $measure->manager()->recordSnmp($measure->end());
return new SnmpResponse($output, $stderr, $exitCode); return new SnmpResponse($output, $stderr, $exitCode);
} }

View File

@ -25,8 +25,8 @@
namespace LibreNMS\Data\Store; namespace LibreNMS\Data\Store;
use LibreNMS\Data\Measure\Measurement; use App\Polling\Measure\Measurement;
use LibreNMS\Data\Measure\MeasurementCollection; use App\Polling\Measure\MeasurementCollection;
use LibreNMS\Interfaces\Data\Datastore as DatastoreContract; use LibreNMS\Interfaces\Data\Datastore as DatastoreContract;
abstract class BaseDatastore implements DatastoreContract abstract class BaseDatastore implements DatastoreContract

View File

@ -25,6 +25,7 @@
namespace LibreNMS\Data\Store; namespace LibreNMS\Data\Store;
use Illuminate\Support\Collection;
use LibreNMS\Config; use LibreNMS\Config;
use LibreNMS\Interfaces\Data\Datastore as DatastoreContract; use LibreNMS\Interfaces\Data\Datastore as DatastoreContract;
@ -151,12 +152,15 @@ class Datastore
return $this->stores; return $this->stores;
} }
public function getStats() /**
* Get the measurements for all datastores, keyed by datastore name
*
* @return \Illuminate\Support\Collection<\App\Polling\Measure\MeasurementCollection>
*/
public function getStats(): Collection
{ {
return array_reduce($this->stores, function ($result, DatastoreContract $store) { return collect($this->stores)->mapWithKeys(function (DatastoreContract $store) {
$result[$store->getName()] = $store->getStats(); return [$store->getName() => $store->getStats()];
});
return $result;
}, []);
} }
} }

View File

@ -26,9 +26,9 @@
namespace LibreNMS\Data\Store; namespace LibreNMS\Data\Store;
use App\Polling\Measure\Measurement;
use Carbon\Carbon; use Carbon\Carbon;
use LibreNMS\Config; use LibreNMS\Config;
use LibreNMS\Data\Measure\Measurement;
use Log; use Log;
class Graphite extends BaseDatastore class Graphite extends BaseDatastore

View File

@ -26,10 +26,10 @@
namespace LibreNMS\Data\Store; namespace LibreNMS\Data\Store;
use App\Polling\Measure\Measurement;
use InfluxDB\Client; use InfluxDB\Client;
use InfluxDB\Driver\UDP; use InfluxDB\Driver\UDP;
use LibreNMS\Config; use LibreNMS\Config;
use LibreNMS\Data\Measure\Measurement;
use Log; use Log;
class InfluxDB extends BaseDatastore class InfluxDB extends BaseDatastore

View File

@ -26,9 +26,9 @@
namespace LibreNMS\Data\Store; namespace LibreNMS\Data\Store;
use App\Polling\Measure\Measurement;
use Carbon\Carbon; use Carbon\Carbon;
use LibreNMS\Config; use LibreNMS\Config;
use LibreNMS\Data\Measure\Measurement;
use Log; use Log;
class OpenTSDB extends BaseDatastore class OpenTSDB extends BaseDatastore

View File

@ -26,10 +26,10 @@
namespace LibreNMS\Data\Store; namespace LibreNMS\Data\Store;
use App\Polling\Measure\Measurement;
use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use LibreNMS\Config; use LibreNMS\Config;
use LibreNMS\Data\Measure\Measurement;
use Log; use Log;
class Prometheus extends BaseDatastore class Prometheus extends BaseDatastore

View File

@ -25,9 +25,9 @@
namespace LibreNMS\Data\Store; namespace LibreNMS\Data\Store;
use App\Polling\Measure\Measurement;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use LibreNMS\Config; use LibreNMS\Config;
use LibreNMS\Data\Measure\Measurement;
use LibreNMS\Exceptions\FileExistsException; use LibreNMS\Exceptions\FileExistsException;
use LibreNMS\Exceptions\RrdGraphException; use LibreNMS\Exceptions\RrdGraphException;
use LibreNMS\Proc; use LibreNMS\Proc;

View File

@ -19,11 +19,11 @@
* *
* @link https://www.librenms.org * @link https://www.librenms.org
* *
* @copyright 2020 Tony Murray * @copyright 2021 Tony Murray
* @author Tony Murray <murraytony@gmail.com> * @author Tony Murray <murraytony@gmail.com>
*/ */
namespace LibreNMS\Data\Measure; namespace App\Polling\Measure;
class Measurement class Measurement
{ {
@ -31,10 +31,21 @@ class Measurement
private $type; private $type;
private $duration; private $duration;
private function __construct(string $type) private function __construct(string $type, float $duration = null)
{ {
$this->type = $type; $this->type = $type;
$this->start = microtime(true); $this->start = microtime(true);
if ($duration !== null) {
$this->duration = $duration;
}
}
/**
* Create a measurement with an existing duration
*/
public static function make(string $type, float $duration): Measurement
{
return new static($type, $duration);
} }
/** /**
@ -43,17 +54,15 @@ class Measurement
* @param string $type * @param string $type
* @return static * @return static
*/ */
public static function start(string $type) public static function start(string $type): Measurement
{ {
return new static($type); return new static($type);
} }
/** /**
* End the timer for this operation * End the timer for this operation
*
* @return $this
*/ */
public function end() public function end(): Measurement
{ {
$this->duration = microtime(true) - $this->start; $this->duration = microtime(true) - $this->start;
@ -62,21 +71,22 @@ class Measurement
/** /**
* Get the duration of the operation * Get the duration of the operation
*
* @return float
*/ */
public function getDuration() public function getDuration(): float
{ {
return $this->duration; return $this->duration;
} }
/** /**
* Get the type of the operation * Get the type of the operation
*
* @return string
*/ */
public function getType() public function getType(): string
{ {
return $this->type; return $this->type;
} }
public function manager(): MeasurementManager
{
return app(MeasurementManager::class);
}
} }

View File

@ -19,42 +19,42 @@
* *
* @link https://www.librenms.org * @link https://www.librenms.org
* *
* @copyright 2020 Tony Murray * @copyright 2021 Tony Murray
* @author Tony Murray <murraytony@gmail.com> * @author Tony Murray <murraytony@gmail.com>
*/ */
namespace LibreNMS\Data\Measure; namespace App\Polling\Measure;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
class MeasurementCollection extends Collection class MeasurementCollection extends Collection
{ {
public function getTotalCount() public function getTotalCount(): int
{ {
return $this->sumStat('getCount'); return $this->sumStat('getCount');
} }
public function getTotalDuration() public function getTotalDuration(): float
{ {
return $this->sumStat('getDuration'); return $this->sumStat('getDuration');
} }
public function getCountDiff() public function getCountDiff(): int
{ {
return $this->sumStat('getCountDiff'); return $this->sumStat('getCountDiff');
} }
public function getDurationDiff() public function getDurationDiff(): float
{ {
return $this->sumStat('getDurationDiff'); return $this->sumStat('getDurationDiff');
} }
public function checkpoint() public function checkpoint(): void
{ {
$this->each->checkpoint(); $this->each->checkpoint();
} }
public function record(Measurement $measurement) public function record(Measurement $measurement): void
{ {
$type = $measurement->getType(); $type = $measurement->getType();
@ -65,10 +65,19 @@ class MeasurementCollection extends Collection
$this->get($type)->add($measurement); $this->get($type)->add($measurement);
} }
private function sumStat($function) public function getSummary(string $type): MeasurementSummary
{ {
return $this->reduce(function ($sum, $measurement) use ($function) { return $this->get($type, new MeasurementSummary($type));
$sum += $measurement->$function(); }
/**
* @param string $method method on measurement class to call
* @return int|float
*/
private function sumStat(string $method)
{
return $this->reduce(function ($sum, $measurement) use ($method) {
$sum += $measurement->$method();
return $sum; return $sum;
}, 0); }, 0);

View File

@ -0,0 +1,142 @@
<?php
/**
* MeasurementManager.php
*
* -Description-
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* @link https://www.librenms.org
*
* @copyright 2021 Tony Murray
* @author Tony Murray <murraytony@gmail.com>
*/
namespace App\Polling\Measure;
use DB;
use Illuminate\Database\Events\QueryExecuted;
class MeasurementManager
{
const SNMP_COLOR = "\e[0;36m";
const DB_COLOR = "\e[1;33m";
const DATASTORE_COLOR = "\e[0;32m";
const NO_COLOR = "\e[0m";
/**
* @var MeasurementCollection
*/
private static $snmp;
/**
* @var MeasurementCollection
*/
private static $db;
public function __construct()
{
if (self::$snmp === null) {
self::$snmp = new MeasurementCollection();
self::$db = new MeasurementCollection();
}
}
/**
* Register DB listener to record sql query stats
*/
public function listenDb(): void
{
DB::listen(function (QueryExecuted $event) {
$type = strtolower(substr($event->sql, 0, strpos($event->sql, ' ')));
$this->recordDb(Measurement::make($type, $event->time ? $event->time / 100 : 0));
});
}
/**
* Update statistics for db operations
*/
public function recordDb(Measurement $measurement): void
{
self::$db->record($measurement);
}
/**
* Print out the stats totals since the last checkpoint
*/
public function printChangedStats(): void
{
printf(
'>> %sSNMP%s: [%d/%.2fs] %sMySQL%s: [%d/%.2fs]',
self::SNMP_COLOR,
self::NO_COLOR,
self::$snmp->getCountDiff(),
self::$snmp->getDurationDiff(),
self::DB_COLOR,
self::NO_COLOR,
self::$db->getCountDiff(),
self::$db->getDurationDiff()
);
app('Datastore')->getStats()->each(function (MeasurementCollection $stats, $datastore) {
printf(' %s%s%s: [%d/%.2fs]', self::DATASTORE_COLOR, $datastore, self::NO_COLOR, $stats->getCountDiff(), $stats->getDurationDiff());
});
$this->checkpoint();
echo PHP_EOL;
}
/**
* Make a new checkpoint so to compare against
*/
public function checkpoint(): void
{
self::$snmp->checkpoint();
self::$db->checkpoint();
app('Datastore')->getStats()->each->checkpoint();
}
/**
* Record a measurement for snmp
*/
public function recordSnmp(Measurement $measurement): void
{
self::$snmp->record($measurement);
}
/**
* Print global stat arrays
*/
public function printStats(): void
{
$this->printSummary('SNMP', self::$snmp, self::SNMP_COLOR);
$this->printSummary('SQL', self::$db, self::DB_COLOR);
app('Datastore')->getStats()->each(function (MeasurementCollection $stats, string $datastore) {
$this->printSummary($datastore, $stats, self::DATASTORE_COLOR);
});
}
private function printSummary(string $name, MeasurementCollection $collection, string $color = ''): void
{
printf('%s%s%s [%d/%.2fs]:', $color, $name, $color ? self::NO_COLOR : '', $collection->getTotalCount(), $collection->getTotalDuration());
$collection->each(function (MeasurementSummary $stat) {
printf(' %s[%d/%.2fs]', ucfirst($stat->getType()), $stat->getCount(), $stat->getDuration());
});
echo PHP_EOL;
}
}

View File

@ -19,11 +19,11 @@
* *
* @link https://www.librenms.org * @link https://www.librenms.org
* *
* @copyright 2020 Tony Murray * @copyright 2021 Tony Murray
* @author Tony Murray <murraytony@gmail.com> * @author Tony Murray <murraytony@gmail.com>
*/ */
namespace LibreNMS\Data\Measure; namespace App\Polling\Measure;
class MeasurementSummary class MeasurementSummary
{ {
@ -39,7 +39,7 @@ class MeasurementSummary
$this->type = $type; $this->type = $type;
} }
public function add(Measurement $measurement) public function add(Measurement $measurement): void
{ {
$this->count++; $this->count++;
$this->duration += $measurement->getDuration(); $this->duration += $measurement->getDuration();
@ -48,10 +48,8 @@ class MeasurementSummary
/** /**
* Get the measurement summary * Get the measurement summary
* ['count' => #, 'duration' => s] * ['count' => #, 'duration' => s]
*
* @return array
*/ */
public function get() public function get(): array
{ {
return [ return [
'count' => $this->count, 'count' => $this->count,
@ -59,33 +57,36 @@ class MeasurementSummary
]; ];
} }
public function getCount() public function getCount(): int
{ {
return $this->count; return $this->count;
} }
public function getType() public function getType(): string
{ {
return $this->type; return $this->type;
} }
public function getDuration() public function getDuration(): float
{ {
return $this->duration; return $this->duration;
} }
public function checkpoint() /**
* Set a new checkpoint to compare against with diff methods
*/
public function checkpoint(): void
{ {
$this->checkpointCount = $this->count; $this->checkpointCount = $this->count;
$this->checkpointDuration = $this->duration; $this->checkpointDuration = $this->duration;
} }
public function getCountDiff() public function getCountDiff(): int
{ {
return $this->count - $this->checkpointCount; return $this->count - $this->checkpointCount;
} }
public function getDurationDiff() public function getDurationDiff(): float
{ {
return $this->duration - $this->checkpointDuration; return $this->duration - $this->checkpointDuration;
} }

View File

@ -3,6 +3,7 @@
namespace App\Providers; namespace App\Providers;
use App\Models\Sensor; use App\Models\Sensor;
use App\Polling\Measure\MeasurementManager;
use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Facades\Blade; use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
@ -38,8 +39,9 @@ class AppServiceProvider extends ServiceProvider
* *
* @return void * @return void
*/ */
public function boot() public function boot(MeasurementManager $measure)
{ {
$measure->listenDb();
\Illuminate\Pagination\Paginator::useBootstrap(); \Illuminate\Pagination\Paginator::useBootstrap();
$this->app->booted('\LibreNMS\DB\Eloquent::initLegacyListeners'); $this->app->booted('\LibreNMS\DB\Eloquent::initLegacyListeners');

View File

@ -136,7 +136,8 @@ $string = $argv[0] . " $doing " . date(\LibreNMS\Config::get('dateformat.compact
d_echo("$string\n"); d_echo("$string\n");
if (! isset($options['q'])) { if (! isset($options['q'])) {
printStats(); echo PHP_EOL;
app(\App\Polling\Measure\MeasurementManager::class)->printStats();
} }
logfile($string); logfile($string);

View File

@ -34,5 +34,5 @@ if (Debug::isEnabled()) {
echo '<br />'; echo '<br />';
printf('Runtime %.3fs', microtime(true) - $start); printf('Runtime %.3fs', microtime(true) - $start);
echo '<br />'; echo '<br />';
printStats(); app(\App\Polling\Measure\MeasurementManager::class)->printStats();
} }

View File

@ -18,7 +18,6 @@
*/ */
use Illuminate\Database\QueryException; use Illuminate\Database\QueryException;
use LibreNMS\Config;
use LibreNMS\DB\Eloquent; use LibreNMS\DB\Eloquent;
use LibreNMS\Exceptions\DatabaseConnectException; use LibreNMS\Exceptions\DatabaseConnectException;
use LibreNMS\Util\Laravel; use LibreNMS\Util\Laravel;
@ -110,8 +109,6 @@ function dbQuery($sql, $parameters = [])
*/ */
function dbInsert($data, $table) function dbInsert($data, $table)
{ {
$time_start = microtime(true);
$sql = 'INSERT IGNORE INTO `' . $table . '` (`' . implode('`,`', array_keys($data)) . '`) VALUES (' . implode(',', dbPlaceHolders($data)) . ')'; $sql = 'INSERT IGNORE INTO `' . $table . '` (`' . implode('`,`', array_keys($data)) . '`) VALUES (' . implode(',', dbPlaceHolders($data)) . ')';
try { try {
@ -120,7 +117,6 @@ function dbInsert($data, $table)
dbHandleException(new QueryException($sql, $data, $pdoe)); dbHandleException(new QueryException($sql, $data, $pdoe));
} }
recordDbStatistic('insert', $time_start);
if ($result) { if ($result) {
return Eloquent::DB()->getPdo()->lastInsertId(); return Eloquent::DB()->getPdo()->lastInsertId();
} else { } else {
@ -139,8 +135,6 @@ function dbInsert($data, $table)
*/ */
function dbBulkInsert($data, $table) function dbBulkInsert($data, $table)
{ {
$time_start = microtime(true);
// check that data isn't an empty array // check that data isn't an empty array
if (empty($data)) { if (empty($data)) {
return false; return false;
@ -160,8 +154,6 @@ function dbBulkInsert($data, $table)
try { try {
$result = Eloquent::DB()->table($table)->insert((array) $data_chunk); $result = Eloquent::DB()->table($table)->insert((array) $data_chunk);
recordDbStatistic('insert', $time_start);
return $result; return $result;
} catch (PDOException $pdoe) { } catch (PDOException $pdoe) {
// FIXME query? // FIXME query?
@ -184,8 +176,6 @@ function dbBulkInsert($data, $table)
*/ */
function dbUpdate($data, $table, $where = null, $parameters = []) function dbUpdate($data, $table, $where = null, $parameters = [])
{ {
$time_start = microtime(true);
// need field name and placeholder value // need field name and placeholder value
// but how merge these field placeholders with actual $parameters array for the WHERE clause // but how merge these field placeholders with actual $parameters array for the WHERE clause
$sql = 'UPDATE `' . $table . '` set '; $sql = 'UPDATE `' . $table . '` set ';
@ -213,8 +203,6 @@ function dbUpdate($data, $table, $where = null, $parameters = [])
try { try {
$result = Eloquent::DB()->update($sql, (array) $data); $result = Eloquent::DB()->update($sql, (array) $data);
recordDbStatistic('update', $time_start);
return $result; return $result;
} catch (PDOException $pdoe) { } catch (PDOException $pdoe) {
dbHandleException(new QueryException($sql, $data, $pdoe)); dbHandleException(new QueryException($sql, $data, $pdoe));
@ -225,8 +213,6 @@ function dbUpdate($data, $table, $where = null, $parameters = [])
function dbDelete($table, $where = null, $parameters = []) function dbDelete($table, $where = null, $parameters = [])
{ {
$time_start = microtime(true);
$sql = 'DELETE FROM `' . $table . '`'; $sql = 'DELETE FROM `' . $table . '`';
if ($where) { if ($where) {
$sql .= ' WHERE ' . $where; $sql .= ' WHERE ' . $where;
@ -238,8 +224,6 @@ function dbDelete($table, $where = null, $parameters = [])
dbHandleException(new QueryException($sql, $parameters, $pdoe)); dbHandleException(new QueryException($sql, $parameters, $pdoe));
} }
recordDbStatistic('delete', $time_start);
return $result; return $result;
}//end dbDelete() }//end dbDelete()
@ -253,8 +237,6 @@ function dbDelete($table, $where = null, $parameters = [])
*/ */
function dbDeleteOrphans($target_table, $parents) function dbDeleteOrphans($target_table, $parents)
{ {
$time_start = microtime(true);
if (empty($parents)) { if (empty($parents)) {
// don't delete all entries if parents is missing // don't delete all entries if parents is missing
return false; return false;
@ -288,8 +270,6 @@ function dbDeleteOrphans($target_table, $parents)
dbHandleException(new QueryException($query, [], $pdoe)); dbHandleException(new QueryException($query, [], $pdoe));
} }
recordDbStatistic('delete', $time_start);
return $result; return $result;
} }
@ -301,14 +281,11 @@ function dbDeleteOrphans($target_table, $parents)
function dbFetchRows($sql, $parameters = []) function dbFetchRows($sql, $parameters = [])
{ {
global $PDO_FETCH_ASSOC; global $PDO_FETCH_ASSOC;
$time_start = microtime(true);
try { try {
$PDO_FETCH_ASSOC = true; $PDO_FETCH_ASSOC = true;
$rows = Eloquent::DB()->select($sql, (array) $parameters); $rows = Eloquent::DB()->select($sql, (array) $parameters);
recordDbStatistic('fetchrows', $time_start);
return $rows; return $rows;
} catch (PDOException $pdoe) { } catch (PDOException $pdoe) {
dbHandleException(new QueryException($sql, $parameters, $pdoe)); dbHandleException(new QueryException($sql, $parameters, $pdoe));
@ -347,14 +324,11 @@ function dbFetch($sql, $parameters = [])
function dbFetchRow($sql = null, $parameters = []) function dbFetchRow($sql = null, $parameters = [])
{ {
global $PDO_FETCH_ASSOC; global $PDO_FETCH_ASSOC;
$time_start = microtime(true);
try { try {
$PDO_FETCH_ASSOC = true; $PDO_FETCH_ASSOC = true;
$row = Eloquent::DB()->selectOne($sql, (array) $parameters); $row = Eloquent::DB()->selectOne($sql, (array) $parameters);
recordDbStatistic('fetchrow', $time_start);
return $row; return $row;
} catch (PDOException $pdoe) { } catch (PDOException $pdoe) {
dbHandleException(new QueryException($sql, $parameters, $pdoe)); dbHandleException(new QueryException($sql, $parameters, $pdoe));
@ -372,12 +346,10 @@ function dbFetchRow($sql = null, $parameters = [])
function dbFetchCell($sql, $parameters = []) function dbFetchCell($sql, $parameters = [])
{ {
global $PDO_FETCH_ASSOC; global $PDO_FETCH_ASSOC;
$time_start = microtime(true);
try { try {
$PDO_FETCH_ASSOC = true; $PDO_FETCH_ASSOC = true;
$row = Eloquent::DB()->selectOne($sql, (array) $parameters); $row = Eloquent::DB()->selectOne($sql, (array) $parameters);
recordDbStatistic('fetchcell', $time_start);
if ($row) { if ($row) {
return reset($row); return reset($row);
// shift first field off first row // shift first field off first row
@ -399,7 +371,6 @@ function dbFetchCell($sql, $parameters = [])
function dbFetchColumn($sql, $parameters = []) function dbFetchColumn($sql, $parameters = [])
{ {
global $PDO_FETCH_ASSOC; global $PDO_FETCH_ASSOC;
$time_start = microtime(true);
$cells = []; $cells = [];
@ -410,8 +381,6 @@ function dbFetchColumn($sql, $parameters = [])
} }
$PDO_FETCH_ASSOC = false; $PDO_FETCH_ASSOC = false;
recordDbStatistic('fetchcolumn', $time_start);
return $cells; return $cells;
} catch (PDOException $pdoe) { } catch (PDOException $pdoe) {
dbHandleException(new QueryException($sql, $parameters, $pdoe)); dbHandleException(new QueryException($sql, $parameters, $pdoe));
@ -544,58 +513,6 @@ function dbGenPlaceholders($count)
return '(' . implode(',', array_fill(0, $count, '?')) . ')'; return '(' . implode(',', array_fill(0, $count, '?')) . ')';
} }
/**
* Update statistics for db operations
*
* @param string $stat fetchcell, fetchrow, fetchrows, fetchcolumn, update, insert, delete
* @param float $start_time The time the operation started with 'microtime(true)'
* @return float The calculated run time
*/
function recordDbStatistic($stat, $start_time)
{
global $db_stats, $db_stats_last;
if (! isset($db_stats)) {
$db_stats = [
'ops' => [
'insert' => 0,
'update' => 0,
'delete' => 0,
'fetchcell' => 0,
'fetchcolumn' => 0,
'fetchrow' => 0,
'fetchrows' => 0,
],
'time' => [
'insert' => 0.0,
'update' => 0.0,
'delete' => 0.0,
'fetchcell' => 0.0,
'fetchcolumn' => 0.0,
'fetchrow' => 0.0,
'fetchrows' => 0.0,
],
];
$db_stats_last = $db_stats;
}
$runtime = microtime(true) - $start_time;
$db_stats['ops'][$stat]++;
$db_stats['time'][$stat] += $runtime;
//double accounting corrections
if ($stat == 'fetchcolumn') {
$db_stats['ops']['fetchrows']--;
$db_stats['time']['fetchrows'] -= $runtime;
}
if ($stat == 'fetchcell') {
$db_stats['ops']['fetchrow']--;
$db_stats['time']['fetchrow'] -= $runtime;
}
return $runtime;
}
/** /**
* Synchronize a relationship to a list of related ids * Synchronize a relationship to a list of related ids
* *

View File

@ -146,6 +146,10 @@ function discover_device(&$device, $force_module = false)
$discovery_devices = Config::get('discovery_modules', []); $discovery_devices = Config::get('discovery_modules', []);
$discovery_devices = ['core' => true] + $discovery_devices; $discovery_devices = ['core' => true] + $discovery_devices;
/** @var \App\Polling\Measure\MeasurementManager $measurements */
$measurements = app(\App\Polling\Measure\MeasurementManager::class);
$measurements->checkpoint(); // don't count previous stats
foreach ($discovery_devices as $module => $module_status) { foreach ($discovery_devices as $module => $module_status) {
$os_module_status = Config::getOsSetting($device['os'], "discovery_modules.$module"); $os_module_status = Config::getOsSetting($device['os'], "discovery_modules.$module");
d_echo('Modules status: Global' . (isset($module_status) ? ($module_status ? '+ ' : '- ') : ' ')); d_echo('Modules status: Global' . (isset($module_status) ? ($module_status ? '+ ' : '- ') : ' '));
@ -173,7 +177,7 @@ function discover_device(&$device, $force_module = false)
$module_time = substr($module_time, 0, 5); $module_time = substr($module_time, 0, 5);
$module_mem = (memory_get_usage() - $start_memory); $module_mem = (memory_get_usage() - $start_memory);
printf("\n>> Runtime for discovery module '%s': %.4f seconds with %s bytes\n", $module, $module_time, $module_mem); printf("\n>> Runtime for discovery module '%s': %.4f seconds with %s bytes\n", $module, $module_time, $module_mem);
printChangedStats(); $measurements->printChangedStats();
echo "#### Unload disco module $module ####\n\n"; echo "#### Unload disco module $module ####\n\n";
} elseif (isset($attribs['discover_' . $module]) && $attribs['discover_' . $module] == '0') { } elseif (isset($attribs['discover_' . $module]) && $attribs['discover_' . $module] == '0') {
echo "Module [ $module ] disabled on host.\n\n"; echo "Module [ $module ] disabled on host.\n\n";

View File

@ -1449,134 +1449,6 @@ function q_bridge_bits2indices($hex_data)
return $indices; return $indices;
} }
/**
* Intialize global stat arrays
*/
function initStats()
{
global $snmp_stats, $snmp_stats_last;
if (! isset($snmp_stats)) {
$snmp_stats = [
'ops' => [
'snmpget' => 0,
'snmpgetnext' => 0,
'snmpwalk' => 0,
],
'time' => [
'snmpget' => 0.0,
'snmpgetnext' => 0.0,
'snmpwalk' => 0.0,
],
];
$snmp_stats_last = $snmp_stats;
}
}
/**
* Print out the stats totals since the last time this function was called
*
* @param bool $update_only Only update the stats checkpoint, don't print them
*/
function printChangedStats($update_only = false)
{
global $snmp_stats, $db_stats;
global $snmp_stats_last, $db_stats_last;
$output = sprintf(
'>> SNMP: [%d/%.2fs] MySQL: [%d/%.2fs]',
array_sum($snmp_stats['ops'] ?? []) - array_sum($snmp_stats_last['ops'] ?? []),
array_sum($snmp_stats['time'] ?? []) - array_sum($snmp_stats_last['time'] ?? []),
array_sum($db_stats['ops'] ?? []) - array_sum($db_stats_last['ops'] ?? []),
array_sum($db_stats['time'] ?? []) - array_sum($db_stats_last['time'] ?? [])
);
foreach (app('Datastore')->getStats() as $datastore => $stats) {
/** @var \LibreNMS\Data\Measure\MeasurementCollection $stats */
$output .= sprintf(' %s: [%d/%.2fs]', $datastore, $stats->getCountDiff(), $stats->getDurationDiff());
$stats->checkpoint();
}
if (! $update_only) {
echo $output . PHP_EOL;
}
// make a new checkpoint
$snmp_stats_last = $snmp_stats;
$db_stats_last = $db_stats;
}
/**
* Print global stat arrays
*/
function printStats()
{
global $snmp_stats, $db_stats;
if ($snmp_stats) {
printf(
"SNMP [%d/%.2fs]: Get[%d/%.2fs] Getnext[%d/%.2fs] Walk[%d/%.2fs]\n",
array_sum($snmp_stats['ops']),
array_sum($snmp_stats['time']),
$snmp_stats['ops']['snmpget'],
$snmp_stats['time']['snmpget'],
$snmp_stats['ops']['snmpgetnext'],
$snmp_stats['time']['snmpgetnext'],
$snmp_stats['ops']['snmpwalk'],
$snmp_stats['time']['snmpwalk']
);
}
if ($db_stats) {
printf(
"MySQL [%d/%.2fs]: Cell[%d/%.2fs] Row[%d/%.2fs] Rows[%d/%.2fs] Column[%d/%.2fs] Update[%d/%.2fs] Insert[%d/%.2fs] Delete[%d/%.2fs]\n",
array_sum($db_stats['ops']),
array_sum($db_stats['time']),
$db_stats['ops']['fetchcell'],
$db_stats['time']['fetchcell'],
$db_stats['ops']['fetchrow'],
$db_stats['time']['fetchrow'],
$db_stats['ops']['fetchrows'],
$db_stats['time']['fetchrows'],
$db_stats['ops']['fetchcolumn'],
$db_stats['time']['fetchcolumn'],
$db_stats['ops']['update'],
$db_stats['time']['update'],
$db_stats['ops']['insert'],
$db_stats['time']['insert'],
$db_stats['ops']['delete'],
$db_stats['time']['delete']
);
}
foreach (app('Datastore')->getStats() as $datastore => $stats) {
/** @var \LibreNMS\Data\Measure\MeasurementCollection $stats */
printf('%s [%d/%.2fs]:', $datastore, $stats->getTotalCount(), $stats->getTotalDuration());
foreach ($stats as $stat) {
/** @var \LibreNMS\Data\Measure\MeasurementSummary $stat */
printf(' %s[%d/%.2fs]', ucfirst($stat->getType()), $stat->getCount(), $stat->getDuration());
}
echo PHP_EOL;
}
}
/**
* @param string $stat snmpget, snmpwalk
* @param float $start_time The time the operation started with 'microtime(true)'
* @return float The calculated run time
*/
function recordSnmpStatistic($stat, $start_time)
{
global $snmp_stats;
initStats();
$runtime = microtime(true) - $start_time;
$snmp_stats['ops'][$stat]++;
$snmp_stats['time'][$stat] += $runtime;
return $runtime;
}
function update_device_logo(&$device) function update_device_logo(&$device)
{ {
$icon = getImageName($device, false); $icon = getImageName($device, false);

View File

@ -310,7 +310,10 @@ function poll_device($device, $force_module = false)
$device['status'] = $deviceModel->status; $device['status'] = $deviceModel->status;
$device['status_reason'] = $deviceModel->status_reason; $device['status_reason'] = $deviceModel->status_reason;
printChangedStats(true); // don't count previous stats /** @var \App\Polling\Measure\MeasurementManager $measurements */
$measurements = app(\App\Polling\Measure\MeasurementManager::class);
$measurements->checkpoint(); // don't count previous stats
foreach (Config::get('poller_modules') as $module => $module_status) { foreach (Config::get('poller_modules') as $module => $module_status) {
$os_module_status = Config::get("os.{$device['os']}.poller_modules.$module"); $os_module_status = Config::get("os.{$device['os']}.poller_modules.$module");
d_echo('Modules status: Global' . (isset($module_status) ? ($module_status ? '+ ' : '- ') : ' ')); d_echo('Modules status: Global' . (isset($module_status) ? ($module_status ? '+ ' : '- ') : ' '));
@ -336,7 +339,7 @@ function poll_device($device, $force_module = false)
$module_time = microtime(true) - $module_start; $module_time = microtime(true) - $module_start;
$module_mem = (memory_get_usage() - $start_memory); $module_mem = (memory_get_usage() - $start_memory);
printf("\n>> Runtime for poller module '%s': %.4f seconds with %s bytes\n", $module, $module_time, $module_mem); printf("\n>> Runtime for poller module '%s': %.4f seconds with %s bytes\n", $module, $module_time, $module_mem);
printChangedStats(); $measurements->printChangedStats();
echo "#### Unload poller module $module ####\n\n"; echo "#### Unload poller module $module ####\n\n";
// save per-module poller stats // save per-module poller stats

View File

@ -16,6 +16,7 @@
*/ */
use App\Models\Device; use App\Models\Device;
use App\Polling\Measure\Measurement;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use LibreNMS\Config; use LibreNMS\Config;
use LibreNMS\Util\Debug; use LibreNMS\Util\Debug;
@ -185,7 +186,7 @@ function gen_snmp_cmd($cmd, $device, $oids, $options = null, $mib = null, $mibdi
function snmp_get_multi($device, $oids, $options = '-OQUs', $mib = null, $mibdir = null, $array = []) function snmp_get_multi($device, $oids, $options = '-OQUs', $mib = null, $mibdir = null, $array = [])
{ {
$time_start = microtime(true); $measure = Measurement::start('snmpget');
if (! is_array($oids)) { if (! is_array($oids)) {
$oids = explode(' ', $oids); $oids = explode(' ', $oids);
@ -212,14 +213,14 @@ function snmp_get_multi($device, $oids, $options = '-OQUs', $mib = null, $mibdir
} }
} }
recordSnmpStatistic('snmpget', $time_start); $measure->manager()->recordSnmp($measure->end());
return $array; return $array;
}//end snmp_get_multi() }//end snmp_get_multi()
function snmp_get_multi_oid($device, $oids, $options = '-OUQn', $mib = null, $mibdir = null) function snmp_get_multi_oid($device, $oids, $options = '-OUQn', $mib = null, $mibdir = null)
{ {
$time_start = microtime(true); $measure = Measurement::start('snmpget');
$oid_limit = get_device_oid_limit($device); $oid_limit = get_device_oid_limit($device);
if (! is_array($oids)) { if (! is_array($oids)) {
@ -255,7 +256,7 @@ function snmp_get_multi_oid($device, $oids, $options = '-OUQn', $mib = null, $mi
} }
} }
recordSnmpStatistic('snmpget', $time_start); $measure->manager()->recordSnmp($measure->end());
return $array; return $array;
}//end snmp_get_multi_oid() }//end snmp_get_multi_oid()
@ -272,7 +273,7 @@ function snmp_get_multi_oid($device, $oids, $options = '-OUQn', $mib = null, $mi
*/ */
function snmp_get($device, $oid, $options = null, $mib = null, $mibdir = null) function snmp_get($device, $oid, $options = null, $mib = null, $mibdir = null)
{ {
$time_start = microtime(true); $measure = Measurement::start('snmpget');
if (strstr($oid, ' ')) { if (strstr($oid, ' ')) {
throw new Exception("snmp_get called for multiple OIDs: $oid"); throw new Exception("snmp_get called for multiple OIDs: $oid");
@ -282,7 +283,7 @@ function snmp_get($device, $oid, $options = null, $mib = null, $mibdir = null)
$output = str_replace('Wrong Type (should be OBJECT IDENTIFIER): ', '', $output); $output = str_replace('Wrong Type (should be OBJECT IDENTIFIER): ', '', $output);
$data = trim($output, "\\\" \n\r"); $data = trim($output, "\\\" \n\r");
recordSnmpStatistic('snmpget', $time_start); $measure->manager()->recordSnmp($measure->end());
if (preg_match('/(No Such Instance|No Such Object|No more variables left|Authentication failure)/i', $data)) { if (preg_match('/(No Such Instance|No Such Object|No more variables left|Authentication failure)/i', $data)) {
return false; return false;
} elseif ($data || $data === '0') { } elseif ($data || $data === '0') {
@ -305,13 +306,13 @@ function snmp_get($device, $oid, $options = null, $mib = null, $mibdir = null)
*/ */
function snmp_getnext($device, $oid, $options = null, $mib = null, $mibdir = null) function snmp_getnext($device, $oid, $options = null, $mib = null, $mibdir = null)
{ {
$time_start = microtime(true); $measure = Measurement::start('snmpgetnext');
$snmpcmd = [Config::get('snmpgetnext', 'snmpgetnext')]; $snmpcmd = [Config::get('snmpgetnext', 'snmpgetnext')];
$cmd = gen_snmp_cmd($snmpcmd, $device, $oid, $options, $mib, $mibdir); $cmd = gen_snmp_cmd($snmpcmd, $device, $oid, $options, $mib, $mibdir);
$data = trim(external_exec($cmd), "\" \n\r"); $data = trim(external_exec($cmd), "\" \n\r");
recordSnmpStatistic('snmpgetnext', $time_start); $measure->manager()->recordSnmp($measure->end());
if (preg_match('/(No Such Instance|No Such Object|No more variables left|Authentication failure)/i', $data)) { if (preg_match('/(No Such Instance|No Such Object|No more variables left|Authentication failure)/i', $data)) {
return false; return false;
} elseif ($data || $data === '0') { } elseif ($data || $data === '0') {
@ -334,7 +335,7 @@ function snmp_getnext($device, $oid, $options = null, $mib = null, $mibdir = nul
*/ */
function snmp_getnext_multi($device, $oids, $options = '-OQUs', $mib = null, $mibdir = null, $array = []) function snmp_getnext_multi($device, $oids, $options = '-OQUs', $mib = null, $mibdir = null, $array = [])
{ {
$time_start = microtime(true); $measure = Measurement::start('snmpgetnext');
if (! is_array($oids)) { if (! is_array($oids)) {
$oids = explode(' ', $oids); $oids = explode(' ', $oids);
} }
@ -355,7 +356,7 @@ function snmp_getnext_multi($device, $oids, $options = '-OQUs', $mib = null, $mi
} }
} }
} }
recordSnmpStatistic('snmpgetnext', $time_start); $measure->manager()->recordSnmp($measure->end());
return $array; return $array;
}//end snmp_getnext_multi() }//end snmp_getnext_multi()
@ -366,7 +367,7 @@ function snmp_getnext_multi($device, $oids, $options = '-OQUs', $mib = null, $mi
*/ */
function snmp_check($device) function snmp_check($device)
{ {
$time_start = microtime(true); $measure = Measurement::start('snmpget');
try { try {
$oid = '.1.3.6.1.2.1.1.2.0'; $oid = '.1.3.6.1.2.1.1.2.0';
@ -379,7 +380,7 @@ function snmp_check($device)
Log::debug("Device didn't respond to snmpget before {$e->getExceededTimeout()}s timeout"); Log::debug("Device didn't respond to snmpget before {$e->getExceededTimeout()}s timeout");
} }
recordSnmpStatistic('snmpget', $time_start); $measure->manager()->recordSnmp($measure->end());
if ($code === 0) { if ($code === 0) {
return true; return true;
@ -390,7 +391,7 @@ function snmp_check($device)
function snmp_walk($device, $oid, $options = null, $mib = null, $mibdir = null) function snmp_walk($device, $oid, $options = null, $mib = null, $mibdir = null)
{ {
$time_start = microtime(true); $measure = Measurement::start('snmpwalk');
$cmd = gen_snmpwalk_cmd($device, $oid, $options, $mib, $mibdir); $cmd = gen_snmpwalk_cmd($device, $oid, $options, $mib, $mibdir);
$data = trim(external_exec($cmd)); $data = trim(external_exec($cmd));
@ -410,7 +411,7 @@ function snmp_walk($device, $oid, $options = null, $mib = null, $mibdir = null)
} }
} }
recordSnmpStatistic('snmpwalk', $time_start); $measure->manager()->recordSnmp($measure->end());
return $data; return $data;
}//end snmp_walk() }//end snmp_walk()

View File

@ -159,7 +159,8 @@ $string = $argv[0] . " $doing " . date(Config::get('dateformat.compact')) . " -
d_echo("$string\n"); d_echo("$string\n");
if (! isset($options['q'])) { if (! isset($options['q'])) {
printStats(); echo PHP_EOL;
app(\App\Polling\Measure\MeasurementManager::class)->printStats();
} }
logfile($string); logfile($string);