在php开发API接口时,记录完整的请求日志能够帮助我们快速定位接口异常、分析性能瓶颈,完整的日志需要包含请求入参、响应结果和请求耗时三个核心部分。

实现思路梳理
要实现完整的API请求日志记录,我们可以按照以下流程处理:
- 在请求开始时记录当前时间戳,作为请求起始时间
- 获取当前请求的所有入参,包括GET、POST参数以及请求头信息
- 执行正常的API业务逻辑,获取接口响应结果
- 在请求结束时再次记录时间戳,计算请求耗时
- 将入参、响应结果、耗时、请求路径等信息组装成日志内容,写入日志文件
核心代码实现
1. 基础日志类定义
我们首先定义一个专门的API日志处理类,封装日志记录的相关方法:
<?php
class ApiLogger {
// 日志文件存储目录
private $logDir;
// 请求开始时间戳(微秒级)
private $startTime;
public function __construct($logDir = './api_logs/') {
$this->logDir = rtrim($logDir, '/') . '/';
// 如果日志目录不存在则创建
if (!is_dir($this->logDir)) {
mkdir($this->logDir, 0755, true);
}
}
/**
* 记录请求开始时间
*/
public function startRecord() {
$this->startTime = microtime(true);
}
/**
* 获取请求入参
* @return array 包含请求方法、路径、参数、请求头的信息数组
*/
public function getRequestParams() {
$requestMethod = $_SERVER['REQUEST_METHOD'];
$requestPath = $_SERVER['REQUEST_URI'];
// 获取GET参数
$getParams = $_GET;
// 获取POST参数,兼容json格式入参
$postParams = [];
$inputContent = file_get_contents('php://input');
if (!empty($inputContent)) {
$jsonParams = json_decode($inputContent, true);
if (json_last_error() === JSON_ERROR_NONE) {
$postParams = $jsonParams;
} else {
parse_str($inputContent, $postParams);
}
}
if (empty($postParams)) {
$postParams = $_POST;
}
// 获取部分常用请求头
$headers = [];
$headerKeys = ['HTTP_USER_AGENT', 'HTTP_ACCEPT', 'HTTP_CONTENT_TYPE', 'HTTP_AUTHORIZATION'];
foreach ($headerKeys as $key) {
if (isset($_SERVER[$key])) {
$headerName = str_replace('HTTP_', '', $key);
$headerName = str_replace('_', '-', $headerName);
$headers[$headerName] = $_SERVER[$key];
}
}
return [
'method' => $requestMethod,
'path' => $requestPath,
'get_params' => $getParams,
'post_params' => $postParams,
'headers' => $headers
];
}
/**
* 记录完整日志
* @param mixed $response 接口响应结果
* @param array $requestInfo 请求入参信息
*/
public function saveLog($response, $requestInfo) {
if (empty($this->startTime)) {
return;
}
$endTime = microtime(true);
$costTime = round(($endTime - $this->startTime) * 1000, 2); // 转换为毫秒
// 组装日志内容
$logData = [
'log_time' => date('Y-m-d H:i:s'),
'cost_time_ms' => $costTime,
'request_info' => $requestInfo,
'response' => $response
];
$logContent = json_encode($logData, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
// 按日期生成日志文件名
$logFileName = $this->logDir . date('Y-m-d') . '_api.log';
// 写入日志,追加模式
file_put_contents($logFileName, $logContent . PHP_EOL . '---LOG_SPLIT---' . PHP_EOL, FILE_APPEND);
}
}
?>
2. API接口中使用日志类
在具体的API接口逻辑中,我们只需要在请求开始时初始化日志类,请求结束时保存日志即可:
<?php
// 引入日志类文件
require_once 'ApiLogger.php';
// 初始化日志类
$logger = new ApiLogger();
// 记录请求开始
$logger->startRecord();
// 获取请求入参
$requestInfo = $logger->getRequestParams();
// 模拟API业务逻辑处理
$response = [
'code' => 200,
'msg' => '请求成功',
'data' => [
'user_id' => 1001,
'user_name' => '测试用户'
]
];
// 模拟业务处理耗时
usleep(50000); // 暂停50毫秒
// 保存API请求日志
$logger->saveLog($response, $requestInfo);
// 返回响应结果
header('Content-Type: application/json; charset=utf-8');
echo json_encode($response, JSON_UNESCAPED_UNICODE);
?>
注意事项
- 日志目录需要保证php进程有写入权限,否则会导致日志写入失败
- 如果接口响应结果包含二进制内容或者超大文件,建议只记录响应状态码和关键标识,避免日志文件过大
- 生产环境可以对日志内容做脱敏处理,比如隐藏用户手机号、身份证号等敏感信息
- 如果API请求量较大,可以考虑按小时拆分日志文件,或者引入专业的日志收集工具如ELK进行统一管理
日志效果示例
生成的日志内容格式如下,包含完整的请求信息和耗时:
{
"log_time": "2024-05-20 14:30:22",
"cost_time_ms": 52.3,
"request_info": {
"method": "POST",
"path": "/api/user/info",
"get_params": [],
"post_params": {
"user_id": 1001
},
"headers": {
"USER-AGENT": "PostmanRuntime/7.29.2",
"ACCEPT": "*/*",
"CONTENT-TYPE": "application/json",
"AUTHORIZATION": "Bearer test_token_123"
}
},
"response": {
"code": 200,
"msg": "请求成功",
"data": {
"user_id": 1001,
"user_name": "测试用户"
}
}
}
---LOG_SPLIT---