| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620 |
- <?php
- // +----------------------------------------------------------------------
- // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
- // +----------------------------------------------------------------------
- // | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
- // +----------------------------------------------------------------------
- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
- // +----------------------------------------------------------------------
- // | Author: luofei614 <weibo.com/luofei614>
- // +----------------------------------------------------------------------
- // $Id$
- /**
- * 将Trace信息输出到chrome浏览器的控制器,从而不影响ajax效果和页面的布局。
- * 使用前,你需要先安装 chrome log 这个插件: http://craig.is/writing/chrome-logger。
- * 定义应用的tags.php文件 Application/Common/Conf/tags.php,
- * <code>
- * <?php return array(
- * 'app_end'=>array(
- * 'Behavior\ChromeShowPageTrace'
- * )
- * );
- * </code>
- * 如果trace信息没有正常输出,请查看您的日志。
- * 这是通过http headers和chrome通信,所以要保证在输出trace信息之前不能有
- * headers输出,你可以在入口文件第一行加入代码 ob_start(); 或者配置output_buffering
- *
- */
- namespace Behavior;
- use Behavior\ChromePhp as ChromePhp;
- use Think\Log;
- /**
- * 系统行为扩展 页面Trace显示输出
- */
- class ChromeShowPageTraceBehavior
- {
- protected $tracePageTabs = array('BASE' => '基本', 'FILE' => '文件', 'INFO' => '流程', 'ERR|NOTIC' => '错误', 'SQL' => 'SQL', 'DEBUG' => '调试');
- // 行为扩展的执行入口必须是run
- public function run(&$params)
- {
- if (C('SHOW_PAGE_TRACE')) {
- $this->showTrace();
- }
- }
- /**
- * 显示页面Trace信息
- * @access private
- */
- private function showTrace()
- {
- // 系统默认显示信息
- $files = get_included_files();
- $info = array();
- foreach ($files as $key => $file) {
- $info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )';
- }
- $trace = array();
- $base = array(
- '请求信息' => date('Y-m-d H:i:s', $_SERVER['REQUEST_TIME']) . ' ' . $_SERVER['SERVER_PROTOCOL'] . ' ' . $_SERVER['REQUEST_METHOD'] . ' : ' . __SELF__,
- '运行时间' => $this->showTime(),
- '吞吐率' => number_format(1 / G('beginTime', 'viewEndTime'), 2) . 'req/s',
- '内存开销' => MEMORY_LIMIT_ON ? number_format((memory_get_usage() - $GLOBALS['_startUseMems']) / 1024, 2) . ' kb' : '不支持',
- '查询信息' => N('db_query') . ' queries ' . N('db_write') . ' writes ',
- '文件加载' => count(get_included_files()),
- '缓存信息' => N('cache_read') . ' gets ' . N('cache_write') . ' writes ',
- '配置加载' => count(c()),
- '会话信息' => 'SESSION_ID=' . session_id(),
- );
- // 读取应用定义的Trace文件
- $traceFile = COMMON_PATH . 'Conf/trace.php';
- if (is_file($traceFile)) {
- $base = array_merge($base, include $traceFile);
- }
- $debug = trace();
- $tabs = C('TRACE_PAGE_TABS', null, $this->tracePageTabs);
- foreach ($tabs as $name => $title) {
- switch (strtoupper($name)) {
- case 'BASE': // 基本信息
- $trace[$title] = $base;
- break;
- case 'FILE': // 文件信息
- $trace[$title] = $info;
- break;
- default: // 调试信息
- $name = strtoupper($name);
- if (strpos($name, '|')) {
- // 多组信息
- $array = explode('|', $name);
- $result = array();
- foreach ($array as $name) {
- $result += isset($debug[$name]) ? $debug[$name] : array();
- }
- $trace[$title] = $result;
- } else {
- $trace[$title] = isset($debug[$name]) ? $debug[$name] : '';
- }
- }
- }
- chromeDebug('TRACE信息:' . __SELF__, 'group');
- //输出日志
- foreach ($trace as $title => $log) {
- '错误' == $title ? chromeDebug($title, 'group') : chromeDebug($title, 'groupCollapsed');
- foreach ($log as $i => $logstr) {
- chromeDebug($i . '.' . $logstr, 'log');
- }
- chromeDebug('', 'groupEnd');
- }
- chromeDebug('', 'groupEnd');
- if ($save = C('PAGE_TRACE_SAVE')) {
- // 保存页面Trace日志
- if (is_array($save)) { // 选择选项卡保存
- $tabs = C('TRACE_PAGE_TABS', null, $this->tracePageTabs);
- $array = array();
- foreach ($save as $tab) {
- $array[] = $tabs[$tab];
- }
- }
- $content = date('[ c ]') . ' ' . get_client_ip() . ' ' . $_SERVER['REQUEST_URI'] . "\r\n";
- foreach ($trace as $key => $val) {
- if (!isset($array) || in_array($key, $array)) {
- $content .= '[ ' . $key . " ]\r\n";
- if (is_array($val)) {
- foreach ($val as $k => $v) {
- $content .= (!is_numeric($k) ? $k . ':' : '') . print_r($v, true) . "\r\n";
- }
- } else {
- $content .= print_r($val, true) . "\r\n";
- }
- $content .= "\r\n";
- }
- }
- error_log(str_replace('<br/>', "\r\n", $content), 3, LOG_PATH . date('y_m_d') . '_trace.log');
- }
- unset($files, $info, $base);
- }
- /**
- * 获取运行时间
- */
- private function showTime()
- {
- // 显示运行时间
- G('beginTime', $GLOBALS['_beginTime']);
- G('viewEndTime');
- // 显示详细运行时间
- return G('beginTime', 'viewEndTime') . 's ( Load:' . G('beginTime', 'loadTime') . 's Init:' . G('loadTime', 'initTime') . 's Exec:' . G('initTime', 'viewStartTime') . 's Template:' . G('viewStartTime', 'viewEndTime') . 's )';
- }
- }
- if (!function_exists('chrome_debug')) {
- //ChromePhp 输出trace的函数
- function chromeDebug($msg, $type = 'trace', $trace_level = 1)
- {
- if ('trace' == $type) {
- ChromePhp::groupCollapsed($msg);
- $traces = debug_backtrace(false);
- $traces = array_reverse($traces);
- $max = count($traces) - $trace_level;
- for ($i = 0; $i < $max; $i++) {
- $trace = $traces[$i];
- $fun = isset($trace['class']) ? $trace['class'] . '::' . $trace['function'] : $trace['function'];
- $file = isset($trace['file']) ? $trace['file'] : 'unknown file';
- $line = isset($trace['line']) ? $trace['line'] : 'unknown line';
- $trace_msg = '#' . $i . ' ' . $fun . ' called at [' . $file . ':' . $line . ']';
- if (!empty($trace['args'])) {
- ChromePhp::groupCollapsed($trace_msg);
- ChromePhp::log($trace['args']);
- ChromePhp::groupEnd();
- } else {
- ChromePhp::log($trace_msg);
- }
- }
- ChromePhp::groupEnd();
- } else {
- if (method_exists('Behavior\ChromePhp', $type)) {
- //支持type trace,warn,log,error,group, groupCollapsed, groupEnd等
- call_user_func(array('Behavior\ChromePhp', $type), $msg);
- } else {
- //如果type不为trace,warn,log等,则为log的标签
- call_user_func_array(array('Behavior\ChromePhp', 'log'), func_get_args());
- }
- }
- }
- /**
- * Server Side Chrome PHP debugger class
- *
- * @package ChromePhp
- * @author Craig Campbell <iamcraigcampbell@gmail.com>
- */
- class ChromePhp
- {
- /**
- * @var string
- */
- const VERSION = '4.1.0';
- /**
- * @var string
- */
- const HEADER_NAME = 'X-ChromeLogger-Data';
- /**
- * @var string
- */
- const BACKTRACE_LEVEL = 'backtrace_level';
- /**
- * @var string
- */
- const LOG = 'log';
- /**
- * @var string
- */
- const WARN = 'warn';
- /**
- * @var string
- */
- const ERROR = 'error';
- /**
- * @var string
- */
- const GROUP = 'group';
- /**
- * @var string
- */
- const INFO = 'info';
- /**
- * @var string
- */
- const GROUP_END = 'groupEnd';
- /**
- * @var string
- */
- const GROUP_COLLAPSED = 'groupCollapsed';
- /**
- * @var string
- */
- const TABLE = 'table';
- /**
- * @var string
- */
- protected $_php_version;
- /**
- * @var int
- */
- protected $_timestamp;
- /**
- * @var array
- */
- protected $_json = array(
- 'version' => self::VERSION,
- 'columns' => array('log', 'backtrace', 'type'),
- 'rows' => array(),
- );
- /**
- * @var array
- */
- protected $_backtraces = array();
- /**
- * @var bool
- */
- protected $_error_triggered = false;
- /**
- * @var array
- */
- protected $_settings = array(
- self::BACKTRACE_LEVEL => 1,
- );
- /**
- * @var ChromePhp
- */
- protected static $_instance;
- /**
- * Prevent recursion when working with objects referring to each other
- *
- * @var array
- */
- protected $_processed = array();
- /**
- * constructor
- */
- private function __construct()
- {
- $this->_php_version = phpversion();
- $this->_timestamp = $this->_php_version >= 5.1 ? $_SERVER['REQUEST_TIME'] : time();
- $this->_json['request_uri'] = $_SERVER['REQUEST_URI'];
- }
- /**
- * gets instance of this class
- *
- * @return ChromePhp
- */
- public static function getInstance()
- {
- if (null === self::$_instance) {
- self::$_instance = new self();
- }
- return self::$_instance;
- }
- /**
- * logs a variable to the console
- *
- * @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
- * @return void
- */
- public static function log()
- {
- $args = func_get_args();
- return self::_log('', $args);
- }
- /**
- * logs a warning to the console
- *
- * @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
- * @return void
- */
- public static function warn()
- {
- $args = func_get_args();
- return self::_log(self::WARN, $args);
- }
- /**
- * logs an error to the console
- *
- * @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
- * @return void
- */
- public static function error()
- {
- $args = func_get_args();
- return self::_log(self::ERROR, $args);
- }
- /**
- * sends a group log
- *
- * @param string value
- */
- public static function group()
- {
- $args = func_get_args();
- return self::_log(self::GROUP, $args);
- }
- /**
- * sends an info log
- *
- * @param mixed $data,... unlimited OPTIONAL number of additional logs [...]
- * @return void
- */
- public static function info()
- {
- $args = func_get_args();
- return self::_log(self::INFO, $args);
- }
- /**
- * sends a collapsed group log
- *
- * @param string value
- */
- public static function groupCollapsed()
- {
- $args = func_get_args();
- return self::_log(self::GROUP_COLLAPSED, $args);
- }
- /**
- * ends a group log
- *
- * @param string value
- */
- public static function groupEnd()
- {
- $args = func_get_args();
- return self::_log(self::GROUP_END, $args);
- }
- /**
- * sends a table log
- *
- * @param string value
- */
- public static function table()
- {
- $args = func_get_args();
- return self::_log(self::TABLE, $args);
- }
- /**
- * internal logging call
- *
- * @param string $type
- * @return void
- */
- protected static function _log($type, array $args)
- {
- // nothing passed in, don't do anything
- if (count($args) == 0 && self::GROUP_END != $type) {
- return;
- }
- $logger = self::getInstance();
- $logger->_processed = array();
- $logs = array();
- foreach ($args as $arg) {
- $logs[] = $logger->_convert($arg);
- }
- $backtrace = debug_backtrace(false);
- $level = $logger->getSetting(self::BACKTRACE_LEVEL);
- $backtrace_message = 'unknown';
- if (isset($backtrace[$level]['file']) && isset($backtrace[$level]['line'])) {
- $backtrace_message = $backtrace[$level]['file'] . ' : ' . $backtrace[$level]['line'];
- }
- $logger->_addRow($logs, $backtrace_message, $type);
- }
- /**
- * converts an object to a better format for logging
- *
- * @param Object
- * @return array
- */
- protected function _convert($object)
- {
- // if this isn't an object then just return it
- if (!is_object($object)) {
- return $object;
- }
- //Mark this object as processed so we don't convert it twice and it
- //Also avoid recursion when objects refer to each other
- $this->_processed[] = $object;
- $object_as_array = array();
- // first add the class name
- $object_as_array['___class_name'] = get_class($object);
- // loop through object vars
- $object_vars = get_object_vars($object);
- foreach ($object_vars as $key => $value) {
- // same instance as parent object
- if ($value === $object || in_array($value, $this->_processed, true)) {
- $value = 'recursion - parent object [' . get_class($value) . ']';
- }
- $object_as_array[$key] = $this->_convert($value);
- }
- $reflection = new ReflectionClass($object);
- // loop through the properties and add those
- foreach ($reflection->getProperties() as $property) {
- // if one of these properties was already added above then ignore it
- if (array_key_exists($property->getName(), $object_vars)) {
- continue;
- }
- $type = $this->_getPropertyKey($property);
- if ($this->_php_version >= 5.3) {
- $property->setAccessible(true);
- }
- try {
- $value = $property->getValue($object);
- } catch (ReflectionException $e) {
- $value = 'only PHP 5.3 can access private/protected properties';
- }
- // same instance as parent object
- if ($value === $object || in_array($value, $this->_processed, true)) {
- $value = 'recursion - parent object [' . get_class($value) . ']';
- }
- $object_as_array[$type] = $this->_convert($value);
- }
- return $object_as_array;
- }
- /**
- * takes a reflection property and returns a nicely formatted key of the property name
- *
- * @param ReflectionProperty
- * @return string
- */
- protected function _getPropertyKey(ReflectionProperty $property)
- {
- $static = $property->isStatic() ? ' static' : '';
- if ($property->isPublic()) {
- return 'public' . $static . ' ' . $property->getName();
- }
- if ($property->isProtected()) {
- return 'protected' . $static . ' ' . $property->getName();
- }
- if ($property->isPrivate()) {
- return 'private' . $static . ' ' . $property->getName();
- }
- }
- /**
- * adds a value to the data array
- *
- * @var mixed
- * @return void
- */
- protected function _addRow(array $logs, $backtrace, $type)
- {
- // if this is logged on the same line for example in a loop, set it to null to save space
- if (in_array($backtrace, $this->_backtraces)) {
- $backtrace = null;
- }
- // for group, groupEnd, and groupCollapsed
- // take out the backtrace since it is not useful
- if (self::GROUP == $type || self::GROUP_END == $type || self::GROUP_COLLAPSED == $type) {
- $backtrace = null;
- }
- if (null !== $backtrace) {
- $this->_backtraces[] = $backtrace;
- }
- $row = array($logs, $backtrace, $type);
- $this->_json['rows'][] = $row;
- $this->_writeHeader($this->_json);
- }
- protected function _writeHeader($data)
- {
- header(self::HEADER_NAME . ': ' . $this->_encode($data));
- }
- /**
- * encodes the data to be sent along with the request
- *
- * @param array $data
- * @return string
- */
- protected function _encode($data)
- {
- return base64_encode(utf8_encode(json_encode($data)));
- }
- /**
- * adds a setting
- *
- * @param string key
- * @param mixed value
- * @return void
- */
- public function addSetting($key, $value)
- {
- $this->_settings[$key] = $value;
- }
- /**
- * add ability to set multiple settings in one call
- *
- * @param array $settings
- * @return void
- */
- public function addSettings(array $settings)
- {
- foreach ($settings as $key => $value) {
- $this->addSetting($key, $value);
- }
- }
- /**
- * gets a setting
- *
- * @param string key
- * @return mixed
- */
- public function getSetting($key)
- {
- if (!isset($this->_settings[$key])) {
- return null;
- }
- return $this->_settings[$key];
- }
- }
- }
|