本文目录导读:

为PHP项目设置请求日志,有以下几种常用方法,从简单到完整逐步介绍:
基础方法:PHP内置错误日志
php.ini 配置:
log_errors = On error_log = /var/log/php_errors.log ; 记录所有错误级别 error_reporting = E_ALL
代码中使用 error_log():
error_log("用户请求:{$_SERVER['REQUEST_URI']}", 3, '/var/log/requests.log');
手动实现请求日志中间件
<?php
class RequestLogger {
private $logFile;
public function __construct($logFile = '/var/log/requests.log') {
$this->logFile = $logFile;
$this->ensureLogDirectory();
}
public function log($request = null, $response = null) {
$logData = [
'timestamp' => date('Y-m-d H:i:s'),
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'CLI',
'method' => $_SERVER['REQUEST_METHOD'] ?? 'CLI',
'uri' => $_SERVER['REQUEST_URI'] ?? '/',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'status_code' => http_response_code(),
'response_time_ms' => $this->getResponseTime(),
'request_body' => file_get_contents('php://input')
];
// 过滤敏感信息
$logData['request_body'] = $this->maskSensitiveData($logData['request_body']);
$logEntry = json_encode($logData) . PHP_EOL;
file_put_contents($this->logFile, $logEntry, FILE_APPEND | LOCK_EX);
}
private function getResponseTime() {
if (defined('REQUEST_START_TIME')) {
return round((microtime(true) - REQUEST_START_TIME) * 1000, 2);
}
return 0;
}
private function maskSensitiveData($data) {
$sensitive = ['password', 'token', 'credit_card', 'ssn'];
$decoded = json_decode($data, true);
if (is_array($decoded)) {
foreach ($sensitive as $key) {
if (isset($decoded[$key])) {
$decoded[$key] = str_repeat('*', strlen($decoded[$key]));
}
}
return json_encode($decoded);
}
return $data;
}
private function ensureLogDirectory() {
$dir = dirname($this->logFile);
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
}
}
// 在项目入口文件使用
define('REQUEST_START_TIME', microtime(true));
$logger = new RequestLogger();
register_shutdown_function([$logger, 'log']);
使用框架集成(以Laravel为例)
Laravel 内置日志配置:
// config/logging.php
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['daily', 'slack'],
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => 'info',
'days' => 14,
],
],
// 创建中间件
php artisan make:middleware RequestLogger
// app/Http/Middleware/RequestLogger.php
public function handle($request, Closure $next)
{
$startTime = microtime(true);
$response = $next($request);
Log::channel('daily')->info('Request logged', [
'url' => $request->fullUrl(),
'method' => $request->method(),
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
'status_code' => $response->status(),
'duration_ms' => round((microtime(true) - $startTime) * 1000, 2),
'request_data' => $this->filterSensitiveData($request->all()),
]);
return $response;
}
// 在 Kernel.php 注册中间件
protected $middleware = [
\App\Http\Middleware\RequestLogger::class,
];
使用 Monolog(独立PHP项目)
<?php
require 'vendor/autoload.php';
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Formatter\JsonFormatter;
class RequestLogger {
private $logger;
public function __construct() {
$this->logger = new Logger('request');
// 按日期轮转日志
$handler = new RotatingFileHandler('/var/log/requests.log', 30, Logger::INFO);
// JSON格式输出,方便日志分析
$handler->setFormatter(new JsonFormatter());
$this->logger->pushHandler($handler);
}
public function logRequest() {
$this->logger->info('HTTP Request', [
'timestamp' => date('c'),
'method' => $_SERVER['REQUEST_METHOD'],
'path' => $_SERVER['REQUEST_URI'],
'query' => $_SERVER['QUERY_STRING'],
'ip' => $_SERVER['REMOTE_ADDR'],
'headers' => getallheaders(),
'body' => $this->getRequestBody(),
]);
}
private function getRequestBody() {
$body = file_get_contents('php://input');
return mb_strlen($body) > 10000 ? substr($body, 0, 10000) . '...' : $body;
}
}
// 使用
$logger = new RequestLogger();
$logger->logRequest();
最佳实践与配置示例
综合配置文件:
<?php
// config/logging.php
return [
'request_logging' => [
'enabled' => true,
'channel' => 'request',
'format' => 'json', // json, line, structured
'fields' => [
'basic' => ['method', 'uri', 'ip'],
'extended' => ['headers', 'query_params', 'request_body'],
'performance' => ['response_time', 'memory_usage'],
],
'sensitive_fields' => [
'password', 'token', 'secret', 'credit_card', 'ssn'
],
'exclude_paths' => [
'/health', '/metrics', '/favicon.ico'
],
'max_body_size' => 10000, // 限制请求体大小
'rotation' => [
'enabled' => true,
'max_files' => 30,
'max_size' => '100MB',
],
],
];
日志管理与分析建议
- 日志轮转:使用
logrotate或框架内置功能 - 使用ELK Stack:Elasticsearch + Logstash + Kibana
- 日志级别:DEBUG < INFO < WARNING < ERROR < CRITICAL
- 不要记录敏感数据:密码、令牌、信用卡信息
- 性能考虑:异步日志写入或使用消息队列
logrotate 配置示例:
/var/log/php-requests.log {
daily
rotate 30
compress
missingok
notifempty
create 0640 www-data www-data
postrotate
/usr/bin/killall -USR1 php-fpm
endscript
}
选择哪种方法取决于你的项目规模:
- 小型项目:使用 error_log() 或文件追加
- 中型项目:使用 Monolog 或框架内置功能
- 大型项目:使用 ELK Stack 或专业的日志服务