From 2552ca9511959e1ba89cd5af302a3ecfbc764392 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Wed, 22 Apr 2026 20:44:39 +0700 Subject: [PATCH] feat: implement system log detail view with dynamic status badges and optimize log file retrieval performance --- .../Admin/System/LogController.php | 22 +++----------- resources/js/lib/log-helpers.ts | 18 ++++++++++++ .../js/pages/admin/system/logs/index.tsx | 4 ++- .../admin/system/logs/partials/columns.tsx | 29 ++----------------- .../system/logs/partials/log-detail-modal.tsx | 5 ++-- resources/js/types/index.ts | 1 + resources/js/types/system-log.ts | 7 +++++ 7 files changed, 38 insertions(+), 48 deletions(-) create mode 100644 resources/js/lib/log-helpers.ts create mode 100644 resources/js/types/system-log.ts diff --git a/app/Http/Controllers/Admin/System/LogController.php b/app/Http/Controllers/Admin/System/LogController.php index d50c6ec..116cdf4 100644 --- a/app/Http/Controllers/Admin/System/LogController.php +++ b/app/Http/Controllers/Admin/System/LogController.php @@ -10,14 +10,10 @@ class LogController extends Controller { - /** - * Display a listing of log entries. - */ public function index(Request $request): Response { $logFiles = $this->getLogFiles(); - // Default to the latest log file if not specified or doesn't exist $selectedFile = $request->input('file'); if (! $selectedFile || ! in_array($selectedFile, $logFiles)) { @@ -33,9 +29,6 @@ public function index(Request $request): Response ]); } - /** - * Get all .log files from storage/logs. - */ private function getLogFiles(): array { $path = storage_path('logs'); @@ -46,16 +39,14 @@ private function getLogFiles(): array $files = File::files($path); return collect($files) - ->map(fn ($file) => $file->getFilename()) - ->filter(fn ($filename) => str_ends_with($filename, '.log')) - ->sortDesc() // Latest dates first + ->map(fn($file) => $file->getFilename()) + ->filter(fn($filename) => str_ends_with($filename, '.log')) + ->sortDesc() + ->take(10) ->values() ->toArray(); } - /** - * Parse a log file into an array of entries. - */ private function parseLogFile($filename): array { $path = storage_path("logs/{$filename}"); @@ -63,12 +54,8 @@ private function parseLogFile($filename): array return []; } - // Limit file reading to prevent memory issues with huge logs - // For simplicity, we read the whole file here, but in production consider chunking. $content = File::get($path); - // Pattern: [timestamp] environment.LEVEL: Message - // Handles multi-line messages (like stack traces) using lookahead $pattern = '/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (\w+)\.(\w+): ([\s\S]*?)(?=\n^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\]|$)/m'; preg_match_all($pattern, $content, $matches, PREG_SET_ORDER); @@ -84,7 +71,6 @@ private function parseLogFile($filename): array ]; } - // Return latest logs first return array_reverse($logs); } } diff --git a/resources/js/lib/log-helpers.ts b/resources/js/lib/log-helpers.ts new file mode 100644 index 0000000..4f3b802 --- /dev/null +++ b/resources/js/lib/log-helpers.ts @@ -0,0 +1,18 @@ +export const getLevelColor = (level: string) => { + switch (level.toUpperCase()) { + case 'EMERGENCY': + case 'ALERT': + case 'CRITICAL': + case 'ERROR': + return 'destructive'; + case 'WARNING': + return 'outline'; + case 'NOTICE': + case 'INFO': + return 'secondary'; + case 'DEBUG': + return 'outline'; + default: + return 'default'; + } +} diff --git a/resources/js/pages/admin/system/logs/index.tsx b/resources/js/pages/admin/system/logs/index.tsx index aa2bd66..44367a7 100644 --- a/resources/js/pages/admin/system/logs/index.tsx +++ b/resources/js/pages/admin/system/logs/index.tsx @@ -2,7 +2,8 @@ import { Head, router } from '@inertiajs/react'; import { Card, CardContent } from '@/components/ui/card'; import { DataTable } from '@/components/data-table'; import { useState } from 'react'; -import { getColumns, SystemLog } from './partials/columns'; +import { getColumns } from './partials/columns'; +import { SystemLog } from '@/types'; import system from '@/routes/system'; import { Select, @@ -78,6 +79,7 @@ export default function SystemLogIndex({ logFiles, selectedFile, logs }: Props) ] } ]} + showSelection={false} /> diff --git a/resources/js/pages/admin/system/logs/partials/columns.tsx b/resources/js/pages/admin/system/logs/partials/columns.tsx index ee749ad..deb09b5 100644 --- a/resources/js/pages/admin/system/logs/partials/columns.tsx +++ b/resources/js/pages/admin/system/logs/partials/columns.tsx @@ -4,38 +4,13 @@ import { Badge } from '@/components/ui/badge'; import { Eye } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; - -export interface SystemLog { - id: number; - timestamp: string; - env: string; - level: string; - message: string; -} +import { SystemLog } from '@/types'; +import { getLevelColor } from '@/lib/log-helpers'; interface ColumnProps { onView: (log: SystemLog) => void; } -const getLevelColor = (level: string) => { - switch (level.toUpperCase()) { - case 'EMERGENCY': - case 'ALERT': - case 'CRITICAL': - case 'ERROR': - return 'destructive'; - case 'WARNING': - return 'outline'; // Fallback if no warning variant - case 'NOTICE': - case 'INFO': - return 'secondary'; - case 'DEBUG': - return 'outline'; - default: - return 'default'; - } -} - export const getColumns = ({ onView }: ColumnProps): ColumnDef[] => [ { accessorKey: "timestamp", diff --git a/resources/js/pages/admin/system/logs/partials/log-detail-modal.tsx b/resources/js/pages/admin/system/logs/partials/log-detail-modal.tsx index fe2c443..9c28c6d 100644 --- a/resources/js/pages/admin/system/logs/partials/log-detail-modal.tsx +++ b/resources/js/pages/admin/system/logs/partials/log-detail-modal.tsx @@ -5,7 +5,8 @@ import { DialogTitle, } from "@/components/ui/dialog" import { Badge } from "@/components/ui/badge" -import { SystemLog } from "./columns" +import { SystemLog } from "@/types" +import { getLevelColor } from "@/lib/log-helpers" interface Props { log: SystemLog | null; @@ -21,7 +22,7 @@ export function LogDetailModal({ log, onClose }: Props) {
Detail Log - {log.level} + {log.level} {log.timestamp}
diff --git a/resources/js/types/index.ts b/resources/js/types/index.ts index cbd200d..2e23a54 100644 --- a/resources/js/types/index.ts +++ b/resources/js/types/index.ts @@ -8,4 +8,5 @@ export type * from './payroll'; export type * from './purchase'; export type * from './general-setting'; export type * from './activity'; +export type * from './system-log'; diff --git a/resources/js/types/system-log.ts b/resources/js/types/system-log.ts new file mode 100644 index 0000000..77787c4 --- /dev/null +++ b/resources/js/types/system-log.ts @@ -0,0 +1,7 @@ +export interface SystemLog { + id: number; + timestamp: string; + env: string; + level: string; + message: string; +}