'apilog', 'title' => 'API访问监测分析', 'icon' => 'fa fa-pie-chart', 'ismenu' => 1, 'sublist' => [ [ "name" => "apilog/data", "title" => "基础数据", "ismenu" => 1, "icon" => "fa fa-dashboard", 'sublist' => [ ['name' => 'apilog/data/index', 'title' => '查看'], ] ], [ "name" => "apilog/trend", "title" => "趋势数据", "ismenu" => 1, "icon" => "fa fa-area-chart", 'sublist' => [ ['name' => 'apilog/trend/index', 'title' => '查看'], ] ], [ "name" => "apilog/index", "title" => "请求列表", "ismenu" => 1, "icon" => "fa fa-list", 'sublist' => [ ['name' => 'apilog/index/index', 'title' => '查看'], ['name' => 'apilog/index/del', 'title' => '删除'], ['name' => 'apilog/index/detail', 'title' => '详情'], ['name' => 'apilog/index/banip', 'title' => '禁用IP'], ['name' => 'apilog/index/clear', 'title' => '清空数据'], ] ], ] ] ]; Menu::create($menu); Service::refresh(); return true; } /** * 插件卸载方法 * @return bool */ public function uninstall() { Menu::delete("apilog"); Service::refresh(); return true; } /** * 插件启用方法 * @return bool */ public function enable() { Menu::enable('apilog'); return true; } /** * 插件禁用方法 * @return bool */ public function disable() { Menu::disable("apilog"); return true; } public function responseSend(&$params) { try { if (Request::instance()->module() == "api") { $log['time'] = (microtime(true) - Request::instance()->time(true)) * 1000; $auth = Auth::instance(); $user_id = $auth->isLogin() ? $auth->id : 0; $username = $auth->isLogin() ? $auth->username : __('Unknown'); $log['url'] = substr(Request::instance()->baseUrl(), 0, 200); $log['method'] = Request::instance()->method(); $log['param'] = json_encode(Request::instance()->param()); $log['ip'] = Request::instance()->ip(); $log['ua'] = substr(Request::instance()->header('user-agent'), 0, 200); $log['controller'] = Request::instance()->controller(); $log['action'] = Request::instance()->action(); $log['code'] = $params->getCode(); $log['user_id'] = $user_id; $log['username'] = $username; $log['response'] = $params->getContent(); (new ModelApilog)->save($log); $config = get_addon_config('apilog'); //状态码记录 if ($config['error']['open'] == 1) { $count_code = Cache::get('countcode', null); if (is_null($count_code)) { Cache::set('countcode', 0, $config['error']['pl']); $tagkey = Cache::get('tag_' . md5('code')); $keys = $tagkey ? array_filter(explode(',', $tagkey)) : []; foreach ($keys as $k => $v) { Cache::rm($v); } Cache::rm($tagkey); } $count_code = Cache::inc('countcode'); $k_code = 'code:' . $params->getCode(); $yj_code = Cache::get($k_code, null); if (is_null($yj_code)) { Cache::set($k_code, 0, 0); Cache::tag('code', $k_code); } Cache::inc($k_code); $codes = array_filter(explode(',', $config['error']['sj'])); $now = 0; foreach ($codes as $k => $v) { $now += Cache::get('code:' . $v, 0); } if ($now / $count_code >= $config['error']['zb'] / 100) { // echo '触发错误预警' . $now / $count_code; $this->emailnotify($config['base']['email'], '请求错误监控', '当前api请求错误率已达到【' . round($now / $count_code * 100, 2) . '%】,请及时关注!'); } } //超时记录数 if ($config['time']['open'] == 1) { $count_time = Cache::get('counttime', null); if (is_null($count_time)) { Cache::set('counttime', 0, $config['time']['pl']); Cache::rm('time'); } $tot_time = Cache::inc('counttime'); if ($log['time'] > $config['time']['sj']) { $yj_time = Cache::get('time', null); if (is_null($yj_time)) { Cache::set('time', 0, 0); } $now_time = Cache::inc('time'); if ($now_time / $tot_time >= $config['time']['zb'] / 100) { // echo '触发超时预警' . $now_time / $tot_time; $this->emailnotify($config['base']['email'], '响应超时监控', '当前api响应超时请求占比已达到【' . round($now_time / $tot_time * 100, 2) . '%】,请及时关注!'); } } } } } catch (\Exception $e) { //宁可不记录也不能影响api的正常访问 } } public function moduleInit(&$params) { try { if (Request::instance()->module() == "api") { $ip = 'banip:' . Request::instance()->ip(); $cacheIp = Cache::get($ip); if ($cacheIp !== false) { $this->respone(500, '抱歉,您的IP已被禁止访问'); } $config = get_addon_config('apilog'); //总请求数 if ($config['count']['open'] == 1) { $yj_count = Cache::get('count', null); if (is_null($yj_count)) { Cache::set('count', 0, $config['count']['pl']); } Cache::inc('count'); //预警 if ($yj_count + 1 >= $config['count']['max']) { Cache::rm('count'); // $this->respone(500, '触发请求量预警'); $this->emailnotify($config['base']['email'], '请求量监控', '当前最大请求数量已达到【' . ++$yj_count . '次】,请及时关注!'); } } //IP访问请求数 if ($config['ip']['open'] == 1) { $count_ip = Cache::get('countip', null); if (is_null($count_ip)) { Cache::set('countip', 0, $config['ip']['pl']); $tagkey = Cache::get('tag_' . md5('ip')); $keys = $tagkey ? array_filter(explode(',', $tagkey)) : []; foreach ($keys as $k => $v) { Cache::rm($v); } Cache::rm($tagkey); } $count_ip = Cache::inc('countip'); $k_ip = 'ip:' . Request::instance()->ip(); $yj_ip = Cache::get($k_ip, null); if (is_null($yj_ip)) { Cache::set($k_ip, 0, 0); Cache::tag('ip', $k_ip); } $this_ip = Cache::inc($k_ip); //白名单 $white = array_filter(explode(',', $config['ip']['white'])); //预警 if (!in_array(Request::instance()->ip(), $white) && $this_ip / $count_ip >= $config['ip']['zb'] / 100) { //$this->respone(500, '触发IP预警'); $this->emailnotify($config['base']['email'], 'IP异常监控', 'IP【' . Request::instance()->ip() . '】的访问请求占比已达到【' . round($this_ip / $count_ip * 100, 2) . '%】,请及时关注!'); } } } } catch (\Exception $e) { //宁可不记录也不能影响api的正常访问 } } protected function respone($code, $msg) { $result = [ 'code' => $code, 'msg' => $msg, 'time' => Request::instance()->server('REQUEST_TIME'), 'data' => null, ]; $type = Request::instance()->param(config('var_jsonp_handler')) ? 'jsonp' : 'json'; $response = \think\Response::create($result, $type, 500); throw new \think\exception\HttpResponseException($response); } /** * 发送邮件预警 * 同类型预警半小时最多发一次 */ protected function emailnotify($receiver, $subject, $content) { $cache = Cache::get('notify:' . $subject, null); if (is_null($cache)) { $email = new Email; $result = $email ->to($receiver) ->subject('【API预警】' . $subject) ->message('