在一个项目中,不是简简单单做好请求处理与响应就可以了,我们还需要完整的记录用户的所有行为,以方便我们掌握程序是否存在漏洞和bug,比如我们使用Laravel框架开发项目时,可以在中间件上记录用户的请求与服务器给出对应的响应数据。
这里假设你已经安装好环境和Laravel框架。
1.创建中间件
进入Laravel项目根目录,执行如下:
php artisan make:middleware UserRequestAndResponse
如果不报错的话,会提示成功如下:
Middleware created successfully.
2. 自定义日志类
进入app目录,新建文件夹 Utils
cd app && mkdir Utils
新建PHP类文件CustomLogger:
<?php
namespace App\Utils;
use Illuminate\Support\Facades\Log;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
class CustomLogger
{
/**
* @param $message
* @param array $data
* @param string $filename
* @param string $type
*/
private static function _save($message, $data = [], $filename = 'log' , $type = 'info')
{
try{
$dir_name = $filename;
$log = new Logger('req_res');
if (PHP_SAPI == 'cli') {
// 命令行访问脚本的,加一个cli标识和用户浏览器访问的区分开
$filename .= '_cli';
}
$filename = date('Y-m-d') . '-'.$filename .'.log';
$path = storage_path('logs/'.$dir_name);
// 有时候运维没给号权限,容易导致写入日志失败
self::mkDirs($path);
$path = $path . '/' . $filename;
if (gettype($data) != 'array') {
$message .= ":" . $data;
$data = [];
}
if($type == 'info'){
$log->pushHandler(new StreamHandler($path, Logger::INFO));
$log->addInfo($message, $data);
}else{
$log->pushHandler(new StreamHandler($path, Logger::ERROR));
$log->addError($message , $data);
}
}catch (\Exception $e){
Log::info("日志记录错误:".$e->getMessage());
}
}
/**
* @param $message
* @param array $data
* @param string $filename
*/
public static function info($message, $data = [], $filename = 'info')
{
self::_save($message, $data, $filename);
}
/**
* @param $message
* @param array $data
* @param string $filename
*/
public static function error($message, $data = [], $filename = 'error')
{
// 错误日志不会太多,按单文件记录可以了
self::_save($message, $data, $filename);
}
/**
* 给日志文件夹权限
*
* @param $dir
* @param int $mode
* @return bool
*/
public static function mkDirs($dir, $mode = 0777)
{
if (is_dir($dir) || @mkdir($dir, $mode)) {
return TRUE;
}
if (!self::mkdirs(dirname($dir), $mode)) {
return FALSE;
}
return @mkdir($dir, $mode);
}
CustomLogger日志类的使用如下:
use App\Utils\CustomLogger;
/**
* 定义日志名
*/
const LOG_NAME = 'req_res';
# 在方法中使用
CustomLogger::info($message , $data , self::LOG_NAME);
CustomLogger::error($message , $data , self::LOG_NAME);
3. 编辑UserRequestAndResponse中间件
文件位置:
app/Http/Middleware/UserRequestAndResponse.php
<?php
namespace App\Http\Middleware;
use App\Utils\ReqResLogger;
use Closure;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
class UserRequestAndResponse
{
protected $sequence;
protected $code;
const LOG_NAME = 'req_res';
/**
* 记录请求与响应数据
*
* UserRequestAndResponse constructor.
* @param Request $request
*/
public function __construct(Request $request)
{
$this->code = $request->path();
$this->sequence = str_pad(microtime(true), 15, 0);
}
/**
* 处理主程序.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
ReqResLogger::info("======================================" , [] , self::LOG_NAME);
$this->request($request);
$response = $next($request);
if ($response instanceof JsonResponse) {
$this->response($response);
}
$this->time();
ReqResLogger::info("======================================" , [] , self::LOG_NAME); return $response;
}
/**
* 时间计算
*
*/
protected function time():void
{
$start = str_pad(LARAVEL_START, 15, 0);
$end = str_pad(microtime(true), 15, 0);
$standard = config('develop.runtime-lower-limit');
$time = ($end - LARAVEL_START) * 1000;
switch (true) {
case $time > $standard:
$mark = '超过[>]';
$diff = $time - $standard;
break;
case $time === $standard:
$mark = '持平[=]';
$diff = 0;
break;
default:
$mark = '低于[<]';
$diff = $time - $standard;
}
$message = "请求序号[{$this->sequence}] 请求接口[{$this->code}] 开始时间[{$start}] 结束时间[{$end}] 运行时间[{$time}] {$mark}[{$standard}] {$diff}毫秒";
ReqResLogger::info($message ,[], self::LOG_NAME); }
/**
* 请求记录
*
* @param $request
*/
protected function request($request):void
{
$input = $request->input();
if (array_key_exists('data', $input)) {
$input['data'] = json_decode($input['data'], true);
}
ReqResLogger::info("请求数据:" ,$input, self::LOG_NAME); }
/**
* 响应记录
*
* @param JsonResponse $response
*/
protected function response(JsonResponse $response):void
{
$output = json_decode($response->getContent(), true);
if (json_last_error() === JSON_ERROR_NONE) {
ReqResLogger::info("响应数据:" ,$output, self::LOG_NAME); }
}
}
4. 编辑Kernel文件
文件位置:
app/Http/Kernel.php
protected $middleware = [
\App\Http\Middleware\TrustProxies::class,
\App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
+ \App\Http\Middleware\UserRequestAndResponse::class];
end.至此配置已经结束
当访问项目的时候,会在storage/logs目录下新建一个目录res_req,并在该目录下生成一个日志文件xxxx-xx-xx-res_req.log
试试吧,以后用户的请求与服务器的响应就有个记录了,长吐一口气......心里踏实了。