correlationId = $correlationId ?? $_SERVER['HTTP_X_CORRELATION_ID'] ?? $_SERVER['CORRELATION_ID'] ?? $this->generateCorrelationId(); $this->db = DatabaseFactory::dev(); } /** * Generate a unique correlation ID. */ private function generateCorrelationId(): string { return bin2hex(random_bytes(8)); } /** * Get the current correlation ID. */ public function getCorrelationId(): string { return $this->correlationId; } /** * Log a generic event. * * @param string $event Event name (e.g., "user.login", "chat.message.sent") * @param array $context Additional context data * @param string $level Log level: debug, info, warning, error */ public function log(string $event, array $context = [], string $level = 'info'): void { if (!in_array($level, self::LOG_LEVELS, true)) { $level = 'info'; } $stmt = $this->db->prepare(' INSERT INTO audit_log (correlation_id, event, context, level, user_id, ip_address, created_at) VALUES (?, ?, ?, ?, ?, ?, NOW()) '); $stmt->execute([ $this->correlationId, $event, json_encode($context, JSON_UNESCAPED_UNICODE), $level, $this->getUserId(), $this->getIpAddress(), ]); } /** * Log an entity action (CRUD operation). * * @param string $action Action performed: create, read, update, delete, etc. * @param string $entity Entity type: user, task, content, etc. * @param int|null $entityId Entity ID (null for create before ID is known) * @param array $data Additional data about the action */ public function logAction( string $action, string $entity, ?int $entityId = null, array $data = [] ): void { $event = "{$entity}.{$action}"; $stmt = $this->db->prepare(' INSERT INTO audit_log (correlation_id, event, entity_type, entity_id, context, level, user_id, ip_address, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW()) '); $stmt->execute([ $this->correlationId, $event, $entity, $entityId, json_encode($data, JSON_UNESCAPED_UNICODE), 'info', $this->getUserId(), $this->getIpAddress(), ]); } /** * Log an error event. */ public function logError(string $event, string $message, array $context = []): void { $context['error_message'] = $message; $this->log($event, $context, 'error'); } /** * Log a warning event. */ public function logWarning(string $event, string $message, array $context = []): void { $context['warning_message'] = $message; $this->log($event, $context, 'warning'); } /** * Log debug information (only in development). */ public function debug(string $event, array $context = []): void { // Only log debug in development if ($_ENV['APP_ENV'] ?? 'production' === 'development') { $this->log($event, $context, 'debug'); } } /** * Get the current user ID from session. */ private function getUserId(): ?int { return isset($_SESSION['user_id']) ? (int) $_SESSION['user_id'] : null; } /** * Get the client IP address. */ private function getIpAddress(): ?string { return $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'] ?? null; } /** * Query logs by correlation ID. * * @return array> */ public function getByCorrelationId(string $correlationId): array { $stmt = $this->db->prepare(' SELECT * FROM audit_log WHERE correlation_id = ? ORDER BY created_at ASC '); $stmt->execute([$correlationId]); return $stmt->fetchAll(PDO::FETCH_ASSOC); } /** * Query recent logs for an entity. * * @return array> */ public function getByEntity(string $entityType, int $entityId, int $limit = 50): array { $stmt = $this->db->prepare(' SELECT * FROM audit_log WHERE entity_type = ? AND entity_id = ? ORDER BY created_at DESC LIMIT ? '); $stmt->execute([$entityType, $entityId, $limit]); return $stmt->fetchAll(PDO::FETCH_ASSOC); } }