重写Log类和驱动 SockectLog纳入Log驱动

This commit is contained in:
thinkphp
2015-12-20 18:08:36 +08:00
parent 4a35f56b1b
commit 0afe9e3aad
12 changed files with 379 additions and 502 deletions

View File

@@ -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);
}
}

View File

@@ -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' => [],
],
];

View File

@@ -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']);
}

View File

@@ -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);
}
}
/**

View File

@@ -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;
}
}
}

View File

@@ -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:

View File

@@ -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);
}
}
}

View File

@@ -1,23 +1,25 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2015 http://thinkphp.cn All rights reserved.
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
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);
}
}

View File

@@ -1,19 +1,20 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: luofei614 <weibo.com/luofei614>
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
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
}
}
}

View File

@@ -0,0 +1,185 @@
<?php
/**
* github: https://github.com/luofei614/SocketLog
* @author luofei614<weibo.com/luofei614>
*/
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;
}
}

View File

@@ -1,382 +0,0 @@
<?php
/**
* github: https://github.com/luofei614/SocketLog
* @author luofei614<weibo.com/luofei614>
*/
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);
}
}
}

View File

@@ -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,
],
];