From 0afe9e3aad114fac4d116411ed083346307dc630 Mon Sep 17 00:00:00 2001 From: thinkphp Date: Sun, 20 Dec 2015 18:08:36 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E5=86=99Log=E7=B1=BB=E5=92=8C?= =?UTF-8?q?=E9=A9=B1=E5=8A=A8=20SockectLog=E7=BA=B3=E5=85=A5Log=E9=A9=B1?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- base.php | 12 +- convention.php | 15 -- library/think/app.php | 8 +- library/think/db/driver.php | 7 +- library/think/debug.php | 81 +++++- library/think/error.php | 9 +- library/think/log.php | 94 ++++--- library/think/log/driver/file.php | 45 +++- library/think/log/driver/sae.php | 42 ++- library/think/log/driver/socket.php | 185 ++++++++++++++ library/think/slog.php | 382 ---------------------------- mode/common.php | 1 - 12 files changed, 379 insertions(+), 502 deletions(-) create mode 100644 library/think/log/driver/socket.php delete mode 100644 library/think/slog.php diff --git a/base.php b/base.php index 66bea834..c4903edc 100644 --- a/base.php +++ b/base.php @@ -38,7 +38,6 @@ defined('APP_DEBUG') or define('APP_DEBUG', false); // 是否调试模式 defined('APP_HOOK') or define('APP_HOOK', false); // 是否开启HOOK defined('ENV_PREFIX') or define('ENV_PREFIX', 'T_'); // 环境变量的配置前缀 defined('IS_API') or define('IS_API', false); // 是否API接口 -defined('SLOG_ON') or define('SLOG_ON', false); // 是否开启socketLog defined('IN_UNIT_TEST') or define('IN_UNIT_TEST', false); // 是否为单元测试 // 应用模式 默认为普通模式 @@ -310,14 +309,13 @@ function S($name, $value = '', $options = null) * 添加Trace记录到SocketLog * @param mixed $log log信息 支持字符串和数组 * @param string $level 日志级别 - * @param string $css 样式 * @return void|array */ -function trace($log, $level = 'log', $css = '') +function trace($log='[think]', $level = 'log') { - if ('trace' == $level) { - \think\Slog::trace($log, 2, $css); - } else { - \think\Slog::record($level, $log, $css); + if('[think]'==$log){ + return \think\Log::getLog(); + }else{ + \think\Log::record($log,$level); } } diff --git a/convention.php b/convention.php index 751382da..a0ee6bae 100644 --- a/convention.php +++ b/convention.php @@ -176,19 +176,4 @@ return [ 'slave_no' => '', ], - // +---------------------------------------------------------------------- - // | SocketLog设置 - // +---------------------------------------------------------------------- - - 'slog' => [ - 'host' => 'localhost', - //是否显示利于优化的参数,如果允许时间,消耗内存等 - 'optimize' => true, - 'show_included_files' => true, - 'error_handler' => true, - //日志强制记录到配置的client_id - 'force_client_id' => '', - //限制允许读取日志的client_id - 'allow_client_ids' => [], - ], ]; diff --git a/library/think/app.php b/library/think/app.php index 646e5831..1f5dc36b 100644 --- a/library/think/app.php +++ b/library/think/app.php @@ -43,10 +43,6 @@ class App // 缓存初始化 Cache::connect($config['cache']); - // 如果启动SocketLog调试, 进行SocketLog配置 - if (SLOG_ON) { - Slog::config($config['slog']); - } // 设置系统时区 date_default_timezone_set($config['default_timezone']); @@ -61,8 +57,8 @@ class App Lang::load(THINK_PATH . 'lang' . DS . LANG_SET . EXT); } - // 启动session API CLI 不开启 - if (!IS_CLI && !IS_API && $config['use_session']) { + // 启动session CLI 不开启 + if (!IS_CLI && $config['use_session']) { Session::init($config['session']); } diff --git a/library/think/db/driver.php b/library/think/db/driver.php index 59393045..ea169b35 100644 --- a/library/think/db/driver.php +++ b/library/think/db/driver.php @@ -1156,16 +1156,11 @@ abstract class Driver Debug::remark('queryStartTime', 'time'); } else { $this->modelSql[$this->model] = $this->queryStr; - //$this->model = '_think_'; // 记录操作结束时间 Debug::remark('queryEndTime', 'time'); - Log::record($this->queryStr . ' [ RunTime:' . Debug::getUseTime('queryStartTime', 'queryEndTime') . 's ]', 'SQL'); + Log::record($this->queryStr . ' [ RunTime:' . Debug::getRangeTime('queryStartTime', 'queryEndTime') . 's ]', 'sql'); } } - - if (SLOG_ON && $start) { - \think\Slog::sql($this->queryStr, $this->_linkID); - } } /** diff --git a/library/think/debug.php b/library/think/debug.php index 4b27f25d..9ff563b1 100644 --- a/library/think/debug.php +++ b/library/think/debug.php @@ -13,9 +13,10 @@ namespace think; class Debug { - + // 区间时间信息 protected static $info = []; - protected static $mem = []; + // 区间内存信息 + protected static $mem = []; /** * 记录时间(微秒)和内存使用情况 @@ -38,9 +39,9 @@ class Debug * @param string $start 开始标签 * @param string $end 结束标签 * @param integer|string $dec 小数位 - * @return mixed + * @return integer */ - public static function getUseTime($start, $end, $dec = 6) + public static function getRangeTime($start, $end, $dec = 6) { if (!isset(self::$info[$end])) { self::$info[$end] = microtime(true); @@ -49,13 +50,32 @@ class Debug } /** - * 记录内存使用情况 + * 统计从开始到统计时的时间(微秒)使用情况 + * @param integer|string $dec 小数位 + * @return integer + */ + public static function getUseTime($dec = 6) + { + return number_format((microtime(true) - START_TIME), $dec); + } + + /** + * 获取当前访问的吞吐率情况 + * @return string + */ + public static function getThroughputRate() + { + return number_format(1 / self::getUseTime(), 2) . 'req/s'; + } + + /** + * 记录区间的内存使用情况 * @param string $start 开始标签 * @param string $end 结束标签 * @param integer|string $dec 小数位 - * @return mixed + * @return string */ - public static function getUseMem($start, $end, $dec = 2) + public static function getRangeMem($start, $end, $dec = 2) { if (!isset(self::$mem['mem'][$end])) { self::$mem['mem'][$end] = memory_get_usage(); @@ -71,7 +91,24 @@ class Debug } /** - * 统计内存峰值情况 + * 统计从开始到统计时的内存使用情况 + * @param integer|string $dec 小数位 + * @return string + */ + public static function getUseMem($dec = 2) + { + $size = memory_get_usage() - START_MEM; + $a = ['B', 'KB', 'MB', 'GB', 'TB']; + $pos = 0; + while ($size >= 1024) { + $size /= 1024; + $pos++; + } + return round($size, $dec) . " " . $a[$pos]; + } + + /** + * 统计区间的内存峰值情况 * @param string $start 开始标签 * @param string $end 结束标签 * @param integer|string $dec 小数位 @@ -92,6 +129,33 @@ class Debug return round($size, $dec) . " " . $a[$pos]; } + /** + * 获取数据库查询信息 + * @return void + */ + public static function getDbQuery() + { + return Db::getInstance()->getQueryTimes() . ' queries ' . Db::getInstance()->getExecuteTimes() . ' writes '; + } + + /** + * 获取文件加载信息 + * @param bool $detail 是否显示详细 + * @return void + */ + public static function getFile($detail = false) + { + if ($detail) { + $files = get_included_files(); + $info = []; + foreach ($files as $key => $file) { + $info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )'; + } + return $info; + } + return count(get_included_files()); + } + /** * 浏览器友好的变量输出 * @param mixed $var 变量 @@ -121,4 +185,5 @@ class Debug return $output; } } + } diff --git a/library/think/error.php b/library/think/error.php index 074a280e..95813286 100644 --- a/library/think/error.php +++ b/library/think/error.php @@ -28,7 +28,7 @@ class Error 'code' => $e->getCode(), ]; // 记录异常日志 - Log::record($error['message'], 'ERR'); + Log::record($error['message'], 'error'); // 发送http状态信息 Response::sendHttpStatus(Config::get('exception_http_status')); // 输出异常页面 @@ -49,14 +49,14 @@ class Error $errorStr = "[{$errno}] {$errstr} {$errfile} 第 {$errline} 行."; switch ($errno) { case E_USER_ERROR: - Log::record($errorStr, 'ERROR'); + Log::record($errorStr, 'error'); self::halt($errorStr, $errno); break; case E_STRICT: case E_USER_WARNING: case E_USER_NOTICE: default: - Log::record($errorStr, 'NOTIC'); + Log::record($errorStr, 'warn'); break; } } @@ -69,9 +69,6 @@ class Error { // 记录日志 Log::save(); - if (SLOG_ON) { - Slog::sendLog(); - } if ($e = error_get_last()) { switch ($e['type']) { case E_ERROR: diff --git a/library/think/log.php b/library/think/log.php index f2b73b45..15577e20 100644 --- a/library/think/log.php +++ b/library/think/log.php @@ -13,10 +13,19 @@ namespace think; class Log { + const LOG = 'log'; + const ERROR = 'error'; + const INFO = 'info'; + const SQL = 'sql'; + const WARN = 'warn'; + const ALERT = 'alert'; + // 日志信息 - protected static $log = []; - protected static $level = ['ERR', 'NOTIC', 'DEBUG', 'SQL', 'INFO']; - protected static $storage = null; + protected static $log = []; + // 日志类型 + protected static $type = ['log', 'error', 'info', 'sql', 'warn', 'alert']; + // 日志写入驱动 + protected static $driver = null; // 日志初始化 public static function init($config = []) @@ -24,70 +33,55 @@ class Log $type = isset($config['type']) ? $config['type'] : 'File'; $class = '\\think\\log\\driver\\' . ucwords($type); unset($config['type']); - self::$storage = new $class($config); + self::$driver = new $class($config); } /** - * 记录日志 并且会过滤未经设置的级别 - * @access public - * - * @param string $message 日志信息 - * @param string $level 日志级别 - * - * @internal param bool $record 是否强制记录 - */ - public static function record($message, $level = 'INFO') - { - self::$log[$level][] = "{$level}: {$message}"; - } - - /** - * 获取内存中的日志信息 - * @access public - * @param string $level 日志级别 + * 获取全部日志信息 * @return array */ - public static function getLog($level = '') + public static function getLog() { - return $level ? self::$log[$level] : self::$log; + return self::$log; } /** - * 日志保存 - * @access public - * @param string $destination 写入目标 - * @param string $level 保存的日志级别 + * 记录调试信息 + * @param string $msg 调试信息 + * @param string $type 信息类型 * @return void */ - public static function save($destination = '', $level = '') + public static function record($msg, $type = 'log') { - $log = self::getLog($level); - if (empty($log)) { - return; - } - $message = ''; - if ($level) { - $message .= implode("\r\n", $log); - self::$log[$level] = []; - } else { - foreach ($log as $info) { - $message .= implode("\r\n", $info) . "\r\n"; - } - self::$log = []; - } - self::$storage && self::$storage->write($message, $destination); + self::$log[] = ['type' => $type, 'msg' => $msg]; } /** - * 日志直接写入 - * @access public - * @param string $log 日志信息 - * @param string $level 日志级别 - * @param string $destination 写入目标 + * 保存调试信息 * @return void */ - public static function write($log, $level = '', $destination = '') + public static function save() { - self::$storage && self::$storage->write("{$level}: {$log}", $destination); + self::$driver && self::$driver->save(self::$log); } + + /** + * 写入调试信息 + * @return void + */ + public static function write($msg, $type, $destination) + { + $log[] = ['type' => $type, 'msg' => $msg]; + self::$driver && self::$driver->save($log); + } + + // 静态调用 + public static function __callStatic($method, $args) + { + if (in_array($method, self::$type)) { + array_push($args, $method); + return call_user_func_array('\think\Log::record', $args); + } + } + } diff --git a/library/think/log/driver/file.php b/library/think/log/driver/file.php index a3f49d24..9fb711c8 100644 --- a/library/think/log/driver/file.php +++ b/library/think/log/driver/file.php @@ -1,23 +1,25 @@ // +---------------------------------------------------------------------- - namespace think\log\driver; +/** + * 本地化调试输出到文件 + */ class File { protected $config = [ 'time_format' => ' c ', 'file_size' => 2097152, - 'path' => '', + 'path' => LOG_PATH, ]; // 实例化并传入参数 @@ -29,22 +31,41 @@ class File /** * 日志写入接口 * @access public - * @param string $log 日志信息 - * @param string $destination 写入目标 + * @param array $log 日志信息 * @return void */ - public function write($log, $destination = '') + public function save($log = []) { - $now = date($this->config['time_format']); - if (empty($destination)) { - $destination = $this->config['path'] . date('y_m_d') . '.log'; - } + $now = date($this->config['time_format']); + $destination = $this->config['path'] . date('y_m_d') . '.log'; //检测日志文件大小,超过配置大小则备份日志文件重新生成 if (is_file($destination) && floor($this->config['file_size']) <= filesize($destination)) { rename($destination, dirname($destination) . DS . time() . '-' . basename($destination)); } + // 获取基本信息 + if (isset($_SERVER['HTTP_HOST'])) { + $current_uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + } else { + $current_uri = "cmd:" . implode(' ', $_SERVER['argv']); + } + $runtime = number_format(microtime(true) - START_TIME, 6); + $reqs = number_format(1 / $runtime, 2); + $time_str = " [运行时间:{$runtime}s] [吞吐率:{$reqs}req/s]"; + $memory_use = number_format((memory_get_usage() - START_MEM) / 1024, 2); + $memory_str = " [内存消耗:{$memory_use}kb]"; + $file_load = " [文件加载:" . count(get_included_files()) . "]"; - error_log("[{$now}] {$_SERVER['SERVER_ADDR']} {$_SERVER['REMOTE_ADDR']} {$_SERVER['REQUEST_URI']}\r\n{$log}\r\n", 3, $destination); + array_unshift($log, [ + 'type' => 'log', + 'msg' => $current_uri . $time_str . $memory_str . $file_load, + ]); + + $info = ''; + foreach ($log as $line) { + $info .= '[' . $line['type'] . '] ' . $line['msg'] . "\r\n"; + } + error_log("[{$now}] {$_SERVER['SERVER_ADDR']} {$_SERVER['REMOTE_ADDR']} {$_SERVER['REQUEST_URI']}\r\n{$info}\r\n", 3, $destination); } + } diff --git a/library/think/log/driver/sae.php b/library/think/log/driver/sae.php index f92140cf..17f4b7fe 100644 --- a/library/think/log/driver/sae.php +++ b/library/think/log/driver/sae.php @@ -1,19 +1,20 @@ +// | Author: liu21st // +---------------------------------------------------------------------- - namespace think\log\driver; +/** + * 调试输出到SAE + */ class Sae { - protected $config = [ 'log_time_format' => ' c ', ]; @@ -27,15 +28,37 @@ class Sae /** * 日志写入接口 * @access public - * @param string $log 日志信息 - * @param string $destination 写入目标 + * @param array $log 日志信息 * @return void */ - public function write($log, $destination = '') + public function save($log = []) { static $is_debug = null; $now = date($this->config['log_time_format']); - $logstr = "[{$now}] {$_SERVER['SERVER_ADDR']} {$_SERVER['REMOTE_ADDR']} {$_SERVER['REQUEST_URI']}\r\n{$log}\r\n"; + // 获取基本信息 + if (isset($_SERVER['HTTP_HOST'])) { + $current_uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + } else { + $current_uri = "cmd:" . implode(' ', $_SERVER['argv']); + } + $runtime = number_format(microtime(true) - START_TIME, 6); + $reqs = number_format(1 / $runtime, 2); + $time_str = " [运行时间:{$runtime}s] [吞吐率:{$reqs}req/s]"; + $memory_use = number_format((memory_get_usage() - START_MEM) / 1024, 2); + $memory_str = " [内存消耗:{$memory_use}kb]"; + $file_load = " [文件加载:" . count(get_included_files()) . "]"; + + array_unshift($log, [ + 'type' => 'log', + 'msg' => $current_uri . $time_str . $memory_str . $file_load, + ]); + + $info = ''; + foreach ($log as $line) { + $info .= '[' . $line['type'] . '] ' . $line['msg'] . "\r\n"; + } + + $logstr = "[{$now}] {$_SERVER['SERVER_ADDR']} {$_SERVER['REMOTE_ADDR']} {$_SERVER['REQUEST_URI']}\r\n{$info}\r\n"; if (is_null($is_debug)) { preg_replace('@(\w+)\=([^;]*)@e', '$appSettings[\'\\1\']="\\2";', $_SERVER['HTTP_APPCOOKIE']); $is_debug = in_array($_SERVER['HTTP_APPVERSION'], explode(',', $appSettings['debug'])) ? true : false; @@ -49,4 +72,5 @@ class Sae } } + } diff --git a/library/think/log/driver/socket.php b/library/think/log/driver/socket.php new file mode 100644 index 00000000..eac26891 --- /dev/null +++ b/library/think/log/driver/socket.php @@ -0,0 +1,185 @@ + + */ +namespace think\log\driver; + +class Socket +{ + public $port = 1116; //SocketLog 服务的http的端口号 + + protected $config = [ + 'enable' => true, //是否记录日志的开关 + 'host' => 'localhost', + //是否显示利于优化的参数,如果允许时间,消耗内存等 + 'optimize' => false, + 'show_included_files' => false, + 'error_handler' => false, + //日志强制记录到配置的client_id + 'force_client_id' => '', + //限制允许读取日志的client_id + 'allow_client_ids' => [], + ]; + + protected $css = [ + 'sql' => 'color:#009bb4;', + 'sql_warn' => 'color:#009bb4;font-size:14px;', + 'error_handler' => 'color:#f4006b;font-size:14px;', + 'page' => 'color:#40e2ff;background:#171717;', + 'big' => 'font-size:20px;color:red;', + ]; + + /** + * 架构函数 + * @param array $options 缓存参数 + * @access public + */ + public function __construct($config = []) + { + if (!empty($config)) { + $this->config = array_merge($this->config, $config); + } + } + + public function save($logs = []) + { + if (!$this->check()) { + return; + } + $runtime = number_format(microtime(true) - START_TIME, 6); + $reqs = number_format(1 / $runtime, 2); + $time_str = " [运行时间:{$runtime}s][吞吐率:{$reqs}req/s]"; + $memory_use = number_format((memory_get_usage() - START_MEM) / 1024, 2); + $memory_str = " [内存消耗:{$memory_use}kb]"; + $file_load = " [文件加载:" . count(get_included_files()) . "]"; + + if (isset($_SERVER['HTTP_HOST'])) { + $current_uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + } else { + $current_uri = "cmd:" . implode(' ', $_SERVER['argv']); + } + array_unshift($logs, [ + 'type' => 'group', + 'msg' => $current_uri . $time_str . $memory_str . $file_load, + 'css' => $this->css['page'], + ]); + + $logs[] = [ + 'type' => 'groupCollapsed', + 'msg' => 'included_files', + 'css' => '', + ]; + $logs[] = [ + 'type' => 'log', + 'msg' => implode("\n", get_included_files()), + 'css' => '', + ]; + $logs[] = [ + 'type' => 'groupEnd', + 'msg' => '', + 'css' => '', + ]; + + $logs[] = [ + 'type' => 'groupEnd', + 'msg' => '', + 'css' => '', + ]; + + foreach ($logs as &$log) { + if ('sql' == $log['type']) { + $log['type'] = 'log'; + } + } + $tabid = $this->getClientArg('tabid'); + if (!$client_id = $this->getClientArg('client_id')) { + $client_id = ''; + } + if ($force_client_id = $this->config['force_client_id']) { + $client_id = $force_client_id; + } + $logs = [ + 'tabid' => $tabid, + 'client_id' => $client_id, + 'logs' => $logs, + 'force_client_id' => $force_client_id, + ]; + $msg = json_encode($logs); + $address = '/' . $client_id; //将client_id作为地址, server端通过地址判断将日志发布给谁 + $this->send($this->config['host'], $msg, $address); + } + + protected function check() + { + $tabid = $this->getClientArg('tabid'); + //是否记录日志的检查 + if (!$tabid && !$this->config['force_client_id']) { + return false; + } + //用户认证 + $allow_client_ids = $this->config['allow_client_ids']; + if (!empty($allow_client_ids)) { + if (!$tabid && in_array($this->config['force_client_id'], $allow_client_ids)) { + return true; + } + + $client_id = $this->getClientArg('client_id'); + if (!in_array($client_id, $allow_client_ids)) { + return false; + } + } + return true; + } + + protected function getClientArg($name) + { + static $args = []; + + $key = 'HTTP_USER_AGENT'; + + if (isset($_SERVER['HTTP_SOCKETLOG'])) { + $key = 'HTTP_SOCKETLOG'; + } + + if (!isset($_SERVER[$key])) { + return null; + } + if (empty($args)) { + if (!preg_match('/SocketLog\((.*?)\)/', $_SERVER[$key], $match)) { + $args = ['tabid' => null]; + return null; + } + parse_str($match[1], $args); + } + if (isset($args[$name])) { + return $args[$name]; + } + return null; + } + + /** + * @param null $host - $host of socket server + * @param string $message - 发送的消息 + * @param string $address - 地址 + * @return bool + */ + protected function send($host, $message = '', $address = '/') + { + $url = 'http://' . $host . ':' . $this->port . $address; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $message); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + $headers = [ + "Content-Type: application/json;charset=UTF-8", + ]; + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //设置header + $txt = curl_exec($ch); + return true; + } + +} diff --git a/library/think/slog.php b/library/think/slog.php deleted file mode 100644 index 02cfb889..00000000 --- a/library/think/slog.php +++ /dev/null @@ -1,382 +0,0 @@ - - */ -namespace think; - -use think\Exception; - -class Slog -{ - public static $start_time = 0; - public static $start_memory = 0; - public static $port = 1116; //SocketLog 服务的http的端口号 - public static $log_types = ['log', 'info', 'error', 'warn', 'table', 'group','groupCollapsed','groupEnd','alert']; - - protected static $config = [ - 'enable' => true, //是否记录日志的开关 - 'host' => 'localhost', - //是否显示利于优化的参数,如果允许时间,消耗内存等 - 'optimize' => false, - 'show_included_files' => false, - 'error_handler' => false, - //日志强制记录到配置的client_id - 'force_client_id' => '', - //限制允许读取日志的client_id - 'allow_client_ids' => [], - ]; - - protected static $logs = []; - - protected static $css = [ - 'sql' => 'color:#009bb4;', - 'sql_warn' => 'color:#009bb4;font-size:14px;', - 'error_handler' => 'color:#f4006b;font-size:14px;', - 'page' => 'color:#40e2ff;background:#171717;', - 'big' => 'font-size:20px;color:red;', - ]; - - public static function sql($sql, $pdo) - { - - if (is_object($pdo)) { - if (!self::check()) { - return; - } - $css = self::$css['sql']; - if (preg_match('/^SELECT /i', $sql)) { - //explain - try { - $obj = $pdo->query("EXPLAIN " . $sql); - if (is_object($obj) && method_exists($obj, 'fetch')) { - $arr = $obj->fetch(\PDO::FETCH_ASSOC); - self::sqlexplain($arr, $sql, $css); - } - } catch (Exception $e) { - - } - } - self::sqlwhere($sql, $css); - self::trace($sql, 2, $css); - } else { - throw new Exception('SocketLog can not support this database link'); - } - - } - - public static function trace($msg, $trace_level = 1, $css = '') - { - if (!self::check()) { - return; - } - self::record('groupCollapsed', $msg, $css); - $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'])) { - self::record('groupCollapsed', $trace_msg); - self::record('log', $trace['args']); - self::record('groupEnd'); - } else { - self::record('log', $trace_msg); - } - } - self::record('groupEnd'); - } - - private static function sqlexplain($arr, &$sql, &$css) - { - $arr = array_change_key_case($arr, CASE_LOWER); - if (false !== strpos($arr['extra'], 'Using filesort')) { - $sql .= ' <---################[Using filesort]'; - $css = self::$css['sql_warn']; - } - if (false !== strpos($arr['extra'], 'Using temporary')) { - $sql .= ' <---################[Using temporary]'; - $css = self::$css['sql_warn']; - } - } - private static function sqlwhere(&$sql, &$css) - { - //判断sql语句是否有where - if (preg_match('/^UPDATE |DELETE /i', $sql) && !preg_match('/WHERE.*(=|>|<|LIKE|IN)/i', $sql)) { - $sql .= '<---###########[NO WHERE]'; - $css = self::$css['sql_warn']; - } - } - - /** - * 接管报错 - */ - public static function registerErrorHandler() - { - if (!self::check()) { - return; - } - - set_error_handler([__CLASS__, 'error_handler']); - register_shutdown_function([__CLASS__, 'fatalError']); - } - - public static function error_handler($errno, $errstr, $errfile, $errline) - { - switch ($errno) { - case E_WARNING: - $severity = 'E_WARNING'; - break; - case E_NOTICE: - $severity = 'E_NOTICE'; - break; - case E_USER_ERROR: - $severity = 'E_USER_ERROR'; - break; - case E_USER_WARNING: - $severity = 'E_USER_WARNING'; - break; - case E_USER_NOTICE: - $severity = 'E_USER_NOTICE'; - break; - case E_STRICT: - $severity = 'E_STRICT'; - break; - case E_RECOVERABLE_ERROR: - $severity = 'E_RECOVERABLE_ERROR'; - break; - case E_DEPRECATED: - $severity = 'E_DEPRECATED'; - break; - case E_USER_DEPRECATED: - $severity = 'E_USER_DEPRECATED'; - break; - case E_ERROR: - $severity = 'E_ERR'; - break; - case E_PARSE: - $severity = 'E_PARSE'; - break; - case E_CORE_ERROR: - $severity = 'E_CORE_ERROR'; - break; - case E_COMPILE_ERROR: - $severity = 'E_COMPILE_ERROR'; - break; - case E_USER_ERROR: - $severity = 'E_USER_ERROR'; - break; - default: - $severity = 'E_UNKNOWN_ERROR_' . $errno; - break; - } - $msg = "{$severity}: {$errstr} in {$errfile} on line {$errline} -- SocketLog error handler"; - self::trace($msg, 2, self::$css['error_handler']); - } - - public static function fatalError() - { - // 保存日志记录 - if ($e = error_get_last()) { - self::error_handler($e['type'], $e['message'], $e['file'], $e['line']); - self::sendLog(); - } - } - - protected static function check() - { - if (!SLOG_ON) { - return false; - } - $tabid = self::getClientArg('tabid'); - //是否记录日志的检查 - if (!$tabid && !self::$config['force_client_id']) { - return false; - } - //用户认证 - $allow_client_ids = self::$config['allow_client_ids']; - if (!empty($allow_client_ids)) { - if (!$tabid && in_array(self::$config['force_client_id'], $allow_client_ids)) { - return true; - } - - $client_id = self::getClientArg('client_id'); - if (!in_array($client_id, $allow_client_ids)) { - return false; - } - } - return true; - } - - protected static function getClientArg($name) - { - static $args = []; - - $key = 'HTTP_USER_AGENT'; - - if (isset($_SERVER['HTTP_SOCKETLOG'])) { - $key = 'HTTP_SOCKETLOG'; - } - - if (!isset($_SERVER[$key])) { - return null; - } - if (empty($args)) { - if (!preg_match('/SocketLog\((.*?)\)/', $_SERVER[$key], $match)) { - $args = ['tabid' => null]; - return null; - } - parse_str($match[1], $args); - } - if (isset($args[$name])) { - return $args[$name]; - } - return null; - } - - //设置配置 - public static function config($config) - { - $config = array_merge(self::$config, $config); - self::$config = $config; - if (self::check()) { - if ($config['optimize']) { - self::$start_time = microtime(true); - self::$start_memory = memory_get_usage(); - } - - if ($config['error_handler']) { - self::registerErrorHandler(); - } - } - } - - //获得配置 - public static function getConfig($name) - { - return isset(self::$config[$name]) ? self::$config[$name] : null; - } - - //记录日志 - public static function record($type, $msg = '', $css = '') - { - if (!self::check()) { - return; - } - - self::$logs[] = [ - 'type' => $type, - 'msg' => $msg, - 'css' => isset(self::$css[$css]) ? self::$css[$css] : $css, - ]; - } - - /** - * @param null $host - $host of socket server - * @param string $message - 发送的消息 - * @param string $address - 地址 - * @return bool - */ - public static function send($host, $message = '', $address = '/') - { - $url = 'http://' . $host . ':' . self::$port . $address; - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $message); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1); - curl_setopt($ch, CURLOPT_TIMEOUT, 10); - $headers = [ - "Content-Type: application/json;charset=UTF-8", - ]; - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //设置header - $txt = curl_exec($ch); - return true; - } - - public static function sendLog() - { - if (!self::check()) { - return; - } - - $time_str = ''; - $memory_str = ''; - if (self::$start_time) { - $runtime = microtime(true) - self::$start_time; - $reqs = number_format(1 / $runtime, 2); - $time_str = "[运行时间:{$runtime}s][吞吐率:{$reqs}req/s]"; - } - if (self::$start_memory) { - $memory_use = number_format((memory_get_usage() - self::$start_memory) / 1024, 2); - $memory_str = "[内存消耗:{$memory_use}kb]"; - } - - if (isset($_SERVER['HTTP_HOST'])) { - $current_uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; - } else { - $current_uri = "cmd:" . implode(' ', $_SERVER['argv']); - } - array_unshift(self::$logs, [ - 'type' => 'group', - 'msg' => $current_uri . $time_str . $memory_str, - 'css' => self::$css['page'], - ]); - - if (self::$config['show_included_files']) { - self::$logs[] = [ - 'type' => 'groupCollapsed', - 'msg' => 'included_files', - 'css' => '', - ]; - self::$logs[] = [ - 'type' => 'log', - 'msg' => implode("\n", get_included_files()), - 'css' => '', - ]; - self::$logs[] = [ - 'type' => 'groupEnd', - 'msg' => '', - 'css' => '', - ]; - } - - self::$logs[] = [ - 'type' => 'groupEnd', - 'msg' => '', - 'css' => '', - ]; - - $tabid = self::getClientArg('tabid'); - if (!$client_id = self::getClientArg('client_id')) { - $client_id = ''; - } - if ($force_client_id = self::$config['force_client_id']) { - $client_id = $force_client_id; - } - $logs = [ - 'tabid' => $tabid, - 'client_id' => $client_id, - 'logs' => self::$logs, - 'force_client_id' => $force_client_id, - ]; - $msg = @json_encode($logs); - $address = '/' . $client_id; //将client_id作为地址, server端通过地址判断将日志发布给谁 - self::send(self::$config['host'], $msg, $address); - - } - - public static function __callStatic($method, $args) - { - if (in_array($method, self::$log_types)) { - array_unshift($args, $method); - return call_user_func_array(['\think\Slog', 'record'], $args); - } - } - -} diff --git a/mode/common.php b/mode/common.php index 1bbb8f1e..26075e41 100644 --- a/mode/common.php +++ b/mode/common.php @@ -45,7 +45,6 @@ return [ 'think\template\driver\File' => CORE_PATH . 'template' . DS . 'driver' . DS . 'file' . EXT, 'think\log\driver\File' => CORE_PATH . 'log' . DS . 'driver' . DS . 'file' . EXT, 'think\cache\driver\File' => CORE_PATH . 'cache' . DS . 'driver' . DS . 'file' . EXT, - 'think\Slog' => CORE_PATH . 'slog'.EXT, ], ];