N°3985 - Performance checks on the back end - Enhance KPIs

This commit is contained in:
Eric Espie
2021-09-09 17:21:50 +02:00
parent 27c397cc1a
commit 15e99a898e
22 changed files with 119 additions and 78 deletions

View File

@@ -24,7 +24,6 @@ use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Input\Select\SelectOptionUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Input\SelectUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\MedallionIcon\MedallionIcon;
use Combodo\iTop\Application\UI\Base\Component\Panel\Panel;
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Title\Title;
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
@@ -3318,12 +3317,13 @@ HTML
}
// Note: Remove the table if we want fields to occupy the whole width of the container
$oForm->AddHtml('<table><tr><td>');
$oForm->AddHtml($oPage->GetDetails($aDetails));
$oForm->AddHtml('</td></tr></table>');
$sHtml = '<table><tr><td>';
$sHtml .= $oPage->GetDetails($aDetails);
$sHtml .= '</td></tr></table>';
$oAppContext = new ApplicationContext();
$oForm->AddHtml($oAppContext->GetForForm());
$sHtml .= $oAppContext->GetForForm();
$oForm->AddHtml($sHtml);
$oCancelButton = ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Cancel'), 'cancel', 'cancel');
$oCancelButton->SetOnClickJsCode("BackToDetails('{$sClass}', '{$this->GetKey()}', '', '{$sOwnershipToken}');");

View File

@@ -604,8 +604,7 @@ class DashletUnknown extends Dashlet
$oDashletContainer = new DashletContainer(null, ['dashlet-content']);
$oDashletContainer->AddHtml('<div class="dashlet-ukn-image"><img src="'.$sIconUrl.'" /></div>');
$oDashletContainer->AddHtml('<div class="dashlet-ukn-text">'.$sExplainText.'</div>');
$oDashletContainer->AddHtml('<div class="dashlet-ukn-image"><img src="'.$sIconUrl.'" /></div><div class="dashlet-ukn-text">'.$sExplainText.'</div>');
return $oDashletContainer;
}
@@ -624,8 +623,7 @@ class DashletUnknown extends Dashlet
$oDashletContainer = new DashletContainer(null, ['dashlet-content']);
$oDashletContainer->AddHtml('<div class="dashlet-ukn-image"><img src="'.$sIconUrl.'" /></div>');
$oDashletContainer->AddHtml('<div class="dashlet-ukn-text">'.$sExplainText.'</div>');
$oDashletContainer->AddHtml('<div class="dashlet-ukn-image"><img src="'.$sIconUrl.'" /></div><div class="dashlet-ukn-text">'.$sExplainText.'</div>');
return $oDashletContainer;
}

View File

@@ -1042,8 +1042,7 @@ JS
$sCountLabel = $aCount['label'];
$oPill = PillFactory::MakeForState($sClass, $sStateValue)
->SetTooltip($sStateLabel)
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--count\">$sCountLabel</span>")
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--label ibo-text-truncated-with-ellipsis\">$sStateLabel</span>");
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--count\">$sCountLabel</span><span class=\"ibo-dashlet-header-dynamic--label ibo-text-truncated-with-ellipsis\">$sStateLabel</span>");
if ($sHyperlink != '-') {
$oPill->SetUrl($sHyperlink);
}

View File

@@ -58,6 +58,10 @@ class ApplicationMenu
* @var array
*/
static $aMenusIndex = array();
/**
* @var array
*/
static $aMenusById = [];
/**
* @var string
*/
@@ -166,6 +170,7 @@ class ApplicationMenu
$aBacktrace = debug_backtrace();
$sFile = isset($aBacktrace[2]["file"]) ? $aBacktrace[2]["file"] : $aBacktrace[1]["file"];
self::$aMenusIndex[$index] = array('node' => $oMenuNode, 'children' => array(), 'parent' => $sParentId, 'rank' => $fRank, 'source_file' => $sFile);
self::$aMenusById[$oMenuNode->GetMenuId()] = $index;
}
else
{
@@ -506,17 +511,11 @@ EOF
*/
public static function GetMenuIndexById($sTitle)
{
$index = -1;
/** @var MenuNode[] $aMenu */
foreach(self::$aMenusIndex as $aMenu)
{
if ($aMenu['node']->GetMenuId() == $sTitle)
{
$index = $aMenu['node']->GetIndex();
break;
}
if (isset(self::$aMenusById[$sTitle])) {
return self::$aMenusById[$sTitle];
}
return $index;
return -1;
}
/**

View File

@@ -66,9 +66,8 @@ class MyHelpers
// getmicrotime()
// format sss.mmmuuupppnnn
public static function getmicrotime()
{
list($usec, $sec) = explode(" ",microtime());
return ((float)$usec + (float)$sec);
{
return microtime(true);
}
/*

View File

@@ -31,10 +31,15 @@ class ExecutionKPI
static protected $m_bBlameCaller = false;
static protected $m_sAllowedUser = '*';
static protected $m_aStats = array(); // Recurrent operations
static protected $m_aExecData = array(); // One shot operations
static protected $m_aStats = []; // Recurrent operations
static protected $m_aExecData = []; // One shot operations
/**
* @var array[ExecutionKPI]
*/
static protected $m_aExecutionStack = []; // embedded execution stats
protected $m_fStarted = null;
protected $m_fChildrenDuration = 0; // Count embedded
protected $m_iInitialMemory = null;
static public function EnableDuration($iLevel)
@@ -284,6 +289,32 @@ class ExecutionKPI
public function __construct()
{
$this->ResetCounters();
self::Push($this);
}
/**
* Stack executions to remove children duration from stats
*
* @param \ExecutionKPI $oExecutionKPI
*/
private static function Push(ExecutionKPI $oExecutionKPI)
{
array_push(self::$m_aExecutionStack, $oExecutionKPI);
}
/**
* Pop current child and count its duration in its parent
*
* @param float|int $fChildDuration
*/
private static function Pop(float $fChildDuration = 0)
{
array_pop(self::$m_aExecutionStack);
// Update the parent's children duration
$oPrevExecutionKPI = end(self::$m_aExecutionStack);
if ($oPrevExecutionKPI) {
$oPrevExecutionKPI->m_fChildrenDuration += $fChildDuration;
}
}
// Get the duration since startup, and reset the counter for the next measure
@@ -294,13 +325,12 @@ class ExecutionKPI
$aNewEntry = null;
if (self::$m_bEnabled_Duration)
{
if (self::$m_bEnabled_Duration) {
$fStopped = MyHelpers::getmicrotime();
$aNewEntry = array(
'op' => $sOperationDesc,
'op' => $sOperationDesc,
'time_begin' => $this->m_fStarted - $fItopStarted,
'time_end' => $fStopped - $fItopStarted,
'time_end' => $fStopped - $fItopStarted,
);
// Reset for the next operation (if the object is recycled)
$this->m_fStarted = $fStopped;
@@ -332,24 +362,23 @@ class ExecutionKPI
public function ComputeStats($sOperation, $sArguments)
{
if (self::$m_bEnabled_Duration)
{
$fDuration = 0;
if (self::$m_bEnabled_Duration) {
$fStopped = MyHelpers::getmicrotime();
$fDuration = $fStopped - $this->m_fStarted;
if (self::$m_bBlameCaller)
{
$fSelfDuration = $fDuration - $this->m_fChildrenDuration;
if (self::$m_bBlameCaller) {
self::$m_aStats[$sOperation][$sArguments][] = array(
'time' => $fDuration,
'time' => $fSelfDuration,
'callers' => MyHelpers::get_callstack(1),
);
}
else
{
} else {
self::$m_aStats[$sOperation][$sArguments][] = array(
'time' => $fDuration
'time' => $fSelfDuration,
);
}
}
self::Pop($fDuration);
}
protected function ResetCounters()

View File

@@ -843,12 +843,12 @@ function DisplayClassDetails($oPage, $sClass, $sContext)
$oPanel = PanelUIBlockFactory::MakeForClass($sClass, MetaModel::GetName($sClass).' ('.$sClass.')')
->SetIcon(MetaModel::GetClassIcon($sClass, false));
$sClassDescritpion = MetaModel::GetClassDescription($sClass);
$oEnchancedPanelSubtitle = $oPanel->GetSubTitleBlock();
$oEnchancedPanelSubtitle->AddHtml($sClassHierarchy.($sClassDescritpion == "" ? "" : ' - '.$sClassDescritpion));
$oEnhancedPanelSubtitle = $oPanel->GetSubTitleBlock();
$sEnhancedPanelSubtitle = $sClassHierarchy.($sClassDescritpion == "" ? "" : ' - '.$sClassDescritpion);
if (MetaModel::IsAbstract($sClass)) {
$oEnchancedPanelSubtitle->AddHtml(' - <i class="fas fa-lock" data-tooltip-content="'.Dict::S('UI:Schema:AbstractClass').'"></i>');
$sEnhancedPanelSubtitle .= ' - <i class="fas fa-lock" data-tooltip-content="'.Dict::S('UI:Schema:AbstractClass').'"></i>';
}
$oEnhancedPanelSubtitle->AddHtml($sEnhancedPanelSubtitle);
$oPage->AddUiBlock($oPanel);
$oPage->AddTabContainer('details', '', $oPanel);
$oPage->SetCurrentTabContainer('details');

View File

@@ -12,6 +12,7 @@ use Combodo\iTop\Application\UI\Base\Component\Html\Html;
use Combodo\iTop\Application\UI\Base\UIBlock;
use Combodo\iTop\Renderer\BlockRenderer;
use CoreTemplateException;
use ExecutionKPI;
use IssueLog;
use Twig\Environment;
use Twig\Error\Error;
@@ -155,7 +156,12 @@ class TwigHelper
public static function RenderTemplate(Environment $oTwig, array $aParams, string $sName, string $sTemplateFileExtension = self::DEFAULT_FILE_TYPE, bool $bLogMissingFile = true): string
{
try {
return $oTwig->render($sName.'.'.$sTemplateFileExtension.'.twig', $aParams);
$oKPI = new ExecutionKPI();
$sFileName = $sName.'.'.$sTemplateFileExtension.'.twig';
$sResult = $oTwig->render($sFileName, $aParams);
$oKPI->ComputeStats('Render TWIG', $sFileName);
return $sResult;
}
catch (Error $oTwigException) {
$oTwigPreviousException = $oTwigException->getPrevious();

View File

@@ -25,6 +25,7 @@ class FieldBadgeUIBlockFactory extends AbstractUIBlockFactory
public static function MakeForField(string $sValue, ?ormStyle $oStyle)
{
$oBadge = null;
$sHtml = '';
if ($oStyle) {
$sStyleClass = $oStyle->GetStyleClass();
$sPrimaryColor = $oStyle->GetMainColor();
@@ -34,27 +35,26 @@ class FieldBadgeUIBlockFactory extends AbstractUIBlockFactory
$oBadge = new FieldBadge(null, $aCSSClasses);
$sDecorationClasses = $oStyle->GetDecorationClasses();
if (!is_null($sDecorationClasses) && !empty($sDecorationClasses)) {
$oBadge->AddHtml("<span class=\"ibo-field-badge--decoration\"><i class=\"$sDecorationClasses\"></i></span>");
$sHtml .= "<span class=\"ibo-field-badge--decoration\"><i class=\"$sDecorationClasses\"></i></span>";
}
$oBadge->AddHtml("<span class=\"ibo-field-badge--label\">$sValue</span>");
$sHtml .= "<span class=\"ibo-field-badge--label\">$sValue</span>";
// Add custom style
// TODO 3.0 To be removed when compilation supports generated CSS
$oBadge->AddHtml(<<<HTML
$sHtml .= <<<HTML
<style>
.$sStyleClass {
color: $sComplementaryColor;
background-color: $sPrimaryColor;
}
</style>
HTML
);
HTML;
}
}
if (!$oBadge) {
$oBadge = new FieldBadge();
$oBadge->AddHtml("<span>$sValue</span>");
$sHtml .= "<span>$sValue</span>";
}
$oBadge->AddHtml($sHtml);
return $oBadge;
}
}

View File

@@ -32,6 +32,7 @@ class AjaxPage extends WebPage implements iTabbedPage
*/
function __construct($s_title)
{
$oKpi = new ExecutionKPI();
$sPrintable = utils::ReadParam('printable', '0');
$bPrintable = ($sPrintable == '1');
@@ -46,6 +47,7 @@ class AjaxPage extends WebPage implements iTabbedPage
$this->sPromiseId = utils::ReadParam('ajax_promise_id', uniqid('ajax_', true));
utils::InitArchiveMode();
$oKpi->ComputeStats(get_class($this).' creation', 'AjaxPage');
}
/**
@@ -199,15 +201,12 @@ class AjaxPage extends WebPage implements iTabbedPage
$aData['aBlockParams'] = $this->GetBlockParams();
$oKpi->ComputeAndReport(get_class($this).' prepare output');
$oKpi = new ExecutionKPI();
$oTwigEnv = TwigHelper::GetTwigEnvironment(BlockRenderer::TWIG_BASE_PATH, BlockRenderer::TWIG_ADDITIONAL_PATHS);
// Render final TWIG into global HTML
$sHtml = TwigHelper::RenderTemplate($oTwigEnv, $aData, $this->GetTemplateRelPath());
$oKpi->ComputeAndReport('TWIG rendering');
$oKpi->ComputeAndReport(get_class($this).' output');
// Echo global HTML
$oKpi = new ExecutionKPI();
echo $sHtml;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($sHtml) / 1024).' Kb)');
ExecutionKPI::ReportStats();

View File

@@ -29,11 +29,13 @@ class CSVPage extends WebPage
{
function __construct($s_title)
{
$oKpi = new ExecutionKPI();
parent::__construct($s_title);
$this->add_header("Content-type: text/plain; charset=".self::PAGES_CHARSET);
$this->no_cache();
$this->add_xframe_options();
//$this->add_header("Content-Transfer-Encoding: binary");
$oKpi->ComputeStats(get_class($this).' creation', 'CSVPage');
}
public function output()

View File

@@ -28,7 +28,9 @@ class CaptureWebPage extends WebPage
{
function __construct()
{
$oKpi = new ExecutionKPI();
parent::__construct('capture web page');
$oKpi->ComputeStats(get_class($this).' creation', 'CaptureWebPage');
}
public function GetHtml()

View File

@@ -49,8 +49,7 @@ class DownloadPage extends AjaxPage
} else {
$sContent = $this->sContent;
}
$oKpi->ComputeAndReport(get_class($this).' prepare output');
$oKpi = new ExecutionKPI();
$oKpi->ComputeAndReport(get_class($this).' output');
echo $sContent;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($sContent) / 1024).' Kb)');
ExecutionKPI::ReportStats();

View File

@@ -17,12 +17,14 @@ class ErrorPage extends NiceWebPage
{
public function __construct($sTitle)
{
$oKpi = new ExecutionKPI();
parent::__construct($sTitle);
$this->add_linked_script("../js/jquery.blockUI.js");
$this->add_linked_script("../setup/setup.js");
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-awesome/css/all.min.css');
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/font-combodo/font-combodo.css');
$this->add_saas("css/setup.scss");
$oKpi->ComputeStats(get_class($this).' creation', 'ErrorPage');
}
public function info($sText)

View File

@@ -26,7 +26,9 @@ class JsonPage extends WebPage
*/
public function __construct()
{
$oKpi = new ExecutionKPI();
parent::__construct('');
$oKpi->ComputeStats(get_class($this).' creation', 'JsonPage');
}
/**
@@ -89,13 +91,12 @@ class JsonPage extends WebPage
$aScripts = array_merge($this->a_init_scripts, $this->a_scripts, $this->a_ready_scripts);
$aJson = $this->bOutputDataOnly ? $this->aData : [
'data' => $this->aData,
'data' => $this->aData,
'scripts' => $aScripts,
];
$oKpi->ComputeAndReport(get_class($this).' prepare output');
$oKpi = new ExecutionKPI();
$sJSON = json_encode($aJson);
$oKpi->ComputeAndReport(get_class($this).' output');
echo $sJSON;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($sJSON) / 1024).' Kb)');
ExecutionKPI::ReportStats();

View File

@@ -27,10 +27,12 @@ class NiceWebPage extends WebPage
public function __construct($s_title, $bPrintable = false)
{
$oKpi = new ExecutionKPI();
$this->m_sRootUrl = $this->GetAbsoluteUrlAppRoot();
parent::__construct($s_title, $bPrintable);
$this->LoadTheme();
$oKpi->ComputeStats(get_class($this).' creation', 'NiceWebPage');
}
/**

View File

@@ -30,6 +30,7 @@ class PDFPage extends WebPage
public function __construct($s_title, $sPageFormat = 'A4', $sPageOrientation = 'L')
{
$oKpi = new ExecutionKPI();
parent::__construct($s_title);
define('K_PATH_FONTS', APPROOT.'lib/combodo/tcpdf/fonts/');
$this->oPdf = new iTopPDF($sPageOrientation, 'mm', $sPageFormat, true, self::PAGES_CHARSET, false);
@@ -54,7 +55,7 @@ class PDFPage extends WebPage
$this->SetContentDisposition('inline', $s_title.'.pdf');
$this->SetDefaultStyle();
$oKpi->ComputeStats(get_class($this).' creation', 'PDFPage');
}
/**

View File

@@ -67,6 +67,7 @@ class UnauthenticatedWebPage extends NiceWebPage
*/
public function __construct($s_title, $bPrintable = false)
{
$oKpi = new ExecutionKPI();
parent::__construct($s_title, $bPrintable);
$this->sContent = '';
@@ -96,6 +97,7 @@ class UnauthenticatedWebPage extends NiceWebPage
// Default theme
$this->add_saas('css/unauthenticated.scss');
$oKpi->ComputeStats(get_class($this).' creation', $s_title);
}
/**
@@ -111,6 +113,7 @@ class UnauthenticatedWebPage extends NiceWebPage
*/
public function output()
{
$oKpi = new ExecutionKPI();
// Send headers
foreach ($this->a_headers as $sHeader) {
header($sHeader);
@@ -172,8 +175,9 @@ class UnauthenticatedWebPage extends NiceWebPage
// Render final TWIG into global HTML
$sHtml = TwigHelper::RenderTemplate($oTwigEnv, $aData, $this->GetTemplateRelPath());
$oKpi->ComputeAndReport(get_class($this).' output');
echo $sHtml;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($sHtml) / 1024).' Kb)');
}
/**

View File

@@ -141,6 +141,7 @@ class WebPage implements Page
*/
public function __construct(string $s_title, bool $bPrintable = false)
{
$oKpi = new ExecutionKPI();
$this->s_title = $s_title;
$this->s_content = "";
$this->s_deferred_content = '';
@@ -170,6 +171,7 @@ class WebPage implements Page
$this->SetTemplateRelPath(static::DEFAULT_PAGE_TEMPLATE_REL_PATH);
ob_start(); // Start capturing the output
$oKpi->ComputeStats(get_class($this).' creation', 'WebPage');
}
/**
@@ -1205,16 +1207,13 @@ JS;
// Favicon
$aData['aPage']['sFaviconUrl'] = $this->GetFaviconAbsoluteUrl();
$oKpi->ComputeAndReport(get_class($this).' prepare output');
$oKpi = new ExecutionKPI();
$oTwigEnv = TwigHelper::GetTwigEnvironment(BlockRenderer::TWIG_BASE_PATH, BlockRenderer::TWIG_ADDITIONAL_PATHS);
// Render final TWIG into global HTML
$sHtml = TwigHelper::RenderTemplate($oTwigEnv, $aData, $this->GetTemplateRelPath());
$oKpi->ComputeAndReport('TWIG rendering');
$oKpi->ComputeAndReport(get_class($this).'output');
// Echo global HTML
$oKpi = new ExecutionKPI();
echo $sHtml;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($sHtml) / 1024).' Kb)');

View File

@@ -37,6 +37,7 @@ class XMLPage extends WebPage
function __construct($s_title, $bPassThrough = false)
{
$oKpi = new ExecutionKPI();
parent::__construct($s_title);
$this->m_bPassThrough = $bPassThrough;
$this->m_bHeaderSent = false;
@@ -44,6 +45,7 @@ class XMLPage extends WebPage
$this->no_cache();
$this->add_xframe_options();
$this->add_header("Content-location: export.xml");
$oKpi->ComputeStats(get_class($this).' creation', 'XMLPage');
}
public function output()
@@ -62,8 +64,7 @@ class XMLPage extends WebPage
{
header($s_header);
}
$oKpi->ComputeAndReport(get_class($this).' prepare output');
$oKpi = new ExecutionKPI();
$oKpi->ComputeAndReport(get_class($this).' output');
echo $this->s_content;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($this->s_content) / 1024).' Kb)');
}

View File

@@ -80,6 +80,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
*/
public function __construct($sTitle, $bPrintable = false)
{
$oKpi = new ExecutionKPI();
parent::__construct($sTitle, $bPrintable);
$this->m_oTabs = new TabManager();
$this->oCtx = new ContextTag(ContextTag::TAG_CONSOLE);
@@ -112,6 +113,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
$oPrintHeader = $this->OutputPrintable();
$this->AddUiBlock($oPrintHeader);
}
$oKpi->ComputeStats(get_class($this).' creation', 'iTopWebPage');
}
/**
@@ -888,18 +890,13 @@ HTML;
header($sHeader);
}
}
$oKpi->ComputeAndReport(get_class($this).' prepare output');
// Render final TWIG into global HTML
$oKpi = new ExecutionKPI();
$sHtml = TwigHelper::RenderTemplate($oTwigEnv, $aData, $this->GetTemplateRelPath());
$oKpi->ComputeAndReport('TWIG rendering');
$oKpi->ComputeAndReport(get_class($this).' output');
// Echo global HTML
$oKpi = new ExecutionKPI();
echo $sHtml;
$oKpi->ComputeAndReport('Echoing ('.round(strlen($sHtml) / 1024).' Kb)');

View File

@@ -33,9 +33,11 @@ class iTopWizardWebPage extends iTopWebPage
var $m_aSteps;
public function __construct($sTitle, $currentOrganization, $iCurrentStep, $aSteps)
{
parent::__construct($sTitle." - step $iCurrentStep of ".count($aSteps)." - ".$aSteps[$iCurrentStep - 1], $currentOrganization);
$oKpi = new ExecutionKPI();
parent::__construct($sTitle." - step $iCurrentStep of ".count($aSteps)." - ".$aSteps[$iCurrentStep - 1], $currentOrganization);
$this->m_iCurrentStep = $iCurrentStep;
$this->m_aSteps = $aSteps;
$this->m_aSteps = $aSteps;
$oKpi->ComputeStats(get_class($this).' creation', 'iTopWizardWebPage');
}
public function output()