Socket.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: luofei614 <weibo.com/luofei614>
  10. // +----------------------------------------------------------------------
  11. namespace think\log\driver;
  12. use think\App;
  13. /**
  14. * github: https://github.com/luofei614/SocketLog
  15. * @author luofei614<weibo.com/luofei614>
  16. */
  17. class Socket
  18. {
  19. public $port = 1116; //SocketLog 服务的http的端口号
  20. protected $config = [
  21. // socket服务器地址
  22. 'host' => 'localhost',
  23. // 是否显示加载的文件列表
  24. 'show_included_files' => false,
  25. // 日志强制记录到配置的client_id
  26. 'force_client_ids' => [],
  27. // 限制允许读取日志的client_id
  28. 'allow_client_ids' => [],
  29. //输出到浏览器默认展开的日志级别
  30. 'expand_level' => ['debug'],
  31. ];
  32. protected $css = [
  33. 'sql' => 'color:#009bb4;',
  34. 'sql_warn' => 'color:#009bb4;font-size:14px;',
  35. 'error' => 'color:#f4006b;font-size:14px;',
  36. 'page' => 'color:#40e2ff;background:#171717;',
  37. 'big' => 'font-size:20px;color:red;',
  38. ];
  39. protected $allowForceClientIds = []; //配置强制推送且被授权的client_id
  40. protected $app;
  41. /**
  42. * 架构函数
  43. * @access public
  44. * @param array $config 缓存参数
  45. */
  46. public function __construct(App $app, array $config = [])
  47. {
  48. $this->app = $app;
  49. if (!empty($config)) {
  50. $this->config = array_merge($this->config, $config);
  51. }
  52. }
  53. /**
  54. * 调试输出接口
  55. * @access public
  56. * @param array $log 日志信息
  57. * @return bool
  58. */
  59. public function save(array $log = [], $append = false)
  60. {
  61. if (!$this->check()) {
  62. return false;
  63. }
  64. $trace = [];
  65. if ($this->app->isDebug()) {
  66. $runtime = round(microtime(true) - $this->app->getBeginTime(), 10);
  67. $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
  68. $time_str = ' [运行时间:' . number_format($runtime, 6) . 's][吞吐率:' . $reqs . 'req/s]';
  69. $memory_use = number_format((memory_get_usage() - $this->app->getBeginMem()) / 1024, 2);
  70. $memory_str = ' [内存消耗:' . $memory_use . 'kb]';
  71. $file_load = ' [文件加载:' . count(get_included_files()) . ']';
  72. if (isset($_SERVER['HTTP_HOST'])) {
  73. $current_uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
  74. } else {
  75. $current_uri = 'cmd:' . implode(' ', $_SERVER['argv']);
  76. }
  77. // 基本信息
  78. $trace[] = [
  79. 'type' => 'group',
  80. 'msg' => $current_uri . $time_str . $memory_str . $file_load,
  81. 'css' => $this->css['page'],
  82. ];
  83. }
  84. foreach ($log as $type => $val) {
  85. $trace[] = [
  86. 'type' => in_array($type, $this->config['expand_level']) ? 'group' : 'groupCollapsed',
  87. 'msg' => '[ ' . $type . ' ]',
  88. 'css' => isset($this->css[$type]) ? $this->css[$type] : '',
  89. ];
  90. foreach ($val as $msg) {
  91. if (!is_string($msg)) {
  92. $msg = var_export($msg, true);
  93. }
  94. $trace[] = [
  95. 'type' => 'log',
  96. 'msg' => $msg,
  97. 'css' => '',
  98. ];
  99. }
  100. $trace[] = [
  101. 'type' => 'groupEnd',
  102. 'msg' => '',
  103. 'css' => '',
  104. ];
  105. }
  106. if ($this->config['show_included_files']) {
  107. $trace[] = [
  108. 'type' => 'groupCollapsed',
  109. 'msg' => '[ file ]',
  110. 'css' => '',
  111. ];
  112. $trace[] = [
  113. 'type' => 'log',
  114. 'msg' => implode("\n", get_included_files()),
  115. 'css' => '',
  116. ];
  117. $trace[] = [
  118. 'type' => 'groupEnd',
  119. 'msg' => '',
  120. 'css' => '',
  121. ];
  122. }
  123. $trace[] = [
  124. 'type' => 'groupEnd',
  125. 'msg' => '',
  126. 'css' => '',
  127. ];
  128. $tabid = $this->getClientArg('tabid');
  129. if (!$client_id = $this->getClientArg('client_id')) {
  130. $client_id = '';
  131. }
  132. if (!empty($this->allowForceClientIds)) {
  133. //强制推送到多个client_id
  134. foreach ($this->allowForceClientIds as $force_client_id) {
  135. $client_id = $force_client_id;
  136. $this->sendToClient($tabid, $client_id, $trace, $force_client_id);
  137. }
  138. } else {
  139. $this->sendToClient($tabid, $client_id, $trace, '');
  140. }
  141. return true;
  142. }
  143. /**
  144. * 发送给指定客户端
  145. * @access protected
  146. * @author Zjmainstay
  147. * @param $tabid
  148. * @param $client_id
  149. * @param $logs
  150. * @param $force_client_id
  151. */
  152. protected function sendToClient($tabid, $client_id, $logs, $force_client_id)
  153. {
  154. $logs = [
  155. 'tabid' => $tabid,
  156. 'client_id' => $client_id,
  157. 'logs' => $logs,
  158. 'force_client_id' => $force_client_id,
  159. ];
  160. $msg = @json_encode($logs);
  161. $address = '/' . $client_id; //将client_id作为地址, server端通过地址判断将日志发布给谁
  162. $this->send($this->config['host'], $msg, $address);
  163. }
  164. protected function check()
  165. {
  166. $tabid = $this->getClientArg('tabid');
  167. //是否记录日志的检查
  168. if (!$tabid && !$this->config['force_client_ids']) {
  169. return false;
  170. }
  171. //用户认证
  172. $allow_client_ids = $this->config['allow_client_ids'];
  173. if (!empty($allow_client_ids)) {
  174. //通过数组交集得出授权强制推送的client_id
  175. $this->allowForceClientIds = array_intersect($allow_client_ids, $this->config['force_client_ids']);
  176. if (!$tabid && count($this->allowForceClientIds)) {
  177. return true;
  178. }
  179. $client_id = $this->getClientArg('client_id');
  180. if (!in_array($client_id, $allow_client_ids)) {
  181. return false;
  182. }
  183. } else {
  184. $this->allowForceClientIds = $this->config['force_client_ids'];
  185. }
  186. return true;
  187. }
  188. protected function getClientArg($name)
  189. {
  190. static $args = [];
  191. $key = 'HTTP_USER_AGENT';
  192. if (isset($_SERVER['HTTP_SOCKETLOG'])) {
  193. $key = 'HTTP_SOCKETLOG';
  194. }
  195. if (!isset($_SERVER[$key])) {
  196. return;
  197. }
  198. if (empty($args)) {
  199. if (!preg_match('/SocketLog\((.*?)\)/', $_SERVER[$key], $match)) {
  200. $args = ['tabid' => null];
  201. return;
  202. }
  203. parse_str($match[1], $args);
  204. }
  205. if (isset($args[$name])) {
  206. return $args[$name];
  207. }
  208. return;
  209. }
  210. /**
  211. * @access protected
  212. * @param string $host - $host of socket server
  213. * @param string $message - 发送的消息
  214. * @param string $address - 地址
  215. * @return bool
  216. */
  217. protected function send($host, $message = '', $address = '/')
  218. {
  219. $url = 'http://' . $host . ':' . $this->port . $address;
  220. $ch = curl_init();
  221. curl_setopt($ch, CURLOPT_URL, $url);
  222. curl_setopt($ch, CURLOPT_POST, true);
  223. curl_setopt($ch, CURLOPT_POSTFIELDS, $message);
  224. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  225. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1);
  226. curl_setopt($ch, CURLOPT_TIMEOUT, 10);
  227. $headers = [
  228. "Content-Type: application/json;charset=UTF-8",
  229. ];
  230. curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); //设置header
  231. return curl_exec($ch);
  232. }
  233. }