@extends('admin.layouts.app') @section('title', 'Monitoring FCM & Jobs') @section('content')

Monitoring Manual

Logs • Jobs • FCM • Notifikasi — endpoint /fcm • API /api/monitoring/*

JSON API /api/monitoring/overview
@if(session('success'))
{{ session('success') }}
@endif @if(session('error'))
{{ session('error') }}
@endif {{-- Tabs --}}
@if($tab==='overview')

Queue

{{ $jobs['pending'] ?? 0 }} pending
Connection: {{ $queue_connection }} • Env: {{ $env }}
By queue: @foreach(($jobs['pending_by_queue']??[]) as $q=>$c) {{ $q }}:{{ $c }} @endforeach
Failed: {{ $jobs['failed'] ?? 0 }} • Batches: {{ $jobs['batches'] ?? 0 }}
Notif queue pending: {{ $fcm['notif_pending'] ?? 0 }} • failed: {{ $fcm['notif_failed'] ?? 0 }}

FCM Credential

Status: @if($fcm['credential_exists']) ADA @else TIDAK ADA @endif
Path: {{ $fcm['credential_path'] ?? '-' }}
Project: {{ $fcm['project_id'] ?? '-' }}
Client: {{ $fcm['client_email'] ?? '-' }}
Total tokens: {{ $fcm['total_tokens'] ?? 0 }} • Valid: {{ $fcm['valid_tokens'] ?? 0 }} • Invalid: {{ $fcm['invalid_tokens'] ?? 0 }} • Legacy users: {{ $fcm['legacy_users_with_token'] ?? 0 }}
By platform: @foreach(($fcm['by_platform']??[]) as $p=>$c) {{ $p }}:{{ $c }} @endforeach

Notifikasi DB

{{ $notifications['total'] ?? 0 }} total
Unread: {{ $notifications['unread'] ?? 0 }} • Read: {{ $notifications['read'] ?? 0 }}
By type: @foreach(($notifications['by_type']??[]) as $t=>$c) {{ $t }}:{{ $c }} @endforeach

Logs

Laravel: {{ $logs['laravel']['exists'] ? number_format($logs['laravel']['size']/1024,1).' KB' : 'tidak ada' }} • {{ count($logs['laravel']['lines'] ?? []) }} lines preview
Firebase: {{ $logs['firebase']['exists'] ? number_format($logs['firebase']['size']/1024,1).' KB' : 'tidak ada' }} • {{ count($logs['firebase']['lines'] ?? []) }} lines
Daily firebase files: {{ $fcm['daily_log_files'] ?? 0 }}

Recent Jobs (5)

Detail
@forelse(($jobs['recent_jobs'] ?? []) as $j)
{{ $j['displayName'] }}
queue: {{ $j['queue'] }} • attempts: {{ $j['attempts'] }}
#{{ $j['id'] }}
@empty
Tidak ada pending jobs
@endforelse

Recent Notifications (10)

Detail
@forelse(($notifications['recent'] ?? []) as $n)
{{ $n['title'] }} ({{ $n['type'] }})
To #{{ $n['notifiable_id'] }} • {{ $n['created_at'] }} • @if($n['read_at']) read @else unread @endif
@empty
Belum ada notifikasi
@endforelse

Queue Worker Command (manual, tanpa Horizon)

php artisan queue:work --queue=notifications --tries=3 --timeout=120 --sleep=3

# cek
php artisan queue:failed
php artisan queue:retry all
tail -f storage/logs/firebase.log
tail -f storage/logs/laravel.log
@csrf
@endif @if($tab==='logs')

Logs Viewer

@csrf
@if(isset($logDetail))
Path: {{ $logDetail['path'] }} • Size: {{ number_format($logDetail['size']/1024,1) }} KB • Exists: {{ $logDetail['exists'] ? 'ya' : 'tidak' }}
{{ implode("\n", $logDetail['lines']) }}
@else

laravel.log (50)

{{ implode("\n", array_slice($logs['laravel']['lines'] ?? [], -30)) }}

firebase.log (50)

{{ implode("\n", array_slice($logs['firebase']['lines'] ?? [], -30)) }}
@endif
Tips: gunakan tail -f storage/logs/laravel.log di server untuk realtime tanpa Horizon.
@endif @if($tab==='jobs')
{{ $jobs['pending'] ?? 0 }}
Pending Jobs
{{ $jobs['failed'] ?? 0 }}
Failed Jobs
{{ $jobs['batches'] ?? 0 }}
Batches

Pending Jobs (queue: notifications + default)

@forelse(($jobs['recent_jobs'] ?? []) as $j) @empty @endforelse
IDQueueJobAttemptsAvailableCreated
{{ $j['id'] }} {{ $j['queue'] }} {{ $j['displayName'] }} {{ $j['attempts'] }} {{ date('H:i:s', $j['available_at']) }} {{ date('d/m H:i', $j['created_at']) }}
Tidak ada pending jobs
Jalankan worker: php artisan queue:work --queue=notifications --tries=3 --timeout=120

Failed Jobs (10 terbaru)

@csrf
@forelse(($jobs['recent_failed'] ?? []) as $f) @php $payload = json_decode($f->payload, true); $display = $payload['displayName'] ?? $payload['job'] ?? '-'; @endphp @empty @endforelse
UUIDQueuePayloadExceptionFailed AtAksi
{{ substr($f->uuid,0,8) }}... {{ $f->queue }} {{ $display }} {{ Str::limit($f->exception, 80) }} {{ $f->failed_at }}
@csrf
@csrf
Tidak ada failed jobs
Manual: php artisan queue:failed • queue:retry all • queue:forget ID • queue:flush
@endif @if($tab==='fcm')

Credential

File: @if($fcm['credential_exists']) ADA @else TIDAK ADA @endif
{{ $fcm['credential_path'] }}
Project: {{ $fcm['project_id'] }}
Client: {{ $fcm['client_email'] }}
Queue: {{ $fcm['queue_connection'] }}

Tokens

Total: {{ $fcm['total_tokens'] }} • Valid: {{ $fcm['valid_tokens'] }} • Invalid: {{ $fcm['invalid_tokens'] }}
By platform: @foreach($fcm['by_platform'] as $p=>$c) {{ $p }}:{{ $c }} @endforeach
Legacy users fcm_token: {{ $fcm['legacy_users_with_token'] }}
Notif pending: {{ $fcm['notif_pending'] }} • failed: {{ $fcm['notif_failed'] }}

Test Kirim

@csrf

Recent Device Tokens (10)

@forelse($fcm['recent_tokens'] as $t) @empty @endforelse
UserTokenPlatformValidLast Used
#{{ $t->user_id }} {{ substr($t->token,0,12) }}***{{ substr($t->token,-4) }} {{ $t->platform ?? '-' }} @if($t->is_valid) ya @else tidak @endif {{ $t->last_used_at }}
Belum ada token

Firebase Log (20 terbaru)

{{ implode("\n", $fcm['firebase_log_recent']['lines'] ?? []) }}
@endif @if($tab==='notifications')
{{ $notifications['total'] }}
Total
{{ $notifications['unread'] }}
Unread
{{ $notifications['read'] }}
Read

By Type

@foreach($notifications['by_type'] as $t=>$c) {{ $t }}: {{ $c }} @endforeach

Recent Notifications (10)

API JSON →
@forelse($notifications['recent'] as $n) @empty @endforelse
IDTypeTitleToReadCreated
{{ substr($n['id'],0,8) }}... {{ $n['type'] }} {{ $n['title'] }} #{{ $n['notifiable_id'] }} @if($n['read_at']) ya @else belum @endif {{ $n['created_at'] }}
Belum ada

Top Users by Notif Count

@forelse($notifications['top_users'] as $u)
User #{{ $u->notifiable_id }}{{ $u->cnt }}
@empty
-
@endforelse
@endif
API Monitoring JSON: GET /api/monitoring/overview • /api/monitoring/logs?channel=firebase&lines=100 • /api/monitoring/jobs • /api/monitoring/fcm • /api/monitoring/notifications (Bearer admin)
Web Monitoring: /fcm?tab=overview|logs|jobs|fcm|notifications + /fcm/logs?channel=laravel&lines=200
@endsection