From 0006c2a9210833789946e198bf585395f25ab8ff Mon Sep 17 00:00:00 2001 From: Starbeamrainbowlabs Date: Fri, 21 Jun 2019 00:02:26 +0100 Subject: [PATCH] Add performance counter system. --- api.php | 12 ++++- di_config.php | 10 +++- lib/SBRL/PerformanceCounter.php | 83 ++++++++++++++++++++++++++++++ logic/Actions/Changelog.php | 12 +++-- logic/Actions/DeviceData.php | 24 +++++---- logic/Actions/DeviceDataBounds.php | 19 ++++--- logic/Actions/DeviceDataRecent.php | 20 ++++--- logic/Actions/DeviceInfo.php | 20 ++++--- logic/Actions/FetchData.php | 20 ++++--- logic/Actions/Index.php | 12 +++-- logic/Actions/ListDevices.php | 17 +++--- logic/Actions/ListReadingTypes.php | 17 ++++-- logic/Actions/Version.php | 11 ++-- logic/Database.php | 17 ++++-- logic/PerfFormatter.php | 27 ---------- 15 files changed, 229 insertions(+), 92 deletions(-) create mode 100644 lib/SBRL/PerformanceCounter.php delete mode 100644 logic/PerfFormatter.php diff --git a/api.php b/api.php index 68bfd31..479851c 100644 --- a/api.php +++ b/api.php @@ -12,16 +12,23 @@ $autoloader->addPrefix("AirQuality", "logic"); $autoloader->addPrefix("SBRL", "lib/SBRL"); $autoloader->register(); +// 1.5: Performance counter +$perfcounter = new \SBRL\PerformanceCounter(); +$perfcounter->start("total"); // 2: Settings +$perfcounter->start("settings"); $settings = new \SBRL\TomlConfig( "data/settings.toml", "settings.default.toml" ); +$perfcounter->end("settings"); // 3: Dependency injection +$perfcounter->start("di"); + $di_builder = new DI\ContainerBuilder(); $di_builder->addDefinitions("di_config.php"); if($settings->get("env.mode") == "production") { @@ -37,6 +44,7 @@ if($settings->get("env.mode") == "production") { $di_container = $di_builder->build(); +$perfcounter->end("di"); // 4: Database @@ -68,6 +76,8 @@ if(!class_exists($handler_name)) { exit("Error: No action with the name '$action' could be found."); } -// FUTURE: A PSR-7 request/response system would be rather nice here. $handler = $di_container->get($handler_name); + +$perfcounter->start("handle"); +// FUTURE: A PSR-7 request/response system would be rather nice here. $handler->handle(); diff --git a/di_config.php b/di_config.php index bd9c444..7214776 100644 --- a/di_config.php +++ b/di_config.php @@ -3,7 +3,7 @@ use Psr\Container\ContainerInterface; use SBRL\TomlConfig; -use AirQuality\Database; +// use AirQuality\Database; use AirQuality\Repositories\IDeviceRepository; use AirQuality\Repositories\MariaDBDeviceRepository; @@ -12,15 +12,23 @@ use AirQuality\Repositories\MariaDBMeasurementDataRepository; use AirQuality\Repositories\IMeasurementTypeRepository; use AirQuality\Repositories\MariaDBMeasurementTypeRepository; +use SBRL\PerformanceCounter; + return [ "settings.file_default" => "data/settings.toml", "settings.file_custom" => "settings.default.toml", + // These are created during initalisation, but we want them available via dependency injection too TomlConfig::class => function(ContainerInterface $c) { global $settings; return $settings; }, + PerformanceCounter::class => function(ContainerInterface $c) { + global $perfcounter; + return $perfcounter; + }, + // Interfaces that need mapping to their implementations IDeviceRepository::class => DI\autowire(MariaDBDeviceRepository::class), IMeasurementDataRepository::class => DI\autowire(MariaDBMeasurementDataRepository::class), IMeasurementTypeRepository::class => DI\autowire(MariaDBMeasurementTypeRepository::class) diff --git a/lib/SBRL/PerformanceCounter.php b/lib/SBRL/PerformanceCounter.php new file mode 100644 index 0000000..3847753 --- /dev/null +++ b/lib/SBRL/PerformanceCounter.php @@ -0,0 +1,83 @@ + [float] | float. + * HACK: If the float is the only item in an array, then it hasn't been ended yet. + * @var array + */ + private $data = []; + + /** + * An array of performance counters to hide when rendering. + * Useful to keep some counters around for later, but keep them hidden so as to not clutter everything up. + * @var string[] + */ + private $hidden = []; + + /** + * Creates a new performance counter instea + */ + function __construct() { + // code... + } + + /** + * Starts recording the time taken for a given key. + * Calling end($key) is optional + * @param string $key The key to start recording the time taken for. + */ + public function start(string $key) { + $this->data[$key] = [microtime(true)]; + } + + /** + * Ends recording the time taken for a given key. + * @param string $key The key to end counting for. + * @return float The time taken, in milliseconds + */ + public function end(string $key) { + if(empty($this->data[$key])) + throw new Exception("Error: We can't end counting for $key because we haven't started yet"); + + $this->data[$key] = (microtime(true) - $this->data[$key][0]) * 1000; + return $this->data[$key]; + } + + /** + * Renders the performance information as a short string. + * Suitable for sending in a HTTP header, for example. + * @return string The rendered performance information. + */ + public function render() { + $result = ""; + $parts = []; + foreach($this->data as $key => $value) { + if(in_array($key, $this->hidden)) + continue; + + if(is_array($value)) + $value = $this->end($key); + + $render = round($value, 2) . "ms $key"; + if($key == "total") { + $result .= "$render | "; + continue; + } + $parts[] = $render; + } + + $result .= implode(", ", $parts); + + return $result; + } +} diff --git a/logic/Actions/Changelog.php b/logic/Actions/Changelog.php index 5aaa62a..11b4a1c 100644 --- a/logic/Actions/Changelog.php +++ b/logic/Actions/Changelog.php @@ -4,36 +4,40 @@ namespace AirQuality\Actions; use \SBRL\TomlConfig; -use \AirQuality\PerfFormatter; class Changelog implements IAction { /** @var TomlConfig */ private $settings; + /** @var \SBRL\PerformanceCounter */ + private $perfcounter; /** @var \ParsedownExtra */ private $parsedown_ext; public function __construct( TomlConfig $in_settings, - \ParsedownExtra $in_parsedown_ext) { + \ParsedownExtra $in_parsedown_ext, + \SBRL\PerformanceCounter $in_perfcounter) { $this->settings = $in_settings; $this->parsedown_ext = $in_parsedown_ext; + $this->perfcounter = $in_perfcounter; } public function handle() : bool { global $start_time; - $start_handle = microtime(true); // 1: Parse markdown + $this->perfcounter->start("parse"); $result = $this->parsedown_ext->text(file_get_contents(ROOT_DIR."Changelog.md")); + $this->perfcounter->end("parse"); // 2: Send response header("content-length: " . strlen($result)); header("content-type: text/html"); - header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, null)); + header("x-time-taken: " . $this->perfcounter->render()); echo($result); return true; } diff --git a/logic/Actions/DeviceData.php b/logic/Actions/DeviceData.php index 640d65d..858e4e8 100644 --- a/logic/Actions/DeviceData.php +++ b/logic/Actions/DeviceData.php @@ -9,11 +9,13 @@ use \AirQuality\Repositories\IMeasurementTypeRepository; use \AirQuality\ApiResponseSender; use \AirQuality\Validator; -use \AirQuality\PerfFormatter; + class DeviceData implements IAction { /** @var TomlConfig */ private $settings; + /** @var \SBRL\PerformanceCounter */ + private $perfcounter; /** @var IMeasurementDataRepository */ private $measurement_repo; @@ -30,11 +32,13 @@ class DeviceData implements IAction { TomlConfig $in_settings, IMeasurementDataRepository $in_measurement_repo, IMeasurementTypeRepository $in_type_repo, - ApiResponseSender $in_sender) { + ApiResponseSender $in_sender, + \SBRL\PerformanceCounter $in_perfcounter) { $this->settings = $in_settings; $this->measurement_repo = $in_measurement_repo; $this->type_repo = $in_type_repo; $this->sender = $in_sender; + $this->perfcounter = $in_perfcounter; $this->validator = new Validator($_GET); } @@ -42,7 +46,6 @@ class DeviceData implements IAction { public function handle() : bool { global $start_time; - $start_handle = microtime(true); // 1: Validate params $this->validator->is_numberish("device-id"); @@ -61,7 +64,7 @@ class DeviceData implements IAction { if(new \DateTime($_GET["start"]) > new \DateTime($_GET["end"])) { $this->sender->send_error_plain( 400, "Error: The start date must be earlier than the end date.", [ - [ "x-time-taken", PerfFormatter::format_perf_data($start_time, $start_handle, null) ] + [ "x-time-taken", $this->perfcounter->render() ] ] ); return false; @@ -70,18 +73,19 @@ class DeviceData implements IAction { if(!empty($_GET["average-seconds"]) && intval($_GET["average-seconds"]) == 0) { $this->sender->send_error_plain( 400, "Error: That average-seconds value is invalid (an integer greater than 0 required).", [ - [ "x-time-taken", PerfFormatter::format_perf_data($start_time, $start_handle, null) ] + [ "x-time-taken", $this->perfcounter->render() ] ] ); return false; } + $this->perfcounter->start("sql"); $reading_type_id = $this->type_repo->get_id($_GET["reading-type"]); if($reading_type_id == null) { $this->sender->send_error_plain( 400, "Error: That reading type is invalid.", [ - [ "x-time-taken", PerfFormatter::format_perf_data($start_time, $start_handle, null) ] + [ "x-time-taken", $this->perfcounter->render() ] ] ); return false; @@ -95,19 +99,20 @@ class DeviceData implements IAction { new \DateTime($_GET["end"]), !empty($_GET["average-seconds"]) ? intval($_GET["average-seconds"]) : 1 ); + $this->perfcounter->end("sql"); // 2.5: Validate data from database if(empty($data)) { http_response_code(404); header("content-type: text/plain"); - header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, null)); + header("x-time-taken: " . $this->perfcounter->render()); echo("Error: No data has been recorded from the device id for that measurement type in the selected time scale or it doesn't exist."); return false; } // 3: Serialise data - $start_encode = microtime(true); + $this->perfcounter->start("encode"); $response_type = "application/octet-stream"; $response_suggested_filename = "data-" . date(\DateTime::ATOM) . ""; $response = null; @@ -123,13 +128,14 @@ class DeviceData implements IAction { $response = ResponseEncoder::encode_csv($data); break; } + $this->perfcounter->end("encode"); // 4: Send response header("content-length: " . strlen($response)); header("content-type: $response_type"); header("content-disposition: inline; filename=$response_suggested_filename"); - header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, $start_encode)); + header("x-time-taken: " . $this->perfcounter->render()); echo($response); return true; } diff --git a/logic/Actions/DeviceDataBounds.php b/logic/Actions/DeviceDataBounds.php index db8135d..f34049d 100644 --- a/logic/Actions/DeviceDataBounds.php +++ b/logic/Actions/DeviceDataBounds.php @@ -7,11 +7,14 @@ use \AirQuality\Repositories\IMeasurementDataRepository; use \AirQuality\ApiResponseSender; use \AirQuality\Validator; -use \AirQuality\PerfFormatter; + class DeviceDataBounds implements IAction { /** @var TomlConfig */ private $settings; + /** @var \SBRL\PerformanceCounter */ + private $perfcounter; + /** @var IMeasurementDataRepository */ private $measurement_repo; @@ -24,10 +27,12 @@ class DeviceDataBounds implements IAction { public function __construct( TomlConfig $in_settings, IMeasurementDataRepository $in_measurement_repo, - ApiResponseSender $in_sender) { + ApiResponseSender $in_sender, + \SBRL\PerformanceCounter $in_perfcounter) { $this->settings = $in_settings; $this->measurement_repo = $in_measurement_repo; $this->sender = $in_sender; + $this->perfcounter = $in_perfcounter; $this->validator = new Validator($_GET); } @@ -35,7 +40,6 @@ class DeviceDataBounds implements IAction { public function handle() : bool { global $start_time; - $start_handle = microtime(true); // 1: Validate params $this->validator->is_numberish("device-id"); @@ -43,29 +47,32 @@ class DeviceDataBounds implements IAction { // 2: Pull data from database + $this->perfcounter->start("sql"); $data = $this->measurement_repo->get_device_reading_bounds( intval($_GET["device-id"]) ); + $this->perfcounter->end("sql"); // 2.5: Validate data from database if(empty($data)) { http_response_code(404); header("content-type: text/plain"); - header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, null)); + header("x-time-taken: " . $this->perfcounter->render()); echo("Error: No data has been recorded from the device id or it doesn't exist."); return false; } // 3: Serialise data - $start_encode = microtime(true); + $this->perfcounter->start("encode"); $response = json_encode($data); + $this->perfcounter->end("encode"); // 4: Send response header("content-length: " . strlen($response)); header("content-type: application/json"); - header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, $start_encode)); + header("x-time-taken: " . $this->perfcounter->render()); echo($response); return true; } diff --git a/logic/Actions/DeviceDataRecent.php b/logic/Actions/DeviceDataRecent.php index 2468cee..9d52cf5 100644 --- a/logic/Actions/DeviceDataRecent.php +++ b/logic/Actions/DeviceDataRecent.php @@ -9,7 +9,7 @@ use \AirQuality\Repositories\IMeasurementTypeRepository; use \AirQuality\ApiResponseSender; use \AirQuality\Validator; -use \AirQuality\PerfFormatter; + /** * Action that retrieves recent data for a device. @@ -17,6 +17,8 @@ use \AirQuality\PerfFormatter; class DeviceDataRecent implements IAction { /** @var TomlConfig */ private $settings; + /** @var \SBRL\PerformanceCounter */ + private $perfcounter; /** @var IMeasurementDataRepository */ private $measurement_repo; @@ -33,11 +35,13 @@ class DeviceDataRecent implements IAction { TomlConfig $in_settings, IMeasurementDataRepository $in_measurement_repo, IMeasurementTypeRepository $in_type_repo, - ApiResponseSender $in_sender) { + ApiResponseSender $in_sender, + \SBRL\PerformanceCounter $in_perfcounter) { $this->settings = $in_settings; $this->measurement_repo = $in_measurement_repo; $this->type_repo = $in_type_repo; $this->sender = $in_sender; + $this->perfcounter = $in_perfcounter; $this->validator = new Validator($_GET); } @@ -45,7 +49,6 @@ class DeviceDataRecent implements IAction { public function handle() : bool { global $start_time; - $start_handle = microtime(true); // 1: Validate params $this->validator->is_numberish("device-id"); @@ -69,24 +72,26 @@ class DeviceDataRecent implements IAction { if($reading_type_id == null) { $this->sender->send_error_plain( 400, "Error: That reading type is invalid.", [ - [ "x-time-taken", PerfFormatter::format_perf_data($start_time, $start_handle, null) ] + [ "x-time-taken", $this->perfcounter->render() ] ] ); return false; } // 2: Pull data from database + $this->perfcounter->start("sql"); $data = $this->measurement_repo->get_recent_readings( intval($_GET["device-id"]), $reading_type_id, $count ); + $this->perfcounter->end("sql"); // 2.5: Validate data from database if(empty($data)) { http_response_code(404); header("content-type: text/plain"); - header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, null)); + header("x-time-taken: " . $this->perfcounter->render()); echo("Error: No data has been recorded from the device id for that measurement type in the selected time scale or it doesn't exist."); return false; } @@ -96,7 +101,7 @@ class DeviceDataRecent implements IAction { // FUTURE: Refactor this into an AbstractAction or something, but the careful not to shift too much work there // We might want to consider a HttpResponse container or something if we can find a decent PSR implementation - $start_encode = microtime(true); + $this->perfcounter->start("encode"); $response_type = "application/octet-stream"; $response_suggested_filename = "data-" . date(\DateTime::ATOM) . ""; $response = null; @@ -112,13 +117,14 @@ class DeviceDataRecent implements IAction { $response = ResponseEncoder::encode_csv($data); break; } + $this->perfcounter->end("encode"); // 4: Send response header("content-length: " . strlen($response)); header("content-type: $response_type"); header("content-disposition: inline; filename=$response_suggested_filename"); - header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, $start_encode)); + header("x-time-taken: " . $this->perfcounter->render()); echo($response); return true; } diff --git a/logic/Actions/DeviceInfo.php b/logic/Actions/DeviceInfo.php index 6120448..be1c1c4 100644 --- a/logic/Actions/DeviceInfo.php +++ b/logic/Actions/DeviceInfo.php @@ -4,14 +4,18 @@ namespace AirQuality\Actions; use \SBRL\TomlConfig; use \AirQuality\Repositories\IDeviceRepository; +use \AirQuality\Repositories\IMeasurementTypeRepository; use \AirQuality\ApiResponseSender; use \AirQuality\Validator; -use \AirQuality\PerfFormatter; + class DeviceInfo implements IAction { /** @var TomlConfig */ private $settings; + /** @var \SBRL\PerformanceCounter */ + private $perfcounter; + /** @var IDeviceRepository */ private $device_repo; @@ -27,10 +31,12 @@ class DeviceInfo implements IAction { public function __construct( TomlConfig $in_settings, IDeviceRepository $in_device_repo, - ApiResponseSender $in_sender) { + ApiResponseSender $in_sender, + \SBRL\PerformanceCounter $in_perfcounter) { $this->settings = $in_settings; $this->device_repo = $in_device_repo; $this->sender = $in_sender; + $this->perfcounter = $in_perfcounter; $this->validator = new Validator($_GET); } @@ -38,28 +44,30 @@ class DeviceInfo implements IAction { public function handle() : bool { global $start_time; - $start_handle = microtime(true); // 1: Validate params $this->validator->is_numberish("device-id"); $this->validator->run(); // 2: Pull data from database + $this->perfcounter->start("sql"); $data = $this->device_repo->get_device_info_ext( $_GET["device-id"] ); + $this->perfcounter->end("sql"); // 2.5: Validate data from database if(empty($data)) { $this->sender->send_error_plain(404, "Error: No data could be found for that device id.", [ - [ "x-time-taken: ", PerfFormatter::format_perf_data($start_time, $start_handle, null) ] + [ "x-time-taken: ", $this->perfcounter->render() ] ]); return false; } // 3: Serialise data - $start_encode = microtime(true); + $this->perfcounter->start("encode"); $response = json_encode($data); + $this->perfcounter->end("encode"); // 4: Send response @@ -70,7 +78,7 @@ class DeviceInfo implements IAction { header("content-length: " . strlen($response)); header("content-type: application/json"); - header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, $start_encode)); + header("x-time-taken: " . $this->perfcounter->render()); echo($response); return true; } diff --git a/logic/Actions/FetchData.php b/logic/Actions/FetchData.php index 35810b5..5c0b443 100644 --- a/logic/Actions/FetchData.php +++ b/logic/Actions/FetchData.php @@ -9,11 +9,13 @@ use \AirQuality\Repositories\IMeasurementTypeRepository; use \AirQuality\ApiResponseSender; use \AirQuality\Validator; -use \AirQuality\PerfFormatter; + class FetchData implements IAction { /** @var TomlConfig */ private $settings; + /** @var \SBRL\PerformanceCounter */ + private $perfcounter; /** @var IMeasurementDataRepository */ private $measurement_repo; @@ -30,11 +32,13 @@ class FetchData implements IAction { TomlConfig $in_settings, IMeasurementDataRepository $in_measurement_repo, IMeasurementTypeRepository $in_type_repo, - ApiResponseSender $in_sender) { + ApiResponseSender $in_sender, + \SBRL\PerformanceCounter $in_perfcounter) { $this->settings = $in_settings; $this->measurement_repo = $in_measurement_repo; $this->type_repo = $in_type_repo; $this->sender = $in_sender; + $this->perfcounter = $in_perfcounter; $this->validator = new Validator($_GET); } @@ -42,7 +46,6 @@ class FetchData implements IAction { public function handle() : bool { global $start_time; - $start_handle = microtime(true); // 1: Validate params $this->validator->is_datetime("datetime"); @@ -58,7 +61,7 @@ class FetchData implements IAction { if($measurement_type_id == null) { $this->sender->send_error_plain( 400, "Error: That reading type is invalid.", [ - [ "x-time-taken", PerfFormatter::format_perf_data($start_time, $start_handle, null) ] + [ "x-time-taken", $this->perfcounter->render() ] ] ); return false; @@ -66,24 +69,26 @@ class FetchData implements IAction { // 2: Pull data from database + $this->perfcounter->start("sql"); $data = $this->measurement_repo->get_readings_by_date( new \DateTime($_GET["datetime"]), $measurement_type_id ); + $this->perfcounter->end("sql"); // 2.5: Validate data from database if(empty($data)) { $this->sender->send_error_plain(404, "Error: No data could be found for that timestamp.", - [ PerfFormatter::format_perf_data($start_time, $start_handle, null) ] + [ [ "x-time-taken", $this->perfcounter->render() ] ] ); return false; } // 3: Serialise data - $start_encode = microtime(true); + $this->perfcounter->start("encode"); $response_type = "application/octet-stream"; $response_suggested_filename = "data-" . date(\DateTime::ATOM) . ""; $response = null; @@ -99,6 +104,7 @@ class FetchData implements IAction { $response = ResponseEncoder::encode_csv($data); break; } + $this->perfcounter->end("encode"); // 4: Send response @@ -111,7 +117,7 @@ class FetchData implements IAction { header("content-type: $response_type"); header("content-length: " . strlen($response)); header("content-disposition: inline; filename=$response_suggested_filename"); - header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, $start_encode)); + header("x-time-taken: " . $this->perfcounter->render()); echo($response); return true; } diff --git a/logic/Actions/Index.php b/logic/Actions/Index.php index 0a23b1e..a76c0a4 100644 --- a/logic/Actions/Index.php +++ b/logic/Actions/Index.php @@ -4,25 +4,27 @@ namespace AirQuality\Actions; use \SBRL\TomlConfig; -use \AirQuality\PerfFormatter; class Index implements IAction { /** @var TomlConfig */ private $settings; + /** @var \SBRL\PerformanceCounter */ + private $perfcounter; /** @var \ParsedownExtra */ private $parsedown_ext; public function __construct( - TomlConfig $in_settings) { + TomlConfig $in_settings, + \SBRL\PerformanceCounter $in_perfcounter) { $this->settings = $in_settings; + $this->perfcounter = $in_perfcounter; } public function handle() : bool { global $start_time; - $start_handle = microtime(true); - + $this->perfcounter->start("handle"); // 1: Parse markdown $result = "Welcome to the Air Quality Web HTTP API! @@ -38,7 +40,7 @@ Note that if you have deployed your own version of the air quality web interface // 2: Send response header("content-length: " . strlen($result)); header("content-type: text/plain"); - header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, null)); + header("x-time-taken: " . $this->perfcounter->render()); echo($result); return true; } diff --git a/logic/Actions/ListDevices.php b/logic/Actions/ListDevices.php index bb6cc4c..52265e4 100644 --- a/logic/Actions/ListDevices.php +++ b/logic/Actions/ListDevices.php @@ -7,13 +7,14 @@ use \SBRL\ResponseEncoder; use \AirQuality\Repositories\IDeviceRepository; use \AirQuality\ApiResponseSender; -use \AirQuality\PerfFormatter; class ListDevices implements IAction { /** @var TomlConfig */ private $settings; /** @var IDeviceRepository */ private $device_repo; + /** @var \SBRL\PerformanceCounter */ + private $perfcounter; /** @var ApiResponseSender */ private $sender; @@ -21,16 +22,17 @@ class ListDevices implements IAction { public function __construct( TomlConfig $in_settings, IDeviceRepository $in_device_repo, - ApiResponseSender $in_sender) { + ApiResponseSender $in_sender, + \SBRL\PerformanceCounter $in_perfcounter) { $this->settings = $in_settings; $this->device_repo = $in_device_repo; $this->sender = $in_sender; + $this->perfcounter = $in_perfcounter; } public function handle() : bool { global $start_time; - $start_handle = microtime(true); // 1: Parse & validate parameters $only_with_location = !empty($_GET["only-with-location"]); @@ -45,21 +47,23 @@ class ListDevices implements IAction { // 2: Pull data from database + $this->perfcounter->start("sql"); $data = $this->device_repo->get_all_devices($only_with_location); + $this->perfcounter->end("sql"); // 2.5: Validate data from database if(empty($data)) { $this->sender->send_error_plain(404, "Error: No devices are currently present in the system.", - [ "x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, null) ] + [ "x-time-taken: ", $this->perfcounter->render() ] ); return false; } // 3: Serialise data - $start_encode = microtime(true); + $this->perfcounter->start("encode"); $response = null; $response_type = "application/octet-stream"; $response_suggested_filename = "data-" . date(\DateTime::ATOM) . ""; @@ -75,6 +79,7 @@ class ListDevices implements IAction { $response = ResponseEncoder::encode_csv($data); break; } + $this->perfcounter->end("encode"); // 4: Send response @@ -84,7 +89,7 @@ class ListDevices implements IAction { header("content-length: " . strlen($response)); header("content-type: $response_type"); header("content-disposition: inline; filename=$response_suggested_filename"); - header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, $start_encode)); + header("x-time-taken: " . $this->perfcounter->render()); echo($response); return true; } diff --git a/logic/Actions/ListReadingTypes.php b/logic/Actions/ListReadingTypes.php index 2643fbb..6b00cf3 100644 --- a/logic/Actions/ListReadingTypes.php +++ b/logic/Actions/ListReadingTypes.php @@ -7,11 +7,13 @@ use \SBRL\ResponseEncoder; use \AirQuality\Repositories\IMeasurementTypeRepository; use \AirQuality\ApiResponseSender; -use \AirQuality\PerfFormatter; class ListReadingTypes implements IAction { /** @var TomlConfig */ private $settings; + /** @var \SBRL\PerformanceCounter */ + private $perfcounter; + /** @var IMeasurementTypeRepository */ private $types_repo; @@ -21,16 +23,17 @@ class ListReadingTypes implements IAction { public function __construct( TomlConfig $in_settings, IMeasurementTypeRepository $in_types_repo, - ApiResponseSender $in_sender) { + ApiResponseSender $in_sender, + \SBRL\PerformanceCounter $in_perfcounter) { $this->settings = $in_settings; $this->types_repo = $in_types_repo; $this->sender = $in_sender; + $this->perfcounter = $in_perfcounter; } public function handle() : bool { global $start_time; - $start_handle = microtime(true); // 1: Parse & validate parameters $device_id = !empty($_GET["device-id"]) ? intval($_GET["device-id"]) : null; @@ -45,10 +48,13 @@ class ListReadingTypes implements IAction { // 1: Pull data from database $data = null; + + $this->perfcounter->start("sql"); if(!is_int($device_id)) $data = $this->types_repo->get_all_types(); else $data = $this->types_repo->get_types_by_device($device_id); + $this->perfcounter->end("sql"); // 1.5: Validate data from database if(empty($data)) { @@ -56,7 +62,7 @@ class ListReadingTypes implements IAction { } // 3: Serialise data - $start_encode = microtime(true); + $this->perfcounter->start("encode"); $response = null; $response_type = "application/octet-stream"; $response_suggested_filename = "data-" . date(\DateTime::ATOM) . ""; @@ -72,6 +78,7 @@ class ListReadingTypes implements IAction { $response = ResponseEncoder::encode_csv($data); break; } + $this->perfcounter->end("encode"); // 4: Send response @@ -81,7 +88,7 @@ class ListReadingTypes implements IAction { header("content-length: " . strlen($response)); header("content-type: $response_type"); header("content-disposition: inline; filename=$response_suggested_filename"); - header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, $start_encode)); + header("x-time-taken: " . $this->perfcounter->render()); echo($response); return true; } diff --git a/logic/Actions/Version.php b/logic/Actions/Version.php index d256df5..4df08d5 100644 --- a/logic/Actions/Version.php +++ b/logic/Actions/Version.php @@ -4,23 +4,24 @@ namespace AirQuality\Actions; use \SBRL\TomlConfig; -use \AirQuality\PerfFormatter; class Version implements IAction { /** @var TomlConfig */ private $settings; + /** @var \SBRL\PerformanceCounter */ + private $perfcounter; public function __construct( - TomlConfig $in_settings) { + TomlConfig $in_settings, + \SBRL\PerformanceCounter $in_perfcounter) { $this->settings = $in_settings; + $this->perfcounter = $in_perfcounter; } public function handle() : bool { global $start_time; - $start_handle = microtime(true); - // 1: Parse markdown $result = file_get_contents(ROOT_DIR."version"); @@ -29,7 +30,7 @@ class Version implements IAction { // 2: Send response header("content-length: " . strlen($result)); header("content-type: text/plain"); - header("x-time-taken: " . PerfFormatter::format_perf_data($start_time, $start_handle, null)); + header("x-time-taken: " . $this->perfcounter->render()); echo($result); return true; } diff --git a/logic/Database.php b/logic/Database.php index 3895ad6..2a5f95c 100644 --- a/logic/Database.php +++ b/logic/Database.php @@ -10,8 +10,16 @@ use Psr\Container\ContainerInterface; */ class Database { - // Whether the database connection should be read only or not. - // Defaults to true, as we don't need to write _anything_ to the database. + /** @var TomlConfig */ + private $settings; + /** @var \SBRL\PerformanceCounter */ + private $perfcounter; + + /** + * Whether the database connection should be read only or not. + * Defaults to true, as we don't need to write _anything_ to the database. + * @var bool + */ private const read_only = true; private $connection; @@ -23,13 +31,15 @@ class Database \PDO::ATTR_AUTOCOMMIT => false ]; - function __construct(TomlConfig $in_settings) { + function __construct(TomlConfig $in_settings, \SBRL\PerformanceCounter $in_perfcounter) { $this->settings = $in_settings; + $this->perfcounter = $in_perfcounter; $this->connect(); // Connect automagically } public function connect() { + $this->perfcounter->start("dbconnect"); $this->connection = new \PDO( $this->get_connection_string(), $this->settings->get("database.username"), @@ -39,6 +49,7 @@ class Database // Make this connection read-only if(self::read_only) $this->connection->query("SET SESSION TRANSACTION READ ONLY;"); + $this->perfcounter->end("dbconnect"); } public function query($sql, $variables = []) { diff --git a/logic/PerfFormatter.php b/logic/PerfFormatter.php deleted file mode 100644 index 814b093..0000000 --- a/logic/PerfFormatter.php +++ /dev/null @@ -1,27 +0,0 @@ -