154 lines
4.2 KiB
PHP
154 lines
4.2 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
/**
|
|
* The trace class file of ZenTaoPMS.
|
|
*
|
|
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.zentao.net)
|
|
* @license ZPL(https://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
|
* @author Lu Fei <lufei@easycorp.ltd>
|
|
* @package trace
|
|
* @link https://www.zentao.net
|
|
*/
|
|
|
|
class trace
|
|
{
|
|
/**
|
|
* @var array
|
|
*/
|
|
public $trace = array();
|
|
|
|
protected $app;
|
|
|
|
protected $dao;
|
|
|
|
public function __construct()
|
|
{
|
|
global $app, $dao;
|
|
$this->app = $app;
|
|
$this->dao = $dao;
|
|
}
|
|
|
|
/**
|
|
* 获取请求信息。
|
|
* Get request info.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function getRequestInfo()
|
|
{
|
|
$this->trace['request'] = array(
|
|
'start' => date('Y-m-d H:i:s', (int)$this->app->startTime),
|
|
'url' => $this->app->getURI(true),
|
|
'protocol' => $this->app->server->server_protocol,
|
|
'method' => $this->app->server->request_method,
|
|
'timeUsed' => round(getTime() - $this->app->startTime, 4) * 1000,
|
|
'memory' => round(memory_get_peak_usage() / 1024, 1),
|
|
'querys' => count(dao::$querys),
|
|
'caches' => 0,
|
|
'files' => count(get_included_files()),
|
|
'session' => session_id(),
|
|
'php' => phpversion()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 获取请求加载的文件。
|
|
* Get request files.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function getRequestFiles()
|
|
{
|
|
$this->trace['files'] = get_included_files();
|
|
}
|
|
|
|
/**
|
|
* 获取请求的 SQL 语句。
|
|
* Get request SQLs.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function getRequestSqls()
|
|
{
|
|
$this->trace['sqlQuery'] = dao::$querys;
|
|
}
|
|
|
|
/**
|
|
* 获取请求的 SQL profiles。
|
|
* Get request SQL profiles.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getSQLProfiles()
|
|
{
|
|
global $config;
|
|
/* 达梦数据库不支持下面的语法,直接跳过。The */
|
|
if($config->db->driver === 'dm') return;
|
|
|
|
$profiling = $this->dao->dbh->query('SHOW PROFILES')->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
foreach($profiling as $key => $profile)
|
|
{
|
|
$profiling[$key]['Duration'] = round($profile['Duration'], 4);
|
|
|
|
$sql = trim($profile['Query']);
|
|
if(stripos($sql, 'select') !== 0
|
|
&& strpos($sql, 'insert') !== 0
|
|
&& strpos($sql, 'update') !== 0
|
|
&& strpos($sql, 'delete') !== 0
|
|
&& strpos($sql, 'replace') !== 0
|
|
)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
try
|
|
{
|
|
$slow = false;
|
|
$rows = $this->dao->dbh->query("EXPLAIN $sql")->fetchAll();
|
|
foreach($rows as $row)
|
|
{
|
|
if($row->type === 'ALL'
|
|
|| stripos($row->Extra, 'temporary') !== false
|
|
|| stripos($row->Extra, 'filesort') !== false
|
|
|| stripos($row->Extra, 'join buffer') !== false
|
|
|| stripos($row->Extra, 'checked for each record') !== false
|
|
|| stripos($row->Extra, 'full scan on null key') !== false
|
|
)
|
|
{
|
|
$slow = true;
|
|
break;
|
|
}
|
|
}
|
|
if($slow) $profiling[$key]['Explain'] = $rows;
|
|
}
|
|
catch(PDOException $e)
|
|
{
|
|
$profiling[$key]['Error'] = 'The sql statement was too long and was truncated and cannot be explained.';
|
|
}
|
|
}
|
|
|
|
$this->trace['profiles'] = $profiling;
|
|
}
|
|
|
|
/**
|
|
* 生成请求 Trace。
|
|
* Generate request trace.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getTrace()
|
|
{
|
|
$this->getRequestInfo();
|
|
$this->getRequestFiles();
|
|
$this->getRequestSqls();
|
|
$this->getSQLProfiles();
|
|
return $this->trace;
|
|
}
|
|
|
|
public function __toString(): string
|
|
{
|
|
return json_encode($this->getTrace());
|
|
}
|
|
}
|