- 필터 적용 시 오래된 순 정렬 (시간순 추적 용이) - 상세 페이지 그룹 요청 섹션: 접힌 상태 기본, 메서드별 뱃지 - 리스트 아이콘 버튼 (상세, AI 복사), 세로 중앙 정렬 - 응답 바디 슬래시 이스케이프 처리 - 시스템 alert를 커스텀 Tailwind 모달로 교체
132 lines
4.1 KiB
PHP
132 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\ApiRequestLog;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\View\View;
|
|
|
|
class ApiLogController extends Controller
|
|
{
|
|
/**
|
|
* API 로그 목록 화면
|
|
*/
|
|
public function index(Request $request): View
|
|
{
|
|
// 필터가 적용되면 오래된 순, 아니면 최신순
|
|
$hasFilter = $request->hasAny(['method', 'status', 'search', 'group_id', 'tenant_id']);
|
|
|
|
$query = ApiRequestLog::query()
|
|
->with(['tenant', 'user']);
|
|
|
|
if ($hasFilter) {
|
|
$query->orderBy('created_at'); // 오래된 순
|
|
} else {
|
|
$query->orderByDesc('created_at'); // 최신순
|
|
}
|
|
|
|
// 필터: HTTP 메서드
|
|
if ($request->filled('method')) {
|
|
$query->where('method', $request->method);
|
|
}
|
|
|
|
// 필터: 상태 코드
|
|
if ($request->filled('status')) {
|
|
$status = $request->status;
|
|
if ($status === '2xx') {
|
|
$query->whereBetween('response_status', [200, 299]);
|
|
} elseif ($status === '4xx') {
|
|
$query->whereBetween('response_status', [400, 499]);
|
|
} elseif ($status === '5xx') {
|
|
$query->whereBetween('response_status', [500, 599]);
|
|
}
|
|
}
|
|
|
|
// 필터: URL 검색
|
|
if ($request->filled('search')) {
|
|
$query->where('url', 'like', '%' . $request->search . '%');
|
|
}
|
|
|
|
// 필터: 그룹 ID
|
|
if ($request->filled('group_id')) {
|
|
$query->where('group_id', $request->group_id);
|
|
}
|
|
|
|
// 필터: 테넌트
|
|
if ($request->filled('tenant_id')) {
|
|
$query->where('tenant_id', $request->tenant_id);
|
|
}
|
|
|
|
// 통계
|
|
$stats = [
|
|
'total' => ApiRequestLog::count(),
|
|
'success' => ApiRequestLog::whereBetween('response_status', [200, 299])->count(),
|
|
'client_error' => ApiRequestLog::whereBetween('response_status', [400, 499])->count(),
|
|
'server_error' => ApiRequestLog::whereBetween('response_status', [500, 599])->count(),
|
|
'avg_duration' => (int) ApiRequestLog::avg('duration_ms'),
|
|
];
|
|
|
|
$logs = $query->paginate(50)->withQueryString();
|
|
|
|
// 현재 페이지 로그들의 그룹별 개수 조회
|
|
$groupIds = $logs->pluck('group_id')->filter()->unique()->values()->toArray();
|
|
$groupCounts = [];
|
|
if (!empty($groupIds)) {
|
|
$groupCounts = ApiRequestLog::whereIn('group_id', $groupIds)
|
|
->selectRaw('group_id, COUNT(*) as count')
|
|
->groupBy('group_id')
|
|
->pluck('count', 'group_id')
|
|
->toArray();
|
|
}
|
|
|
|
return view('api-logs.index', compact('logs', 'stats', 'groupCounts'));
|
|
}
|
|
|
|
/**
|
|
* API 로그 상세 화면
|
|
*/
|
|
public function show(int $id): View
|
|
{
|
|
$log = ApiRequestLog::with(['tenant', 'user'])->findOrFail($id);
|
|
|
|
// 같은 그룹의 다른 요청들 (오래된 순)
|
|
$groupLogs = collect();
|
|
$groupMethodCounts = [];
|
|
if ($log->group_id) {
|
|
$groupLogs = ApiRequestLog::where('group_id', $log->group_id)
|
|
->where('id', '!=', $log->id)
|
|
->orderBy('created_at') // 오래된 순
|
|
->get();
|
|
|
|
// 메서드별 개수 집계
|
|
$groupMethodCounts = $groupLogs->groupBy('method')
|
|
->map(fn($items) => $items->count())
|
|
->sortKeys()
|
|
->toArray();
|
|
}
|
|
|
|
return view('api-logs.show', compact('log', 'groupLogs', 'groupMethodCounts'));
|
|
}
|
|
|
|
/**
|
|
* 오래된 로그 삭제 (수동)
|
|
*/
|
|
public function prune()
|
|
{
|
|
$deleted = ApiRequestLog::pruneOldLogs();
|
|
|
|
return redirect()->route('dev-tools.api-logs.index')
|
|
->with('success', "{$deleted}개의 로그가 삭제되었습니다.");
|
|
}
|
|
|
|
/**
|
|
* 모든 로그 삭제
|
|
*/
|
|
public function truncate()
|
|
{
|
|
$deleted = ApiRequestLog::truncate();
|
|
|
|
return redirect()->route('dev-tools.api-logs.index')
|
|
->with('success', '모든 로그가 삭제되었습니다.');
|
|
}
|
|
} |