$appId, 'request_id' => $this->generateRequestId(), 'method' => $request->getMethod(), 'uri' => $request->getRequestUri(), 'headers' => $this->sanitizeHeaders($request->headers->all()), 'query_params' => $request->query->all(), 'body' => $this->sanitizeBody($request->getContent()), 'response_status' => $response['status'] ?? 200, 'response_headers' => $response['headers'] ?? [], 'response_body' => $this->sanitizeResponseBody($response['body'] ?? ''), 'response_time' => $responseTime, 'ip_address' => $request->ip(), 'user_agent' => $request->userAgent(), 'error_message' => $errorMessage, ]); } /** * 记录认证失败日志 * * @param Request $request * @param string $reason * @return OpenApiLog */ public function logAuthFailure(Request $request, string $reason): OpenApiLog { return OpenApiLog::create([ 'app_id' => 'UNKNOWN', 'request_id' => $this->generateRequestId(), 'method' => $request->getMethod(), 'uri' => $request->getRequestUri(), 'headers' => $this->sanitizeHeaders($request->headers->all()), 'query_params' => $request->query->all(), 'body' => $this->sanitizeBody($request->getContent()), 'response_status' => 401, 'response_headers' => [], 'response_body' => json_encode(['error' => 'unauthorized', 'message' => $reason]), 'response_time' => 0, 'ip_address' => $request->ip(), 'user_agent' => $request->userAgent(), 'error_message' => "认证失败: {$reason}", ]); } /** * 记录限流日志 * * @param string $appId * @param Request $request * @param string $limitType * @return OpenApiLog */ public function logRateLimit(string $appId, Request $request, string $limitType): OpenApiLog { return OpenApiLog::create([ 'app_id' => $appId, 'request_id' => $this->generateRequestId(), 'method' => $request->getMethod(), 'uri' => $request->getRequestUri(), 'headers' => $this->sanitizeHeaders($request->headers->all()), 'query_params' => $request->query->all(), 'body' => $this->sanitizeBody($request->getContent()), 'response_status' => 429, 'response_headers' => [], 'response_body' => json_encode(['error' => 'rate_limit_exceeded', 'message' => '请求频率超出限制']), 'response_time' => 0, 'ip_address' => $request->ip(), 'user_agent' => $request->userAgent(), 'error_message' => "限流触发: {$limitType}", ]); } /** * 获取应用的调用统计 * * @param string $appId * @param string $period 统计周期:day, week, month * @return array */ public function getAppStats(string $appId, string $period = 'day'): array { $startDate = match($period) { 'week' => now()->subWeek(), 'month' => now()->subMonth(), default => now()->subDay(), }; $logs = OpenApiLog::where('app_id', $appId) ->where('created_at', '>=', $startDate) ->get(); return [ 'total_requests' => $logs->count(), 'successful_requests' => $logs->where('response_status', '>=', 200)->where('response_status', '<', 300)->count(), 'client_errors' => $logs->where('response_status', '>=', 400)->where('response_status', '<', 500)->count(), 'server_errors' => $logs->where('response_status', '>=', 500)->count(), 'avg_response_time' => $logs->avg('response_time'), 'max_response_time' => $logs->max('response_time'), 'min_response_time' => $logs->min('response_time'), ]; } /** * 生成请求ID * * @return string */ protected function generateRequestId(): string { return 'req_' . Str::random(16) . '_' . time(); } /** * 清理请求头信息(移除敏感信息) * * @param array $headers * @return array */ protected function sanitizeHeaders(array $headers): array { $sensitiveHeaders = [ 'authorization', 'x-api-key', 'x-api-secret', 'cookie', 'set-cookie', ]; $sanitized = []; foreach ($headers as $key => $value) { if (in_array(strtolower($key), $sensitiveHeaders)) { $sanitized[$key] = ['***REDACTED***']; } else { $sanitized[$key] = $value; } } return $sanitized; } /** * 清理请求体(移除敏感信息) * * @param string $body * @return string */ protected function sanitizeBody(string $body): string { // 限制请求体大小 if (strlen($body) > 10240) { // 10KB return substr($body, 0, 10240) . '...[TRUNCATED]'; } // 如果是JSON,尝试移除敏感字段 $decoded = json_decode($body, true); if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) { $sensitiveFields = ['password', 'secret', 'token', 'key']; foreach ($sensitiveFields as $field) { if (isset($decoded[$field])) { $decoded[$field] = '***REDACTED***'; } } return json_encode($decoded); } return $body; } /** * 清理响应体 * * @param string $body * @return string */ protected function sanitizeResponseBody(string $body): string { // 限制响应体大小 if (strlen($body) > 10240) { // 10KB return substr($body, 0, 10240) . '...[TRUNCATED]'; } return $body; } }