转载:https://www.firstphp.com/archives/250.html
项目的持久运行过程中,随着数据的积累或陡增,时长伴随着一些性能问题、接口响应慢等用户体验问题,当然也不排除一些写法不合理的慢查询SQL所导致。我们在排查问题时,通常都会开启慢日志查询,来通过慢查询的SQL语句来推导出业务功能,但是这里面可能存在一个问题,即在大型项目上纷繁的功能模块,产生的各种形形色色的SQL语句,而且有些业务模块功能强关联的,很难一下通过一条SQL直接定位功能所在。
在思考这个问题的时候一直在想能否有一些辅助方法,来方便快速、便捷的排查慢查询语句,查询愈慢则与之最直接的就是API响应时间愈长,于是思路顿开,通过记录每个API调用的接口访问情况,譬如客户端client_ip、rui、route、path及相应时间等等,其次还可以通过汇总统计API的调用频率,用以评估功能受众如何,岂不妙哉?而后通过laravel的前置/后置中间件的使用场景实现了整个过程,详述如下:
CREATE TABLE `access_log` ( `id` int(11) NOT NULL AUTO_INCREMENT, `enterprise_id` int(11) DEFAULT NULL, `method` char(6) DEFAULT NULL, `route` varchar(255) DEFAULT NULL, `client_ip` varchar(45) DEFAULT NULL, `params` varchar(300) DEFAULT NULL, `user_agent` varchar(100) DEFAULT NULL, `time` char(30) DEFAULT NULL, `created_at` datetime DEFAULT NULL, `updated_at` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=177 DEFAULT CHARSET=utf8mb4;
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class AccessLog extends Model { protected $table = 'access_log'; protected $guarded = ['id']; }
<?php namespace App\Http\Middleware; use Closure; define('START', microtime(true)); class BeforeMiddleware { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { return $next($request); } }
<?php namespace App\Http\Middleware; use App\Models\AccessLog; use Closure; use Illuminate\Support\Facades\DB; class AfterMiddleware { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next) { $response = $next($request); $params = $request->all(); $data = [ 'method' => $request->method(), 'route' => $request->getPathInfo(), 'client_ip' => $request->ip(), 'params' => $params ? json_encode($params) : '', 'user_agent' => $request->userAgent(), 'time' => round(microtime(true) - START, 3) ]; $authToken = $request->bearerToken(); if ($authToken) { $object = \Tymon\JWTAuth\Facades\JWTAuth::setToken($authToken)->getPayload()->get('sub'); $authInfo = object_to_array($object); if (isset($authInfo['enterprise_id'])) { $data['enterprise_id'] = $authInfo['enterprise_id']; } } DB::beginTransaction(); try { AccessLog::query()->create($data); DB::commit(); } catch (\Exception $e){ DB::rollback(); } return $response; } }
$middleware里面添加中间件:
protected $middleware = [ \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, \App\Http\Middleware\TrimStrings::class, \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, \App\Http\Middleware\TrustProxies::class, \App\Http\Middleware\EnableCrossRequestMiddleware::class, \App\Http\Middleware\AfterMiddleware::class, \App\Http\Middleware\BeforeMiddleware::class, ];