From 1ce4cb83f83f829a1f172748467e05213b2c9dfc Mon Sep 17 00:00:00 2001 From: Mihai Oprea Date: Tue, 24 Nov 2015 11:41:25 +0200 Subject: [PATCH 1/6] Add metric for total value of abandoned carts --- .../community/CoScale/Monitor/Model/Metric/Order.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/app/code/community/CoScale/Monitor/Model/Metric/Order.php b/app/code/community/CoScale/Monitor/Model/Metric/Order.php index f37893a..591bf1e 100644 --- a/app/code/community/CoScale/Monitor/Model/Metric/Order.php +++ b/app/code/community/CoScale/Monitor/Model/Metric/Order.php @@ -411,7 +411,8 @@ public function getAbandonnedCarts() $collection->prepareForAbandonedReport(array()); $collection->getSelect() ->columns(array('store_id' => 'main_table.store_id', - 'count' => 'COUNT(*)')) + 'count' => 'COUNT(*)', + 'subtotal' => 'subtotal')) ->group('main_table.store_id'); $output = array(); foreach ($collection as $order) { @@ -422,10 +423,19 @@ public function getAbandonnedCarts() 'store_id' => (int)$order->getStoreId(), 'type' => 'A' ); + $output[] = array( + 'name' => 'Total value of abandoned carts', + 'unit' => 'Amount', + 'value' => (float)$order->getSubtotal(), + 'store_id' => (int)$order->getStoreId(), + 'type' => 'A' + ); } return $output; } + + public function getEmailQueueSize() { $edition = method_exists('Mage', 'getEdition') ? Mage::getEdition():false; From 2983fdd4a7a9bd585245b683b1a2c3a7ba31ca50 Mon Sep 17 00:00:00 2001 From: Mihai Oprea Date: Fri, 27 Nov 2015 15:41:11 +0200 Subject: [PATCH 2/6] add rum implementation --- app/code/community/CoScale/Monitor/etc/config.xml | 11 ++++++++++- .../frontend/base/default/layout/coscale_monitor.xml | 12 ++++++++++++ .../base/default/template/coscale_monitor/rum.phtml | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 app/design/frontend/base/default/layout/coscale_monitor.xml create mode 100644 app/design/frontend/base/default/template/coscale_monitor/rum.phtml diff --git a/app/code/community/CoScale/Monitor/etc/config.xml b/app/code/community/CoScale/Monitor/etc/config.xml index 66c0632..9ebc12a 100644 --- a/app/code/community/CoScale/Monitor/etc/config.xml +++ b/app/code/community/CoScale/Monitor/etc/config.xml @@ -2,7 +2,7 @@ - 0.10.0 + 0.11.0 @@ -201,6 +201,15 @@ + + + + + coscale_monitor.xml + + + + diff --git a/app/design/frontend/base/default/layout/coscale_monitor.xml b/app/design/frontend/base/default/layout/coscale_monitor.xml new file mode 100644 index 0000000..3325f50 --- /dev/null +++ b/app/design/frontend/base/default/layout/coscale_monitor.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/design/frontend/base/default/template/coscale_monitor/rum.phtml b/app/design/frontend/base/default/template/coscale_monitor/rum.phtml new file mode 100644 index 0000000..6cf1da6 --- /dev/null +++ b/app/design/frontend/base/default/template/coscale_monitor/rum.phtml @@ -0,0 +1 @@ + From 72e4d140e45b847dad8dca726589e3b8c856febf Mon Sep 17 00:00:00 2001 From: Stijn Polfliet Date: Mon, 14 Dec 2015 14:10:35 +0100 Subject: [PATCH 3/6] Update package.xml --- app/code/community/CoScale/Monitor/etc/config.xml | 2 +- build.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/code/community/CoScale/Monitor/etc/config.xml b/app/code/community/CoScale/Monitor/etc/config.xml index 66c0632..6c63b6c 100644 --- a/app/code/community/CoScale/Monitor/etc/config.xml +++ b/app/code/community/CoScale/Monitor/etc/config.xml @@ -2,7 +2,7 @@ - 0.10.0 + 0.11.0 diff --git a/build.sh b/build.sh index b6b9be4..3c74e57 100755 --- a/build.sh +++ b/build.sh @@ -33,13 +33,13 @@ cat << EOF > package.xml CoScale $VERSION - beta - Commercial + stable + MITL community This module enables you to send important business and IT metrics from Magento to CoScale. The CoScale module exposes Magento events and metrics to the CoScale Agent. The metrics contain business metrics, such as the number of products, orders, abondoned carts, etc and technical metrics such as Magento caching metrics. The events contain magento admin actions such as page cache flushes, reindexing, etc. - The CoScale module is currently in Beta + The CoScale module is still under development. All feedback is welcome. CoScale Developercs-devinfo@coscale.com 2015-08-13 From 77f3ce357e2387e163ec29bae26a5a25105078c0 Mon Sep 17 00:00:00 2001 From: Vladimir Kerkhoff Date: Fri, 29 Jan 2016 16:05:16 +0100 Subject: [PATCH 4/6] Version: 0.11.0: Added check on database tables Added option to enable/disable module thru system configuration (system=>coscale monitoring) Changed generation (coscale.php) to Model/Output/Generator.php Added event dispatch to build generated output Added extra checks on saving order data to avoid errors during checkout Added statistics for orders processing, picking and calculated values for pick duration Changed debug logging for coscale.php and rounding time to 5 decimals Added extra exception checks on generation of output --- .../community/CoScale/Monitor/Helper/Data.php | 58 ++++ .../CoScale/Monitor/Model/Event/Observer.php | 83 +++++ .../CoScale/Monitor/Model/Metric/Abstract.php | 25 +- .../CoScale/Monitor/Model/Metric/Customer.php | 7 + .../CoScale/Monitor/Model/Metric/File.php | 32 ++ .../CoScale/Monitor/Model/Metric/Observer.php | 42 +++ .../CoScale/Monitor/Model/Metric/Order.php | 289 +++++++++++++++--- .../CoScale/Monitor/Model/Metric/Product.php | 19 ++ .../CoScale/Monitor/Model/Metric/Rewrite.php | 28 +- .../CoScale/Monitor/Model/Metric/System.php | 39 +++ .../Monitor/Model/Output/Generator.php | 38 +++ .../community/CoScale/Monitor/etc/config.xml | 48 ++- .../community/CoScale/Monitor/etc/system.xml | 57 ++++ shell/coscale.php | 178 +---------- 14 files changed, 728 insertions(+), 215 deletions(-) create mode 100644 app/code/community/CoScale/Monitor/Model/Event/Observer.php create mode 100644 app/code/community/CoScale/Monitor/Model/Metric/Observer.php create mode 100644 app/code/community/CoScale/Monitor/Model/Metric/System.php create mode 100644 app/code/community/CoScale/Monitor/Model/Output/Generator.php create mode 100644 app/code/community/CoScale/Monitor/etc/system.xml diff --git a/app/code/community/CoScale/Monitor/Helper/Data.php b/app/code/community/CoScale/Monitor/Helper/Data.php index 26aa927..9d2aa71 100644 --- a/app/code/community/CoScale/Monitor/Helper/Data.php +++ b/app/code/community/CoScale/Monitor/Helper/Data.php @@ -10,5 +10,63 @@ */ class CoScale_Monitor_Helper_Data extends Mage_Core_Helper_Abstract { + protected $debug = false; + protected $timer = array(); + public function isEnabled() + { + if (!Mage::getStoreConfig('system/coscale_monitor/enabled')) { + return false; + } + + $resource = Mage::getSingleton('core/resource')->getConnection('core_write'); + + try { + $tables = Mage::getConfig()->getNode('global/models/coscale_monitor_resource/entities')->asArray(); + + foreach ($tables as $id => $data) { + $metricTable = $resource->getTableName($data['table']); + if (! $resource->isTableExists($metricTable)) { + return false; + } + } + } catch (Exception $ex) { + return false; + } + return true; + } + + public function writeDebug($msg) + { + if ($this->debug) { + Mage::log($msg, Zend_Log::DEBUG, 'coscale-collect.log', true); + } + } + + public function debugStart($name) + { + $this->writeDebug('Start: ' . $name); + $this->timer[$name] = microtime(true); + } + + public function debugEnd($name) + { + $startTime = isset($this->timer[$name]) ? $this->timer[$name]:microtime(true); + $duration = microtime(true)-$startTime; + + $this->writeDebug('Completed: ' . $name . ' (' . number_format($duration, 6) . 's)'); + } + + public function debugEndError($name, $ex) + { + $startTime = isset($this->timer[$name]) ? $this->timer[$name]:microtime(true); + $duration = microtime(true)-$startTime; + + $this->writeDebug('Error: ' . $name . ' (' . number_format($duration, 6) . 's) [' . $ex->getMessage() . ']'); + } + + public function enableDebug() + { + $this->debug = true; + } } \ No newline at end of file diff --git a/app/code/community/CoScale/Monitor/Model/Event/Observer.php b/app/code/community/CoScale/Monitor/Model/Event/Observer.php new file mode 100644 index 0000000..ab6fe8a --- /dev/null +++ b/app/code/community/CoScale/Monitor/Model/Event/Observer.php @@ -0,0 +1,83 @@ +getEvent(); + + /** @var CoScale_Monitor_Helper_Data $logger */ + $logger = $event->getLogger(); + /** @var CoScale_Monitor_Model_Output_Generator $output */ + $output = $event->getOutput(); + + // Get Events Collection + try { + $logger->debugStart('Events Collection'); + + $collection = Mage::getModel('coscale_monitor/event')->getCollection(); + /** @var CoScale_Monitor_Model_Event $event */ + foreach ($collection as $event) { + $output->addEvent( + array( + 'type' => $event->getTypeGroup(), + 'message' => $event->getDescription(), + 'data' => array_merge( + array('originator' => $event->getSource()), + unserialize($event->getEventData()) + ), + 'start_time' => (int)(time() - $event->getTimestampStart()), + 'stop_time' => (int)($event->getTimestampEnd() != 0 ? (time() - $event->getTimestampEnd()) : 0), + ) + ); + + $event->delete(); + if ($event->getState() != $event::STATE_ENABLED) { + //$event->delete(); + } + } + $logger->debugEnd('Events Collection'); + } catch (Exception $ex) { + $logger->debugEndError('Events Collection', $ex); + } + + // Get Cronjob Collection + try { + $logger->debugStart('Cronjob Collection'); + + $endDateTime = date('U'); + $collection = Mage::getModel('cron/schedule')->getCollection() + ->addFieldToFilter('finished_at', array('from' => date('Y-m-d H:i:s', $endDateTime-65))) + ->setOrder('finished_at', 'DESC'); + /** @var Mage_Cron_Model_Schedule $event */ + foreach ($collection as $cron) { + $output['events'][] = array( + 'type' => CoScale_Monitor_Model_Event::GROUP_CRON, + 'message' => $cron->getJobCode(), + 'status' => $cron->getStatus(), + 'start_time' => (int)(time() - strtotime($cron->getExecutedAt())), + 'stop_time' => (int)(time() - strtotime($cron->getFinishedAt())), + ); + } + $logger->debugEnd('Cronjob Collection'); + } catch (Exception $ex) { + $logger->debugEndError('Events Collection', $ex); + } + + try { + $logger->debugStart('Maintenance Flag'); + if (file_exists(Mage::getBaseDir('base') . DS . 'maintenance.flag')) { + $output['events'][] = array( + 'type' => CoScale_Monitor_Model_Event::GROUP_ADMIN, + 'message' => 'Maintenance mode enabled', + 'start_time' => 0, + 'stop_time' => 0, + ); + } + $logger->debugEnd('Maintenance Flag'); + } catch (Exception $ex) { + $logger->debugEndError('Maintenance Flag', $ex); + } + + } +} \ No newline at end of file diff --git a/app/code/community/CoScale/Monitor/Model/Metric/Abstract.php b/app/code/community/CoScale/Monitor/Model/Metric/Abstract.php index 94cf1ee..1fb33d7 100644 --- a/app/code/community/CoScale/Monitor/Model/Metric/Abstract.php +++ b/app/code/community/CoScale/Monitor/Model/Metric/Abstract.php @@ -10,6 +10,7 @@ class CoScale_Monitor_Model_Metric_Abstract { protected $_metric = false; + protected $_helper = false; protected $_metricData = array(); protected $_metricType = CoScale_Monitor_Model_Metric::TYPE_APPLICATION; @@ -20,6 +21,7 @@ class CoScale_Monitor_Model_Metric_Abstract public function __construct() { $this->_metric = Mage::getModel('coscale_monitor/metric'); + $this->_helper = Mage::helper('coscale_monitor'); $this->_contruct(); } @@ -74,14 +76,33 @@ protected function setMetric($action, $key, $store, $value, $unit = false, $type } } + /** + * @param $key + * @param $store + * @return int + */ protected function getMetric($key, $store) { + if (!$data = $this->getMetricData($key, $store)) { + return 0; + } + return $data->getValue(); + } + + /** + * @param $key + * @param $store + * @return $data|bool + */ + protected function getMetricData($key, $store) + { + /** @var CoScale_Monitor_Model_Metric $data */ $data = $this->_metric->loadByKey($key, $store); if (!$data->getId()) { - return 0; + return false; } - return $data->getValue(); + return $data; } } \ No newline at end of file diff --git a/app/code/community/CoScale/Monitor/Model/Metric/Customer.php b/app/code/community/CoScale/Monitor/Model/Metric/Customer.php index c0a1466..d522b31 100644 --- a/app/code/community/CoScale/Monitor/Model/Metric/Customer.php +++ b/app/code/community/CoScale/Monitor/Model/Metric/Customer.php @@ -38,6 +38,10 @@ public function _contruct() */ public function addNew(Varien_Event_Observer $observer) { + if (!$this->_helper->isEnabled()) { + return; + } + /** @var Mage_Customer_Model_Customer $customer */ $customer = $observer->getEvent()->getCustomer(); @@ -58,6 +62,9 @@ public function addNew(Varien_Event_Observer $observer) */ public function dailyCron() { + if (!$this->_helper->isEnabled()) { + return; + } $this->updateTotalCount(); } diff --git a/app/code/community/CoScale/Monitor/Model/Metric/File.php b/app/code/community/CoScale/Monitor/Model/Metric/File.php index ed66e30..185019a 100644 --- a/app/code/community/CoScale/Monitor/Model/Metric/File.php +++ b/app/code/community/CoScale/Monitor/Model/Metric/File.php @@ -9,6 +9,38 @@ */ class CoScale_Monitor_Model_Metric_File extends CoScale_Monitor_Model_Metric_Abstract { + public function generate(Varien_Event_Observer $observer) + { + $event = $observer->getEvent(); + + /** @var CoScale_Monitor_Helper_Data $logger */ + $logger = $event->getLogger(); + /** @var CoScale_Monitor_Model_Output_Generator $output */ + $output = $event->getOutput(); + + // Get Error reports + try { + $logger->debugStart('Error Reports'); + + $output->addMetric($this->getErrorReports()); + + $logger->debugEnd('Error Reports'); + } catch (Exception $ex) { + $logger->debugEndError('Error Reports', $ex); + } + + // Get Logfiles (name and size) + try { + $logger->debugStart('Logfiles'); + + foreach ($this->getLogFiles() as $data) { + $output->addMetric($data); + } + $logger->debugEnd('Logfiles'); + } catch (Exception $ex) { + $logger->debugEndError('Logfiles', $ex); + } + } /** * Get amount of reports in var/report diff --git a/app/code/community/CoScale/Monitor/Model/Metric/Observer.php b/app/code/community/CoScale/Monitor/Model/Metric/Observer.php new file mode 100644 index 0000000..743b7e9 --- /dev/null +++ b/app/code/community/CoScale/Monitor/Model/Metric/Observer.php @@ -0,0 +1,42 @@ +getEvent(); + + /** @var CoScale_Monitor_Helper_Data $logger */ + $logger = $event->getLogger(); + /** @var CoScale_Monitor_Model_Output_Generator $output */ + $output = $event->getOutput(); + + // Get Metrics Collection + try { + $logger->debugStart('Metrics Collection'); + + $metricOrderDelete = Mage::getSingleton('coscale_monitor/metric_order'); + $collection = Mage::getModel('coscale_monitor/metric')->getCollection(); + /** @var CoScale_Monitor_Model_Metric $metric */ + foreach ($collection as $metric) { + $output->addMetric( + array( + 'name' => $metric->getName(), + 'unit' => $metric->getUnit(), + 'value' => (float)$metric->getValue(), + 'store_id' => (int)$metric->getStoreId(), + 'type' => $metric->getTypeText() + ) + ); + + // Check if metric need to be reset after collection + if ($metricOrderDelete->resetOnCollect($metric->getKey())) { + $metric->delete(); + } + } + $logger->debugEnd('Metrics Collection'); + } catch (Exception $ex) { + $logger->debugEndError('Metrics Collection', $ex); + } + } +} \ No newline at end of file diff --git a/app/code/community/CoScale/Monitor/Model/Metric/Order.php b/app/code/community/CoScale/Monitor/Model/Metric/Order.php index f37893a..d928727 100644 --- a/app/code/community/CoScale/Monitor/Model/Metric/Order.php +++ b/app/code/community/CoScale/Monitor/Model/Metric/Order.php @@ -11,6 +11,10 @@ class CoScale_Monitor_Model_Metric_Order extends CoScale_Monitor_Model_Metric_Abstract { + protected $statusPendingPickPack = false; + protected $statusPickPack = false; + protected $statusCompletedPickPack = false; + /** * Identifier for total orders */ @@ -41,6 +45,19 @@ class CoScale_Monitor_Model_Metric_Order extends CoScale_Monitor_Model_Metric_Ab const KEY_ORDER_STATE_PROCESSING = 2031; const KEY_ORDER_STATE_COMPLETED = 2032; + /** + * Identifier for pick order and time calculation + */ + const KEY_ORDER_STATE_PENDING_PICKPACK = 2040; + const KEY_ORDER_STATE_CURRENT_PICKPACK = 2041; + const KEY_ORDER_STATE_COMPLETED_PICKPACK = 2042; + const KEY_START_PICKPACK = 2043; + const KEY_PICKED_QTY = 2044; + const KEY_PICKED_TIME = 2045; + const KEY_AVGTIME_PICKPACK = 2046; + const KEY_TIME_PENDING_PICKPACK = 2047; + const KEY_TIME_CURRENT_PICKPACK = 2048; + /** * Public contructor function */ @@ -123,6 +140,58 @@ public function _contruct() 'description' => 'The total number of orders in completed state', 'unit' => 'orders' ); + + $this->_metricData[self::KEY_ORDER_STATE_PENDING_PICKPACK] = array( + 'name' => 'Orders pending pick/pack', + 'description' => 'The total number of orders in waiting for pick/pack state', + 'unit' => 'orders' + ); + + $this->_metricData[self::KEY_ORDER_STATE_CURRENT_PICKPACK] = array( + 'name' => 'Orders pick/pack', + 'description' => 'The total number of orders in pick/pack state', + 'unit' => 'orders' + ); + + $this->_metricData[self::KEY_ORDER_STATE_COMPLETED_PICKPACK] = array( + 'name' => 'Orders completed pick/pack', + 'description' => 'The total number of orders in completed pick/pack state', + 'unit' => 'orders' + ); + + $this->_metricData[self::KEY_PICKED_QTY] = array( + 'name' => 'Picked qty', + 'description' => 'The qty of orders picked', + 'unit' => 'qty' + ); + + $this->_metricData[self::KEY_PICKED_TIME] = array( + 'name' => 'Picked time', + 'description' => 'Total time to pick/pack', + 'unit' => 'seconds' + ); + + $this->_metricData[self::KEY_AVGTIME_PICKPACK] = array( + 'name' => 'Avg time pick/pack', + 'description' => 'Avg time to pick/pack an order', + 'unit' => 'seconds' + ); + + $this->_metricData[self::KEY_TIME_PENDING_PICKPACK] = array( + 'name' => 'Time pending pick/pack', + 'description' => 'The total time needed to pick/pack new orders', + 'unit' => 'seconds' + ); + + $this->_metricData[self::KEY_TIME_CURRENT_PICKPACK] = array( + 'name' => 'Time current pick/pack ', + 'description' => 'The total time needed to pick/pack current orders in pick/pack state', + 'unit' => 'seconds' + ); + + $this->statusPendingPickPack = Mage::getStoreConfig('system/coscale_monitor/status_pickpack_pending'); + $this->statusPickPack = Mage::getStoreConfig('system/coscale_monitor/status_pickpack'); + $this->statusCompletedPickPack = Mage::getStoreConfig('system/coscale_monitor/status_pickpack_completed'); } public function resetOnCollect($key) @@ -147,11 +216,14 @@ public function resetOnCollect($key) * * @param Varien_Event_Observer $observer */ - public function addNew(Varien_Event_Observer $observer) + public function salesOrderPlaceAfter(Varien_Event_Observer $observer) { + if (!$this->_helper->isEnabled()) { + return; + } + /** @var Mage_Sales_Model_Order $order */ $order = $observer->getEvent()->getOrder(); - $amountUnit = Mage::getStoreConfig('currency/options/base', $order->getStoreId()); $this->setMetric( @@ -198,57 +270,144 @@ public function addNew(Varien_Event_Observer $observer) 1 ); - // Update state statistics (only is changed) + $this->updateAvgOrderValues($order->getStoreId()); + } + + /** + * Save order status changes on order save + * + * @param Varien_Event_Observer $observer + */ + public function salesOrderSaveCommitAfter(Varien_Event_Observer $observer) + { + $keys = array(); + if (!$this->_helper->isEnabled()) { + return; + } + + /** @var Mage_Sales_Model_Order $order */ + $order = $observer->getEvent()->getOrder(); + + // Update state/status statistics (only if changed) if ($order->getState() != $order->getOrigData('state')) { - // Decrease order processing when previous state was processing - if ($order->getOrigData('state') == 'new') { - $this->setMetric( - self::ACTION_INCREMENT, - self::KEY_ORDER_STATE_NEW, - $order->getStoreId(), - -1 - ); + // Decrease qty for previous state + switch ($order->getOrigData('state')) { + case Mage_Sales_Model_Order::STATE_NEW: + $keys[self::KEY_ORDER_STATE_NEW] = -1; + break; + case Mage_Sales_Model_Order::STATE_PROCESSING: + $keys[self::KEY_ORDER_STATE_PROCESSING] = -1; + break; } - // Decrease order processing when previous state was processing - if ($order->getOrigData('state') == 'processing') { - $this->setMetric( - self::ACTION_INCREMENT, - self::KEY_ORDER_STATE_PROCESSING, - $order->getStoreId(), - -1 - ); + // Increase qty for current state + switch ($order->getData('state')) { + case Mage_Sales_Model_Order::STATE_NEW: + $keys[self::KEY_ORDER_STATE_NEW] = 1; + break; + case Mage_Sales_Model_Order::STATE_PROCESSING: + $keys[self::KEY_ORDER_STATE_PROCESSING] = 1; + break; + case Mage_Sales_Model_Order::STATE_COMPLETE: + $keys[self::KEY_ORDER_STATE_COMPLETED] = 1; + break; } + } - // Increase orders with the state new - if ($order->getState() == 'new') { - $this->setMetric( - self::ACTION_INCREMENT, - self::KEY_ORDER_STATE_NEW, - $order->getStoreId(), - 1 - ); + if ($order->getStatus() != $order->getOrigData('status')) { + // Decrease qty for previous state + switch ($order->getOrigData('status')) { + case $this->statusPendingPickPack: + $keys[self::KEY_ORDER_STATE_PENDING_PICKPACK] = -1; + break; + case $this->statusPickPack: + $keys[self::KEY_ORDER_STATE_CURRENT_PICKPACK] = -1; + break; + case $this->statusCompletedPickPack: + $keys[self::KEY_ORDER_STATE_COMPLETED_PICKPACK] = -1; + break; } - // Increase orders with the state processing - if ($order->getState() == 'processing') { - $this->setMetric( - self::ACTION_INCREMENT, - self::KEY_ORDER_STATE_PROCESSING, - $order->getStoreId(), - 1 - ); + // Increase qty for current state + switch ($order->getData('status')) { + case $this->statusPendingPickPack: + $keys[self::KEY_ORDER_STATE_PENDING_PICKPACK] = 1; + break; + case $this->statusPickPack: + $keys[self::KEY_ORDER_STATE_CURRENT_PICKPACK] = 1; + break; + case $this->statusCompletedPickPack: + $keys[self::KEY_ORDER_STATE_COMPLETED_PICKPACK] = 1; + break; } - // Increase orders with the state complete - if ($order->getState() == 'complete') { + } + if (count($keys)>0) { + // Check if order picked data decreases + if (isset($keys[self::KEY_ORDER_STATE_CURRENT_PICKPACK]) && + $keys[self::KEY_ORDER_STATE_CURRENT_PICKPACK]<0) { + // Get difference between last timestamp and now (time used for orderpicking) + $timeUsed = 1; + if ($metricData = $this->getMetricData(self::KEY_PICKED_TIME, $order->getStoreId())) { + $currentDate = date('U', Mage::getModel('core/date')->timestamp(time())); + $lastDate = date('U', strtotime($metricData->getUpdatedAt())); + $timeUsed = $currentDate - $lastDate; + } + $keys[self::KEY_PICKED_TIME] = $timeUsed; + $keys[self::KEY_PICKED_QTY] = 1; + } + + foreach ($keys as $key => $qty) { + if ($qty <> 0) { + $this->setMetric( + self::ACTION_INCREMENT, + $key, + $order->getStoreId(), + $qty + ); + } + + } + $this->updateAvgPickValues($order->getStoreId()); + } + + } + + /** + * Update pick/pack avarage values + * + * @param $storeId + */ + public function updateAvgPickValues($storeId) + { + $pickTime = $this->getMetric(self::KEY_PICKED_TIME, $storeId); + $pickQty = $this->getMetric(self::KEY_PICKED_QTY, $storeId); + + // Update avg pick time + if ($pickQty>0) { + $avgPickTime = floor($pickTime/$pickQty); + $this->setMetric( + self::ACTION_UPDATE, + self::KEY_AVGTIME_PICKPACK, + $storeId, + $avgPickTime, + 'seconds' + ); + + $updateAvgKeys = array( + self::KEY_ORDER_STATE_PENDING_PICKPACK=>self::KEY_TIME_PENDING_PICKPACK, + self::KEY_ORDER_STATE_CURRENT_PICKPACK=>self::KEY_TIME_CURRENT_PICKPACK + ); + // Update avg values for keys + foreach ($updateAvgKeys as $from => $to) { + $qty = $this->getMetric($from, $storeId); + $this->setMetric( - self::ACTION_INCREMENT, - self::KEY_ORDER_STATE_COMPLETED, - $order->getStoreId(), - 1 + self::ACTION_UPDATE, + $to, + $storeId, + ($qty*$avgPickTime), + 'qty' ); } } - - $this->updateAvgOrderValues($order->getStoreId()); } /** @@ -306,6 +465,10 @@ public function updateAvgOrderValues($storeId) public function initOrderData() { + if (!$this->_helper->isEnabled()) { + return; + } + $collection = Mage::getResourceModel('sales/order_collection'); if (!is_object($collection)) { return; @@ -397,6 +560,46 @@ public function initOrderData() } } + /** + * Generate output event + * + * @param Varien_Event_Observer $observer + */ + public function generate(Varien_Event_Observer $observer) + { + $event = $observer->getEvent(); + + /** @var CoScale_Monitor_Helper_Data $logger */ + $logger = $event->getLogger(); + /** @var CoScale_Monitor_Model_Output_Generator $output */ + $output = $event->getOutput(); + + // Get Abandonned Carts + try { + $logger->debugStart('AbandonnedCarts'); + + $carts = $this->getAbandonnedCarts(); + foreach ($carts as $data) { + $output->addMetric($data); + } + $logger->debugEnd('AbandonnedCarts'); + } catch (Exception $ex) { + $logger->debugEndError('AbandonnedCarts', $ex); + } + + // Get Email Queue Size + try { + $logger->debugStart('Email Queue Size'); + + foreach ($this->getEmailQueueSize() as $data) { + $output->addMetric($data); + } + $logger->debugEnd('Email Queue Size'); + } catch (Exception $ex) { + $logger->debugEndError('Email Queue Size', $ex); + } + } + /** * Get Abandonned cart amounts * @return array diff --git a/app/code/community/CoScale/Monitor/Model/Metric/Product.php b/app/code/community/CoScale/Monitor/Model/Metric/Product.php index 6444fe4..414f2d9 100644 --- a/app/code/community/CoScale/Monitor/Model/Metric/Product.php +++ b/app/code/community/CoScale/Monitor/Model/Metric/Product.php @@ -58,6 +58,10 @@ public function _contruct() */ public function addNewProduct(Varien_Event_Observer $observer) { + if (!$this->_helper->isEnabled()) { + return; + } + /** @var Mage_Catalog_Model_Product $product */ $product = $observer->getEvent()->getProduct(); @@ -87,6 +91,10 @@ public function addNewProduct(Varien_Event_Observer $observer) */ public function removeProduct(Varien_Event_Observer $observer) { + if (!$this->_helper->isEnabled()) { + return; + } + $this->setMetric( self::ACTION_INCREMENT, self::KEY_PRODUCT_TOTAL, @@ -102,6 +110,10 @@ public function removeProduct(Varien_Event_Observer $observer) */ public function addNewCategory(Varien_Event_Observer $observer) { + if (!$this->_helper->isEnabled()) { + return; + } + /** @var Mage_Catalog_Model_Category $category */ $category = $observer->getEvent()->getCategory(); @@ -131,6 +143,10 @@ public function addNewCategory(Varien_Event_Observer $observer) */ public function removeCategory(Varien_Event_Observer $observer) { + if (!$this->_helper->isEnabled()) { + return; + } + $this->setMetric( self::ACTION_INCREMENT, self::KEY_CATEGORIES_TOTAL, @@ -144,6 +160,9 @@ public function removeCategory(Varien_Event_Observer $observer) */ public function dailyCron() { + if (!$this->_helper->isEnabled()) { + return; + } $this->resetDayCounter(); $this->updateTotalCount(); } diff --git a/app/code/community/CoScale/Monitor/Model/Metric/Rewrite.php b/app/code/community/CoScale/Monitor/Model/Metric/Rewrite.php index fc3e098..69dd1a9 100644 --- a/app/code/community/CoScale/Monitor/Model/Metric/Rewrite.php +++ b/app/code/community/CoScale/Monitor/Model/Metric/Rewrite.php @@ -10,6 +10,29 @@ class CoScale_Monitor_Model_Metric_Rewrite extends CoScale_Monitor_Model_Metric_Abstract { + public function generate(Varien_Event_Observer $observer) + { + $event = $observer->getEvent(); + + /** @var CoScale_Monitor_Helper_Data $logger */ + $logger = $event->getLogger(); + /** @var CoScale_Monitor_Model_Output_Generator $output */ + $output = $event->getOutput(); + + // Get URL Rewrites + try { + $logger->debugStart('Rewrites'); + + foreach ($this->getUrlRewrites() as $data) { + $output->addMetric($data); + } + + $logger->debugEnd('Rewrites'); + } catch (Exception $ex) { + $logger->debugEndError('Rewrites', $ex); + } + } + /** * Get amount of rewrites * @return array @@ -17,9 +40,8 @@ class CoScale_Monitor_Model_Metric_Rewrite extends CoScale_Monitor_Model_Metric_ public function getUrlRewrites() { $collection = Mage::getResourceModel('core/url_rewrite_collection'); - if(!is_object($collection)) - { - return array(); + if (!is_object($collection)) { + return array(); } $collection->getSelect() ->reset('columns') diff --git a/app/code/community/CoScale/Monitor/Model/Metric/System.php b/app/code/community/CoScale/Monitor/Model/Metric/System.php new file mode 100644 index 0000000..98b2b33 --- /dev/null +++ b/app/code/community/CoScale/Monitor/Model/Metric/System.php @@ -0,0 +1,39 @@ +getEvent(); + + /** @var CoScale_Monitor_Helper_Data $logger */ + $logger = $event->getLogger(); + /** @var CoScale_Monitor_Model_Output_Generator $output */ + $output = $event->getOutput(); + + // Get Installed Modules + try { + $logger->debugStart('Installed Modules'); + + $output->addCustom('modules', array('name' => 'core', 'version' => (string)Mage::getVersion())); + foreach (Mage::getConfig()->getNode('modules')->children() as $module) { + $output->addCustom('modules', array('name' => $module->getName(), 'version' => (string)$module->version)); + } + $logger->debugEnd('Installed Modules'); + } catch (Exception $ex) { + $logger->debugEndError('Installed Modules', $ex); + } + + // Get Stores collection + try { + $logger->debugStart('Stores'); + + foreach (Mage::app()->getStores() as $store) { + $output->addCustom('stores', array('name' => $store->getName(), 'id' => (int)$store->getId())); + } + $logger->debugEnd('Stores'); + } catch (Exception $ex) { + $logger->debugEndError('Stores', $ex); + } + } +} \ No newline at end of file diff --git a/app/code/community/CoScale/Monitor/Model/Output/Generator.php b/app/code/community/CoScale/Monitor/Model/Output/Generator.php new file mode 100644 index 0000000..b87fbaf --- /dev/null +++ b/app/code/community/CoScale/Monitor/Model/Output/Generator.php @@ -0,0 +1,38 @@ + $this, 'logger'=>$logger)); + } + + public function addMetric($data) + { + return $this->addArray('metrics', $data); + } + + public function addEvent($data) + { + return $this->addArray('events', $data); + } + + public function addCustom($name, $data) + { + return $this->addArray($name, $data); + } + + protected function addArray($name, $data) + { + $origData = $this->getData($name); + $origData[] = $data; + $this->setData($name, $origData); + return $this; + } + + public function getJsonOutput() + { + return $this->toJson(); + } +} diff --git a/app/code/community/CoScale/Monitor/etc/config.xml b/app/code/community/CoScale/Monitor/etc/config.xml index 66c0632..a5c8403 100644 --- a/app/code/community/CoScale/Monitor/etc/config.xml +++ b/app/code/community/CoScale/Monitor/etc/config.xml @@ -2,7 +2,7 @@ - 0.10.0 + 0.11.0 @@ -41,6 +41,34 @@ + + + + coscale_monitor/metric_observer + generate + + + coscale_monitor/event_observer + generate + + + coscale_monitor/metric_order + generate + + + coscale_monitor/metric_file + generate + + + coscale_monitor/metric_rewrite + generate + + + coscale_monitor/metric_system + generate + + + @@ -93,10 +121,18 @@ coscale_monitor/metric_order - addNew + salesOrderPlaceAfter + + + + coscale_monitor/metric_order + salesOrderSaveCommitAfter + + + @@ -207,6 +243,14 @@ 1000 + + + 0 + processing + processing_pickpack + processing_pickpack_done + + diff --git a/app/code/community/CoScale/Monitor/etc/system.xml b/app/code/community/CoScale/Monitor/etc/system.xml new file mode 100644 index 0000000..fb029d1 --- /dev/null +++ b/app/code/community/CoScale/Monitor/etc/system.xml @@ -0,0 +1,57 @@ + + + + + + + + 200 + 1 + 0 + 0 + + + + select + adminhtml/system_config_source_yesno + 1 + 1 + 0 + 0 + + + + select + adminhtml/system_config_source_order_status + 1 + 10 + 1 + 0 + 0 + + + + select + adminhtml/system_config_source_order_status + 1 + 11 + 1 + 0 + 0 + + + + select + adminhtml/system_config_source_order_status + 1 + 12 + 1 + 0 + 0 + + + + + + + \ No newline at end of file diff --git a/shell/coscale.php b/shell/coscale.php index d977731..aa7bf8f 100644 --- a/shell/coscale.php +++ b/shell/coscale.php @@ -19,176 +19,24 @@ class CoScale_Shell extends Mage_Shell_Abstract */ public function run() { + $helper = Mage::helper('coscale_monitor'); - $output['metrics'] = array(); - - if ($this->getArg('debug')) { - $this->debug = true; - } - - // Metric collection - $this->writeDebug('Start Metric Collection'); - $startTime = microtime(true); - - $metricOrderDelete = Mage::getSingleton('coscale_monitor/metric_order'); - $collection = Mage::getModel('coscale_monitor/metric')->getCollection(); - /** @var CoScale_Monitor_Model_Metric $metric */ - foreach ($collection as $metric) { - $output['metrics'][] = array( - 'name' => $metric->getName(), - 'unit' => $metric->getUnit(), - 'value' => (float)$metric->getValue(), - 'store_id' => (int)$metric->getStoreId(), - 'type' => $metric->getTypeText()); - - // Check if metric need to be reset after collection - if ($metricOrderDelete->resetOnCollect($metric->getKey())) { - $metric->delete(); - } - } - $this->writeDebug('Completed in '.(microtime(true)-$startTime).' seconds'); - - - // Abandonned carts - $this->writeDebug('Start Abandonned carts'); - $startTime = microtime(true); - - $carts = Mage::getSingleton('coscale_monitor/metric_order')->getAbandonnedCarts(); - foreach ($carts as $data) { - $output['metrics'][] =$data; - } - $this->writeDebug('Completed in '.(microtime(true)-$startTime).' seconds'); - - // Amount of files in var/log - $this->writeDebug('Start Error Reports'); - $startTime = microtime(true); - - $output['metrics'][] = Mage::getSingleton('coscale_monitor/metric_file')->getErrorReports(); - $this->writeDebug('Completed in '.(microtime(true)-$startTime).' seconds'); - - // Log file details - $this->writeDebug('Start Log Files'); - $startTime = microtime(true); - - $logFiles = Mage::getSingleton('coscale_monitor/metric_file')->getLogFiles(); - foreach ($logFiles as $data) { - $output['metrics'][] = $data; - } - $this->writeDebug('Completed in '.(microtime(true)-$startTime).' seconds'); - - // URL Rewrite details - $this->writeDebug('Start URL Rewrites'); - $startTime = microtime(true); - - $urlRewrites = Mage::getSingleton('coscale_monitor/metric_rewrite')->getUrlRewrites(); - foreach ($urlRewrites as $data) { - $output['metrics'][] = $data; - } - $this->writeDebug('Completed in '.(microtime(true)-$startTime).' seconds'); - - // Email queue size - $this->writeDebug('Start Email Queue Site'); - $startTime = microtime(true); - - $emailQueueSize = Mage::getSingleton('coscale_monitor/metric_order')->getEmailQueueSize(); - foreach ($emailQueueSize as $data) { - $output['metrics'][] = $data; + if (!$helper->isEnabled()) { + echo json_encode(array('error'=>'CoScale Module not active!')); + return; } - $this->writeDebug('Completed in '.(microtime(true)-$startTime).' seconds'); - $output['events'] = array(); - - // Maintenance Flag - $this->writeDebug('Start Maintenance Flag'); - $startTime = microtime(true); - - - if (file_exists(Mage::getBaseDir('base') . DS . 'maintenance.flag')) { - $output['events'][] = array( - 'type' => CoScale_Monitor_Model_Event::GROUP_ADMIN, - 'message' => 'Maintenance mode enabled', - 'start_time' => 0, - 'stop_time' => 0, - ); - } - $this->writeDebug('Completed in '.(microtime(true)-$startTime).' seconds'); - - // Event collection - $this->writeDebug('Start Event Collection'); - $startTime = microtime(true); - - $collection = Mage::getModel('coscale_monitor/event')->getCollection(); - /** @var CoScale_Monitor_Model_Event $event */ - foreach ($collection as $event) { - $output['events'][] = array( - 'type' => $event->getTypeGroup(), - 'message' => $event->getDescription(), - 'data' => array_merge(array('originator'=>$event->getSource()), unserialize($event->getEventData())), - 'start_time' => (int)(time() - $event->getTimestampStart()), - 'stop_time' => (int)($event->getTimestampEnd() != 0 ? (time() - $event->getTimestampEnd()) : 0), - ); - $event->delete(); - if ($event->getState() != $event::STATE_ENABLED) { - //$event->delete(); - } - } - $this->writeDebug('Completed in '.(microtime(true)-$startTime).' seconds'); - - // Cronjobs - $this->writeDebug('Start Cronjobs Collection'); - $startTime = microtime(true); - - $endDateTime = date('U'); - $collection = Mage::getModel('cron/schedule')->getCollection() - ->addFieldToFilter('finished_at', array('from' => date('Y-m-d H:i:s', $endDateTime-65))) - ->setOrder('finished_at', 'DESC'); - /** @var Mage_Cron_Model_Schedule $event */ - foreach ($collection as $cron) { - $output['events'][] = array( - 'type' => CoScale_Monitor_Model_Event::GROUP_CRON, - 'message' => $cron->getJobCode(), - 'status' => $cron->getStatus(), - 'start_time' => (int)(time() - strtotime($cron->getExecutedAt())), - 'stop_time' => (int)(time() - strtotime($cron->getFinishedAt())), - ); - } - $this->writeDebug('Completed in '.(microtime(true)-$startTime).' seconds'); - - // Modules - $this->writeDebug('Start Modules Collection'); - $startTime = microtime(true); - - $output['modules'] = array(); - $output['modules'][] = array('name' => 'core', 'version' => (string)Mage::getVersion()); - foreach (Mage::getConfig()->getNode('modules')->children() as $module) { - $output['modules'][] = array('name' => $module->getName(), 'version' => (string)$module->version); - } - $this->writeDebug('Completed in '.(microtime(true)-$startTime).' seconds'); - - // Stores - $this->writeDebug('Start Stores Collection'); - $startTime = microtime(true); - - $output['stores'] = array(); - foreach (Mage::app()->getStores() as $store) { - $output['stores'][] = array('name' => $store->getName(), 'id' => (int)$store->getId()); + if ($this->getArg('debug')) { + $helper->enableDebug(); } - $this->writeDebug('Completed in '.(microtime(true)-$startTime).' seconds'); - // Write JSON output - $this->writeDebug('Start Write JSON Output'); - $startTime = microtime(true); - - echo Zend_Json::encode($output); - $this->writeDebug('Completed in '.(microtime(true)-$startTime).' seconds'); - - $this->writeDebug('Run completed!'); - } - - public function writeDebug($msg) - { - if ($this->debug) { - Mage::log($msg, Zend_Log::DEBUG, 'coscale-collect.log', true); + // Generate output + try { + $helper->debugStart('Output Generation'); + echo Mage::getSingleton('coscale_monitor/output_generator')->getJsonOutput(); + $helper->debugEnd('Output Generation'); + } catch (Exception $ex) { + $helper->debugEndError('Output Generation', $ex); } } } From 2c5503579231fb246cac33c2f2d46478a30d27d5 Mon Sep 17 00:00:00 2001 From: Vladimir Kerkhoff Date: Fri, 29 Jan 2016 16:09:39 +0100 Subject: [PATCH 5/6] Version: 0.11.0: Added check on database tables Added option to enable/disable module thru system configuration (system=>coscale monitoring) Changed generation (coscale.php) to Model/Output/Generator.php Added event dispatch to build generated output Added extra checks on saving order data to avoid errors during checkout Added statistics for orders processing, picking and calculated values for pick duration Changed debug logging for coscale.php and rounding time to 5 decimals Added extra exception checks on generation of output --- app/code/community/CoScale/Monitor/Model/Metric/Abstract.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/code/community/CoScale/Monitor/Model/Metric/Abstract.php b/app/code/community/CoScale/Monitor/Model/Metric/Abstract.php index 1fb33d7..621db67 100644 --- a/app/code/community/CoScale/Monitor/Model/Metric/Abstract.php +++ b/app/code/community/CoScale/Monitor/Model/Metric/Abstract.php @@ -10,8 +10,8 @@ class CoScale_Monitor_Model_Metric_Abstract { protected $_metric = false; - protected $_helper = false; protected $_metricData = array(); + protected $_helper = false; protected $_metricType = CoScale_Monitor_Model_Metric::TYPE_APPLICATION; @@ -105,4 +105,5 @@ protected function getMetricData($key, $store) return $data; } + } \ No newline at end of file From 899786a044a9ba6ea75de22e7bc1d2eb7339fd5c Mon Sep 17 00:00:00 2001 From: Mihai Oprea Date: Fri, 5 Feb 2016 16:42:04 +0200 Subject: [PATCH 6/6] Make magento module enabled by default. --- app/code/community/CoScale/Monitor/Model/Metric/Order.php | 2 +- app/code/community/CoScale/Monitor/etc/config.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/code/community/CoScale/Monitor/Model/Metric/Order.php b/app/code/community/CoScale/Monitor/Model/Metric/Order.php index 3dd21a3..4f1f465 100644 --- a/app/code/community/CoScale/Monitor/Model/Metric/Order.php +++ b/app/code/community/CoScale/Monitor/Model/Metric/Order.php @@ -184,7 +184,7 @@ public function _contruct() ); $this->_metricData[self::KEY_TIME_CURRENT_PICKPACK] = array( - 'name' => 'Time current pick/pack ', + 'name' => 'Time current pick/pack', 'description' => 'The total time needed to pick/pack current orders in pick/pack state', 'unit' => 'seconds' ); diff --git a/app/code/community/CoScale/Monitor/etc/config.xml b/app/code/community/CoScale/Monitor/etc/config.xml index 8fc3a4f..2ed84c0 100644 --- a/app/code/community/CoScale/Monitor/etc/config.xml +++ b/app/code/community/CoScale/Monitor/etc/config.xml @@ -254,7 +254,7 @@ - 0 + 1 processing processing_pickpack processing_pickpack_done