Log Görüntüleyici
storage/logs/ altındaki Laravel log dosyalarını okumak, aramak ve silmek için yalnızca bakım rolüne açık bir admin bölümü. Kendi içinde tamamlanmış bir domain modülü olarak gelir; ek bağımlılık gerektirmez.
Yetkilendirme
Tüm modül yalnızca system_admin rolüne açıktır — config/permission-resources.php içinde girdi yoktur. system_admin olmayan kullanıcılar route'a 403 alır ve menü öğesini hiç görmez; yani özellik onlara görünmez.
routes/web/log-route.php dosyası routes/web.php içindeki $routesWithoutPermissionMiddleware listesinde olduğu için bu grup için dinamik check.permission middleware'i atlanır.
Route'lar
Route dosyası, authenticated web grubunun içinde yüklenir. İsimli route'lar:
logs.index—GET /logs(Inertia)logs.dtApi—GET /logs/dt(JSON datatable feed)logs.show—GET /logs/{filename}(Inertia)logs.entries—GET /logs/{filename}/entries(JSON, sayfalı)logs.destroy—DELETE /logs(JSON, toplu silme)
filename parametreleri where('filename', '[A-Za-z0-9._-]+\.log') kısıtıyla sınırlandırıldığı için path traversal ve .log olmayan istekler controller'a hiç ulaşmaz.
UI
resources/js/pages/Admin/Logs/ altında iki Inertia sayfası vardır:
Index.vue
logs.dtApi üzerinden beslenen SkDatatable. Kolonlar:
name— dosya adı (sortable, substring araması)channel_type—daily(laravel-YYYY-MM-DD.logformatına uyanlar),single(laravel.log) veyaothersize_bytes— KB / MB / GB olarak formatlanırmodified_at— relatif zaman + tooltip'te mutlak zamanis_active— dosya canlı günlük dosyası ya da son 5 saniyedir yazılıyorsa chip görünür
Satır aksiyonu: Sil (is_active ise pasif). Bulk select toolbar'ında Seçiliyi sil aksiyonu vardır; ikisi de logs.destroy üzerinden akar ve useConfirm ile onaylatılır.
Show.vue
Tek dosya için filtre paneli + sayfalı görüntüleyici. Filtreler:
levels[]— sekiz Laravel/PSR-3 seviyesinden çoklu seçim (emergency,alert,critical,error,warning,notice,info,debug)from,to— ISO tarih aralığıkeyword— mesaj + stack trace içinde case-insensitive substring araması
Kayıtlar en yeniden eskiye listelenir. Filtre değişiklikleri useApi üzerinden logs.entries çağrısı yapar, listeyi yeniler ve cursor'ı sıfırlar. Daha fazla yükle, önceki yanıttaki next_cursor ile bir sonraki (daha eski) sayfayı listenin altına ekler. eof flag'i true ise butonu kapatır.
Her kayıt level chip + timestamp + mesajın baş kısmı olarak çökertilmiş gelir. Açıldığında tam mesaj, JSON pretty-print edilmiş context (varsa) ve stack trace görünür.
Domain Katmanı
Logs domain'i vendor-resident'tir — runtime katmanı paket içinden çalışır (src/Domain/Logs/, namespace Lvntr\StarterKit\Domain\Logs\) ve uygulamanıza scaffold edilmez. App\Domain\Logs\... import'ları class_alias ile çalışmaya devam eder.
src/Domain/Logs/ (Lvntr\StarterKit\Domain\Logs\)
├── DTOs/
│ ├── LogFileDTO.php # name, path, size_bytes, modified_at, channel_type, is_active
│ ├── LogEntryDTO.php # timestamp, level, env, message, context, stack, is_raw
│ ├── LogEntryFilterDTO.php # levels, from, to, keyword, cursor, per_page
│ └── DeleteLogFilesDTO.php # filenames[]
├── Queries/
│ ├── LogFileQuery.php # storage/logs/'i listeler; filtre/sıralama/sayfalama in-memory
│ └── LogEntryQuery.php # tek dosyayı cursor sayfalama ile stream eder
├── Actions/
│ └── DeleteLogFilesAction.php # active-file koruması ile toplu silme
├── Events/
│ └── LogFilesDeleted.php # silinen filenames[] + causer id taşır
├── Listeners/
│ └── LogActivityForLogFilesDeleted.php # her batch için bir spatie/activitylog kaydı yazar
└── Services/
└── LaravelLogParser.php # stateless satır parser'ı; çok satırlı stack trace farkındalığı var
LogFilesDeleted → LogActivityForLogFilesDeleted eşleşmesi StarterKitServiceProvider::registerEventListeners() (vendor) içinde register edilir; her iki taraf da vendor FQCN'ine bağlanır, böylece dispatch edilen event registration anahtarıyla eşleşir.
Streaming Kayıt Okuyucu
LogEntryQuery::paginate() dosyayı fopen('rb') ile açar ve satır başına 64KB ile sınırlı fgets() döngüsünü kullanır. Log dosyası sonundan büyüdüğü için sayfa geriye doğru kurulur: pencere cursor offset'inde biter (hariç) ve dosya başına doğru 64KB'lık adımlarla, istek başına 2MB tavanına kadar genişler. Pencere sonra ileri yönde parse edilir (kayıtlar yalnızca yazım sırasına göre okunabilir) ve ters çevrilerek döndürülür. Cursor, sayfanın EN ESKİ kaydının başladığı byte offset'idir; bir sonraki sayfa tek fseek ile devam eder. Bellek kullanımı dosya boyutundan bağımsız olarak sabit kalır.
Eşleşmeyen satırlar konumlarına göre işlenir:
- bir başlığın ardından gelen satırlar — mevcut kaydın
stackalanına eklenir; çok satırlı exception'ların bütünlüğü korunur - ilk başlıktan önceki (veya hiç Laravel-format başlığı içermeyen dosyalarda kalan) satırlar — buffer'lanır ve tek bir raw
LogEntryDTOolarak basılır (is_raw = true,level = 'raw', sentinel epoch-0 timestamp). UI raw entry'lerin zaman damgasını gizler ve gri chip ile gösterir; böylece içerik sessizce kaybolmak yerine görünür kalır. Yapısal filtre uygulandığı an (level / from / to / keyword) raw entry'ler doğal olarak listeden düşer.
Aktif Dosya Koruması
Bir dosya iki koşuldan biri sağlanırsa "aktif" sayılır ve silinmez:
- bugünün günlük dosyasıdır (
laravel-{today}.log), veya mtime'ı son 5 saniye içindedir (başka bir kanal şu an dosyaya yazıyor olabilir).
Aktif dosyalar failed[] listesinde reason: 'active_file_protected' ile döner; böylece kısmi toplu silme geri kalan dosyalar için başarılı olur.
Path-Traversal Koruması
Güvenli dosya adı regex'i ^[A-Za-z0-9._-]+\.log$ üç farklı yerde zorlanır: route parameter kısıtı, DeleteLogFilesRequest validasyonu ve DeleteLogFilesAction (defence in depth). Geri kalan her şey log.invalid_filename döner.
Activity Log
Bir silme batch'i en az bir dosya silebildiğinde LogFilesDeleted dispatch edilir. Listener log_name = 'system', subject yok, silinen dosyalar properties.filenames altında ve causer olarak mevcut kullanıcı ile bir spatie/activitylog kaydı yazar. Kayıt mevcut Admin → Activity Logs sayfasında otomatik görünür.
i18n
Tüm metinler lang/en/sk-log.php ve lang/tr/sk-log.php altında. Menü key'i (sk-menu.logs) mevcut menü çeviri dosyalarında. Action'ın döndüğü hata sebep kodları (invalid_filename, not_found, active_file_protected, delete_failed) bire bir UI tarafındaki sk-log.reason_* key'lerine eşlenir.
Wayfinder
Route'lar tiplidir: import logs from '@/routes/logs' ile logs.index.url(), logs.show.url({ filename }) vs. erişilir. Frontend'de URL hardcoded değildir.
Kapsam Dışı (v1)
Yapılmadı; ihtiyaç duyulursa follow-up issue olarak açılabilir:
- live tail / WebSocket streaming
- tüm log dosyalarında cross-file arama
- zaman tabanlı toplu temizlik ("N günden eski dosyaları sil") — günlük kanal bunu zaten yapıyor
.zipexport / indirme- Laravel-dışı log formatları (Apache, JSON channel)
system_admindışında kullanıcı tabanlı yetki ayrımı