diff --git a/CommonLogic/Monitor.php b/CommonLogic/Monitor.php index da595afe3..d7bdee78e 100644 --- a/CommonLogic/Monitor.php +++ b/CommonLogic/Monitor.php @@ -2,8 +2,12 @@ namespace exface\Core\CommonLogic; use exface\Core\CommonLogic\Debugger\Profiler; +use exface\Core\DataTypes\PhpClassDataType; use exface\Core\Events\Action\OnBeforeActionPerformedEvent; use exface\Core\Events\Action\OnActionPerformedEvent; +use exface\Core\Exceptions\Actions\ActionRuntimeError; +use exface\Core\Facades\AbstractAjaxFacade\AbstractAjaxFacade; +use exface\Core\Interfaces\Tasks\HttpTaskInterface; use exface\Core\Interfaces\WorkbenchInterface; use exface\Core\Interfaces\Actions\ActionInterface; use exface\Core\Interfaces\Actions\iReadData; @@ -40,6 +44,12 @@ class Monitor extends Profiler private $actionsEnabled = false; + private $longRunningActionsLogged = false; + + private $longRunningActionsThreshold = 10; + + private $longRunningActionsLevel = 'CRITICAL'; + private $errorsEnabled = false; /** @@ -51,18 +61,24 @@ public function __construct(WorkbenchInterface $workbench, float $startTimeMs = { parent::__construct($workbench, $startTimeMs); } - + /** - * + * * @param WorkbenchInterface $workbench + * @param float|null $startTimeMs */ - public static function register(WorkbenchInterface $workbench, float $startTimeMs = null) + public static function register(WorkbenchInterface $workbench, float $startTimeMs = null) : void { $self = new self($workbench, $startTimeMs); $config = $workbench->getConfig(); + $self->actionsEnabled = $config->getOption('MONITOR.ACTIONS.ENABLED'); $self->errorsEnabled = $config->getOption('MONITOR.ERRORS.ENABLED'); + $self->longRunningActionsLogged = $config->getOption('DEBUG.LOG_LONG_RUNNING_READS'); + $self->longRunningActionsThreshold = $config->getOption('DEBUG.LOG_LONG_RUNNING_READS_THRESHOLD'); + $self->longRunningActionsLevel = $config->getOption('DEBUG.LOG_LONG_RUNNING_READS_LEVEL'); + // Do not monitor anything while installing the workbench if ($workbench->isInstalled() === false) { return; @@ -157,7 +173,7 @@ public static function onCleanUp(OnCleanUpEvent $event) * @param OnBeforeActionPerformedEvent $event * @return void */ - public function onActionStart(OnBeforeActionPerformedEvent $event) + public function onActionStart(OnBeforeActionPerformedEvent $event) : void { if (! $this->isActionMonitored($event->getAction())) { return; @@ -171,18 +187,33 @@ public function onActionStart(OnBeforeActionPerformedEvent $event) * @param OnActionPerformedEvent $event * @return void */ - public function onActionStop(ActionEventInterface $event) + public function onActionStop(ActionEventInterface $event) : void { - if (! $this->isActionMonitored($event->getAction())) { + $action = $event->getAction(); + + if (! $this->isActionMonitored($action)) { return; } $ms = null; if ($this->actionsEnabled) { - $ms = $this->stop($event->getAction())->getTimeTotalMs(); - } - $this->addRowFromAction($event->getAction(), $event->getTask(), $ms); - return; + $ms = $this->stop($action)->getTimeTotalMs(); + $s = $ms / 1000; + + if($s > $this->longRunningActionsThreshold) { + $this->getWorkbench()->getLogger()->logException(new ActionRuntimeError( + $action, + 'Action "' . $action->getName() . '" ran for ' . $s . 's!', + $this->longRunningActionsLevel + )); + } + } + + if($action instanceof iReadData) { + return; + } + + $this->addRowFromAction($action, $event->getTask(), $ms); } /** @@ -224,7 +255,8 @@ public function addLogIdToLastRowObject(string $ids) : void protected function isActionMonitored(ActionInterface $action) : bool { switch (true) { - case $action instanceof iReadData: + // Ignore ReadData, unless we are logging long-running actions. + case $action instanceof iReadData && !$this->longRunningActionsLogged: case $action instanceof UxonAutosuggest: case $action instanceof ContextBarApi: case $action instanceof ShowContextPopup: @@ -233,12 +265,12 @@ protected function isActionMonitored(ActionInterface $action) : bool return true; } } - + /** - * + * * @param ActionInterface $action - * @param TaskInterface $task - * @param float $duration + * @param TaskInterface $task + * @param float|null $duration * @return Monitor */ protected function addRowFromAction(ActionInterface $action, TaskInterface $task, float $duration = null) : Monitor @@ -323,8 +355,12 @@ protected function saveData() : Monitor 'USER' => $this->getWorkbench()->getSecurity()->getAuthenticatedUser()->getUid(), 'TIME' => $item['time'], 'DATE' => DateDataType::cast($item['time']), - 'DURATION' => $this->getTimeTotalMs() + 'DURATION' => $this->getTimeTotalMs(), + 'TASK_CLASS' => PhpClassDataType::findClassNameWithoutNamespace($task), + 'REQUEST_SIZE' => $task instanceof HttpTaskInterface ? $task->getHttpRequest()->getHeader('Content-Length')[0] : null, + 'UI_FLAG' => $task->getFacade() instanceof AbstractAjaxFacade ]); + $ds->dataCreate(); $logIds = $item['logIds']; diff --git a/Config/System.config.json b/Config/System.config.json index 23151a976..ca2c5d516 100644 --- a/Config/System.config.json +++ b/Config/System.config.json @@ -44,22 +44,25 @@ "DEBUG.SQL_FORMATTING_MAX_CHARS": 20000, "DEBUG.AUTOMATIC_UXON_VALIDATION": true, "DEBUG.MAINTENANCE_MODE": false, - - "LOG.MINIMUM_LEVEL_TO_LOG": "debug", - "LOG.MAX_DAYS_TO_KEEP": "14", - "LOG.PERSIST_LOG_LEVEL": "critical", - "LOG.PASSTHROUGH_LOG_LEVEL": "error", + "DEBUG.LOG_LONG_RUNNING_READS": false, + "DEBUG.LOG_LONG_RUNNING_READS_THRESHOLD": 10, + "DEBUG.LOG_LONG_RUNNING_READS_LEVEL": "CRITICAL", + + "LOG.MINIMUM_LEVEL_TO_LOG": "debug", + "LOG.MAX_DAYS_TO_KEEP": "14", + "LOG.PERSIST_LOG_LEVEL": "critical", + "LOG.PASSTHROUGH_LOG_LEVEL": "error", "LOGIN.PROMPT.MESSAGES": [], - - "MONITOR.ENABLED": true, - "MONITOR.ACTIONS.ENABLED": false, - "MONITOR.ACTIONS.DAYS_TO_KEEP": 30, - "MONITOR.ERRORS.ENABLED": true, - "MONITOR.ERRORS.MINIMUM_LEVEL_TO_LOG": "critical", - "MONITOR.ERRORS.DAYS_TO_KEEP": 30, - - "FOLDERS.USERDATA_PATH_ABSOLUTE": "", + + "MONITOR.ENABLED": true, + "MONITOR.ACTIONS.ENABLED": true, + "MONITOR.ACTIONS.DAYS_TO_KEEP": 30, + "MONITOR.ERRORS.ENABLED": true, + "MONITOR.ERRORS.MINIMUM_LEVEL_TO_LOG": "critical", + "MONITOR.ERRORS.DAYS_TO_KEEP": 30, + + "FOLDERS.USERDATA_PATH_ABSOLUTE": "", "FOLDERS.CACHE_PATH_ABSOLUTE": "", "FOLDERS.BACKUP_PATH_ABSOLUTE": "", "FOLDERS.LOGS_PATH_ABSOLUTE": "", diff --git a/Model/99_PAGE/exface.core.monitor.json b/Model/99_PAGE/exface.core.monitor.json index d6177c5fb..17c59ecaf 100644 --- a/Model/99_PAGE/exface.core.monitor.json +++ b/Model/99_PAGE/exface.core.monitor.json @@ -10,8 +10,8 @@ "replaces_page_selector": null, "created_by_user_selector": "0x31000000000000000000000000000000", "created_on": "2020-11-17 13:30:20", - "modified_by_user_selector": "0x11edb424effd2980b424025041000001", - "modified_on": "2024-03-12 09:53:45", + "modified_by_user_selector": "0x11e8fe1c902c8ebea23ee4b318306b9a", + "modified_on": "2026-02-06 11:46:25", "icon": "tachometer", "contents": { "widget_type": "SplitVertical", @@ -45,6 +45,12 @@ "value": 0, "hidden": true }, + { + "attribute_alias": "MONITOR_ACTION__UI_FLAG", + "value": 0, + "comparator": "!==", + "apply_to_aggregates": true + }, { "/*": "", "hidden": true, @@ -401,6 +407,12 @@ "apply_on_change": true, "hidden": true }, + { + "attribute_alias": "UI_FLAG", + "comparator": "!==", + "value": 0, + "hidden": true + }, { "attribute_alias": "ACTION_NAME" }, diff --git a/Model/exface.Core.MONITOR_ACTION/04_ATTRIBUTE.json b/Model/exface.Core.MONITOR_ACTION/04_ATTRIBUTE.json index 9213c0367..5bbaa2328 100644 --- a/Model/exface.Core.MONITOR_ACTION/04_ATTRIBUTE.json +++ b/Model/exface.Core.MONITOR_ACTION/04_ATTRIBUTE.json @@ -681,6 +681,158 @@ "DEFAULT_VALUE": "", "FIXED_VALUE": "", "CUSTOM_DATA_TYPE": null + }, + { + "_EXPORT_SUMMARY": "Task Class [TASK_CLASS]", + "CREATED_ON": "2026-01-29 10:53:24", + "MODIFIED_ON": "2026-01-29 10:55:05", + "CREATED_BY_USER": "0x11e8fe1c902c8ebea23ee4b318306b9a", + "MODIFIED_BY_USER": "0x11e8fe1c902c8ebea23ee4b318306b9a", + "UID": "0x11f09f045a3808dc9f047722c9cadff2", + "SORTABLEFLAG": 1, + "FILTERABLEFLAG": 1, + "AGGREGATABLEFLAG": 1, + "DEFAULT_AGGREGATE_FUNCTION": "", + "VALUE_LIST_DELIMITER": ",", + "READABLEFLAG": 1, + "WRITABLEFLAG": 1, + "SYSTEMFLAG": 0, + "DEFAULT_EDITOR_UXON": null, + "COPY_WITH_RELATED_OBJECT": 0, + "DELETE_WITH_RELATED_OBJECT": 0, + "DEFAULT_DISPLAY_UXON": null, + "COMMENTS": "", + "NAME": "Task Class", + "RELATION_CARDINALITY": "", + "TYPE": "D", + "COPYABLEFLAG": 1, + "ICON": "", + "ICON_SET": "", + "ABBREVIATION": "", + "ALIAS": "TASK_CLASS", + "DATATYPE": "0x30000000000000000000000000000000", + "DATA_ADDRESS": "task_class", + "OBJECT": "0x11eb830a07a3fc70830a025041000001", + "FORMATTER": "", + "DISPLAYORDER": null, + "LABELFLAG": 0, + "UIDFLAG": 0, + "HIDDENFLAG": 0, + "EDITABLEFLAG": 1, + "REQUIREDFLAG": 0, + "RELATED_OBJ": null, + "SORTERPOS": null, + "SORTERDIR": "", + "RELATED_OBJ_ATTR": null, + "SHORT_DESCRIPTION": "", + "DATA_ADDRESS_PROPS": null, + "DEFAULT_VALUE": "", + "FIXED_VALUE": "", + "CUSTOM_DATA_TYPE": { + "length_max": 100 + } + }, + { + "_EXPORT_SUMMARY": "Request Size [REQUEST_SIZE]", + "CREATED_ON": "2026-01-29 10:53:24", + "MODIFIED_ON": "2026-01-29 10:55:19", + "CREATED_BY_USER": "0x11e8fe1c902c8ebea23ee4b318306b9a", + "MODIFIED_BY_USER": "0x11e8fe1c902c8ebea23ee4b318306b9a", + "UID": "0x11f0a1ea5a38373aa1ea8d10d37f0bc3", + "SORTABLEFLAG": 1, + "FILTERABLEFLAG": 1, + "AGGREGATABLEFLAG": 1, + "DEFAULT_AGGREGATE_FUNCTION": "", + "VALUE_LIST_DELIMITER": ",", + "READABLEFLAG": 1, + "WRITABLEFLAG": 1, + "SYSTEMFLAG": 0, + "DEFAULT_EDITOR_UXON": null, + "COPY_WITH_RELATED_OBJECT": 0, + "DELETE_WITH_RELATED_OBJECT": 0, + "DEFAULT_DISPLAY_UXON": null, + "COMMENTS": "", + "NAME": "Request Size", + "RELATION_CARDINALITY": "", + "TYPE": "D", + "COPYABLEFLAG": 1, + "ICON": "", + "ICON_SET": "", + "ABBREVIATION": "", + "ALIAS": "REQUEST_SIZE", + "DATATYPE": "0x11e7b0277435425e98350205857feb80", + "DATA_ADDRESS": "request_size", + "OBJECT": "0x11eb830a07a3fc70830a025041000001", + "FORMATTER": "", + "DISPLAYORDER": null, + "LABELFLAG": 0, + "UIDFLAG": 0, + "HIDDENFLAG": 0, + "EDITABLEFLAG": 1, + "REQUIREDFLAG": 0, + "RELATED_OBJ": null, + "SORTERPOS": null, + "SORTERDIR": "", + "RELATED_OBJ_ATTR": null, + "SHORT_DESCRIPTION": "", + "DATA_ADDRESS_PROPS": null, + "DEFAULT_VALUE": "", + "FIXED_VALUE": "", + "CUSTOM_DATA_TYPE": null + }, + { + "_EXPORT_SUMMARY": "UI Flag [UI_FLAG]", + "CREATED_ON": "2026-01-29 10:53:24", + "MODIFIED_ON": "2026-02-05 15:19:04", + "CREATED_BY_USER": "0x11e8fe1c902c8ebea23ee4b318306b9a", + "MODIFIED_BY_USER": "0x11e8fe1c902c8ebea23ee4b318306b9a", + "UID": "0x11f0af1d5a382380af1d635c9e1d6249", + "SORTABLEFLAG": 1, + "FILTERABLEFLAG": 1, + "AGGREGATABLEFLAG": 1, + "DEFAULT_AGGREGATE_FUNCTION": "", + "VALUE_LIST_DELIMITER": ",", + "READABLEFLAG": 1, + "WRITABLEFLAG": 1, + "SYSTEMFLAG": 0, + "DEFAULT_EDITOR_UXON": null, + "COPY_WITH_RELATED_OBJECT": 0, + "DELETE_WITH_RELATED_OBJECT": 0, + "DEFAULT_DISPLAY_UXON": null, + "COMMENTS": "", + "NAME": "UI Flag", + "RELATION_CARDINALITY": "", + "TYPE": "D", + "COPYABLEFLAG": 1, + "ICON": "", + "ICON_SET": "", + "ABBREVIATION": "", + "ALIAS": "UI_FLAG", + "DATATYPE": "0x11e7b0277435425e98350205857feb80", + "DATA_ADDRESS": [ + "// Multiline text delimited by `\n`", + "(CASE", + " WHEN ui_flag IS NULL THEN -1", + " ELSE ui_flag", + "END)" + ], + "OBJECT": "0x11eb830a07a3fc70830a025041000001", + "FORMATTER": "", + "DISPLAYORDER": null, + "LABELFLAG": 0, + "UIDFLAG": 0, + "HIDDENFLAG": 0, + "EDITABLEFLAG": 1, + "REQUIREDFLAG": 0, + "RELATED_OBJ": null, + "SORTERPOS": null, + "SORTERDIR": "", + "RELATED_OBJ_ATTR": null, + "SHORT_DESCRIPTION": "", + "DATA_ADDRESS_PROPS": null, + "DEFAULT_VALUE": "", + "FIXED_VALUE": "", + "CUSTOM_DATA_TYPE": null } ], "totals_rows": [],