Files
FendxPHP/app/Interceptor/LogInterceptor.php

198 lines
6.1 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
namespace App\Interceptor;
use Fendx\Core\Aop\JoinPoint;
use Fendx\Core\Aop\Advice\AroundAdvice;
/**
* 日志拦截器
*/
class LogInterceptor implements AroundAdvice
{
public function invoke(JoinPoint $joinPoint): mixed
{
$startTime = microtime(true);
$traceId = \Fendx\Core\Context\Context::getTraceId();
// 记录请求开始
$this->logRequestStart($joinPoint, $traceId);
try {
// 执行目标方法
$result = $joinPoint->proceed();
// 记录请求成功
$this->logRequestSuccess($joinPoint, $result, $startTime, $traceId);
return $result;
} catch (\Exception $e) {
// 记录请求异常
$this->logRequestException($joinPoint, $e, $startTime, $traceId);
throw $e;
}
}
/**
* 记录请求开始
*/
private function logRequestStart(JoinPoint $joinPoint, string $traceId): void
{
$request = $this->getRequest();
$method = $joinPoint->getMethod();
$class = $joinPoint->getTarget()::class;
$logData = [
'trace_id' => $traceId,
'event' => 'request_start',
'class' => $class,
'method' => $method,
'request_uri' => $request->uri(),
'request_method' => $request->method(),
'client_ip' => $request->getClientIp(),
'user_agent' => $request->userAgent(),
'timestamp' => date('Y-m-d H:i:s'),
];
// 记录请求参数(排除敏感信息)
$params = $this->filterSensitiveData($request->all());
if (!empty($params)) {
$logData['request_params'] = $params;
}
$this->writeLog('INFO', 'Request started', $logData);
}
/**
* 记录请求成功
*/
private function logRequestSuccess(JoinPoint $joinPoint, mixed $result, float $startTime, string $traceId): void
{
$executionTime = round((microtime(true) - $startTime) * 1000, 2);
$method = $joinPoint->getMethod();
$class = $joinPoint->getTarget()::class;
$logData = [
'trace_id' => $traceId,
'event' => 'request_success',
'class' => $class,
'method' => $method,
'execution_time' => $executionTime,
'memory_usage' => memory_get_usage(true),
'memory_peak' => memory_get_peak_usage(true),
'timestamp' => date('Y-m-d H:i:s'),
];
// 记录响应数据(排除敏感信息)
if ($result !== null && !is_resource($result)) {
$responseData = $this->filterSensitiveData($result);
if (is_array($responseData) && count($responseData) > 100) {
$logData['response_size'] = count($responseData);
$logData['response_type'] = gettype($result);
} else {
$logData['response_data'] = $responseData;
}
}
// 性能警告
if ($executionTime > 1000) {
$logData['performance_warning'] = 'Slow request detected';
$this->writeLog('WARNING', 'Slow request completed', $logData);
} else {
$this->writeLog('INFO', 'Request completed successfully', $logData);
}
}
/**
* 记录请求异常
*/
private function logRequestException(JoinPoint $joinPoint, \Exception $e, float $startTime, string $traceId): void
{
$executionTime = round((microtime(true) - $startTime) * 1000, 2);
$method = $joinPoint->getMethod();
$class = $joinPoint->getTarget()::class;
$logData = [
'trace_id' => $traceId,
'event' => 'request_exception',
'class' => $class,
'method' => $method,
'execution_time' => $executionTime,
'exception_type' => get_class($e),
'exception_code' => $e->getCode(),
'exception_message' => $e->getMessage(),
'exception_file' => $e->getFile(),
'exception_line' => $e->getLine(),
'stack_trace' => $e->getTraceAsString(),
'timestamp' => date('Y-m-d H:i:s'),
];
$this->writeLog('ERROR', 'Request failed with exception', $logData);
}
/**
* 获取当前请求对象
*/
private function getRequest(): \Fendx\Web\Request\Request
{
return \Fendx\Web\Request\Request::createFromGlobals();
}
/**
* 过滤敏感数据
*/
private function filterSensitiveData(mixed $data): mixed
{
if (!is_array($data)) {
return $data;
}
$sensitiveFields = [
'password', 'passwd', 'secret', 'token', 'key',
'credit_card', 'bank_account', 'ssn', 'id_card'
];
$filtered = [];
foreach ($data as $key => $value) {
if (in_array(strtolower($key), $sensitiveFields)) {
$filtered[$key] = '***';
} elseif (is_array($value)) {
$filtered[$key] = $this->filterSensitiveData($value);
} else {
$filtered[$key] = $value;
}
}
return $filtered;
}
/**
* 写入日志
*/
private function writeLog(string $level, string $message, array $data): void
{
$logEntry = [
'level' => $level,
'message' => $message,
'data' => $data,
];
// 这里应该调用实际的日志系统
// Log::write($level, $message, $data);
// 临时实现:写入文件
$logFile = runtime_path('logs/app_' . date('Y-m-d') . '.log');
$logLine = date('Y-m-d H:i:s') . " [{$level}] {$message} " . json_encode($logEntry, JSON_UNESCAPED_UNICODE) . PHP_EOL;
$logDir = dirname($logFile);
if (!is_dir($logDir)) {
mkdir($logDir, 0755, true);
}
file_put_contents($logFile, $logLine, FILE_APPEND | LOCK_EX);
}
}