logPath = WRITEPATH . 'logs/'; } public function index() { $data = [ 'title' => 'Log Files', 'logFiles' => $this->getLogFiles() ]; return view('logs/index', $data); } public function view($filename = null) { if (!$filename) return redirect()->to('/logs'); $filename = basename($filename); $filePath = $this->logPath . $filename; if (!file_exists($filePath)) return redirect()->to('/logs'); $db = \Config\Database::connect(); // 1. Fetch TPA List for the first row of tabs $tpaConfigs = $db->table('tpa_log_config')->get()->getResultArray(); // 2. Dynamically get Action buttons from table columns $allColumns = $db->getFieldNames('tpa_log_config'); $dynamicKeys = []; foreach ($allColumns as $column) { if ($column !== 'tpa_name') { // Formatting: 'claim_push_key' -> 'Claim Push' $label = str_replace(['_key', '_'], ['', ' '], $column); $dynamicKeys[$column] = ucwords($label); } } $selectedTpa = $this->request->getGet('tpa'); $selectedKey = $this->request->getGet('key'); $searchTerm = $this->request->getGet('search'); // Parse logs with current filters $logEntries = $this->parseLogFileOptimized($filePath, $tpaConfigs, $selectedTpa, $selectedKey, $searchTerm); // Date Pagination $prevFile = $nextFile = null; if (preg_match('/log-(\d{4}-\d{2}-\d{2})\.log/', $filename, $match)) { $currentDate = $match[1]; $prevD = date('Y-m-d', strtotime('-1 day', strtotime($currentDate))); $nextD = date('Y-m-d', strtotime('+1 day', strtotime($currentDate))); if (file_exists($this->logPath . "log-$prevD.log")) $prevFile = "log-$prevD.log"; if (file_exists($this->logPath . "log-$nextD.log")) $nextFile = "log-$nextD.log"; } $data = [ 'title' => 'TPA Logs: ' . $filename, 'filename' => $filename, 'logEntries' => $logEntries, 'tpaConfigs' => $tpaConfigs, 'dynamicKeys' => $dynamicKeys, // Dynamic Buttons 'selectedTpa' => $selectedTpa, 'selectedKey' => $selectedKey, 'searchTerm' => $searchTerm, 'prevFile' => $prevFile, 'nextFile' => $nextFile ]; return view('logs/view', $data); } private function parseLogFileOptimized($path, $configs, $tpaName, $keyType, $searchTerm) { $entries = []; $handle = fopen($path, 'r'); if (!$handle) return []; $filters = []; if ($tpaName) { $filters[] = $tpaName; if ($keyType) { foreach ($configs as $conf) { if ($conf['tpa_name'] === $tpaName && isset($conf[$keyType])) { $filters[] = $conf[$keyType]; } } } } if ($searchTerm) $filters[] = $searchTerm; $currentEntry = null; while (($line = fgets($handle)) !== false) { if (preg_match('/^(\w+)\s*-\s*(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s*-->\s*(.*)$/', $line, $matches)) { if ($currentEntry && $this->matchesFilters($currentEntry['message'], $filters)) { $entries[] = $currentEntry; } $currentEntry = ['level' => $matches[1], 'date' => $matches[2], 'message' => $matches[3]]; } elseif ($currentEntry !== null && trim($line) !== '') { $currentEntry['message'] .= "\n" . $line; } } if ($currentEntry && $this->matchesFilters($currentEntry['message'], $filters)) $entries[] = $currentEntry; fclose($handle); return array_reverse($entries); } private function matchesFilters($message, $filters) { if (empty($filters)) return false; foreach ($filters as $f) { if (stripos($message, $f) === false) return false; } return true; } private function getLogFiles() { $files = []; if (!is_dir($this->logPath)) return $files; $iterator = new \DirectoryIterator($this->logPath); foreach ($iterator as $fileInfo) { if ($fileInfo->isFile() && $fileInfo->getExtension() === 'log') { $files[] = [ 'name' => $fileInfo->getFilename(), 'size' => round($fileInfo->getSize() / 1024, 2) . ' KB', 'modified_date' => date('Y-m-d H:i:s', $fileInfo->getMTime()), 'ts' => $fileInfo->getMTime() ]; } } usort($files, fn($a, $b) => $b['ts'] - $a['ts']); return $files; } }