9. Діагностичні логи
Після кроку 8 у вас є безпечний app-канал: exceptions і
бізнес-info у app.log з masking.
Але два типові дебаг-сценарії в app.log часто
не видно:
- Форма «мовчки» не проходить. Користувач бачить підсвічені поля (крок 7), а ви в app-лозі — нічого: ValidationException оброблена middleware, це не «аварія». Щоб зрозуміти, які правила впали і з яким input, потрібен окремий слід саме валідації.
-
SQL поводиться дивно. N+1, зайвий
SELECT, неочікувані bindings — уinfocontroller-а цього немає. Треба бачити реальний запит до MySQL, не лише «insert пройшов».
Обидва болі вирішуються окремими sinks на вже наявних
bricks, не новим addLogging():
Validation і Database мають власні rotating-файли. Їх шум
інший (сотні рядків SQL на один page load), тому змішувати з
app-каналом — погана ідея. На проді ці sinks зазвичай
вимикають; локально / на staging — вмикають, коли дебажите.
Masking з кроку 8 уже є: підключаємо його й сюди через
withMasking(), бо validation log і query bindings
легко тягнуть email/паролі.
Що додається і навіщо
-
withLogging(…/validation.log)на Validation — файл спроб валідації (які поля, які помилки) -
withQueryLogging(…/query.log)на Database — SQL + bindings у окремий rotating файл -
withMasking()на обох — ті самі правила Data Masker, що й дляapp.log
Це не capability Logging: свої handlers усередині Validation / Database providers. API — Validation і Database.
Лог валідації
Без цього виклику валідація працює як раніше — лише без файлу. З ним ValidationLogger пише в rotating sink: зручно відтворити «чому formal failed», коли flash уже зник або API повернув 422 без деталей у вашому app-лозі.
$stack->addValidation()
->withLogging($rootPath . 'storage/logs/validation.log', 7)
->withMasking();
withMasking() вимагає addMasking()
(у нас уже з кроку 8). Інакше Stack не збереться на
providers() — fail-fast замість «лог з паролями».
Query log
Eloquent/Capsule виконує SQL; без query log ви бачите лише
результат у відповіді. withQueryLogging вмикає
окремий RotatingFileHandler у Database provider — кожен запит
потрапляє у query.log. На проді з постійним
трафіком файл швидко роздується: тримайте opt-in.
$stack->addDatabase()
->setConnection([ /* … */ ])
->setMigrations([ /* … */ ])
->setSeeders([ /* … */ ])
->withQueryLogging($rootPath . 'storage/logs/query.log', 7)
->withMasking();
Bindings у SQL часто містять ті самі email/паролі, що й форма — тому masking тут не «опція на потім», а частина кроку.
Фрагмент Bootstrap
Diff відносно кроку 8 — лише ланцюжки Validation і Database.
addLogging() / addMasking() / ErrorHandling
лишаються як були.
$stack->addValidation()
->withLogging($rootPath . 'storage/logs/validation.log', 7)
->withMasking();
$stack->addDatabase()
->setConnection([ /* mysql … */ ])
->setMigrations([$rootPath . 'database/migrations'])
->setMigrationsTable('migrations')
->setSeeders([ContactSubmissionsSeeder::class])
->withQueryLogging($rootPath . 'storage/logs/query.log', 7)
->withMasking();
// … Session, Http, View, Console, Masking, Logging, ErrorHandling — без змін
Запуск і перевірка
Тег step-09-diagnostic-logs. Мета — побачити
validation*.log і query*.log і переконатися,
що чутливі поля замасковані.
Запуск
git checkout step-09-diagnostic-logs
rm -rf storage/cache storage/sessions storage/logs
composer install
cp docker-compose.yml.example docker-compose.yml
docker compose up -d --build
Перевірка в браузері
-
Відкрийте
http://localhost:8089/contact
і надішліть невалідну форму — redirect з помилками; з’явиться
storage/logs/validation*.log. -
Надішліть валідну форму або відкрийте
http://localhost:8089/api/stats
— SQL потрапить у
storage/logs/query*.log.
Перевірка з CLI
COOKIE=/tmp/c.txt
curl -s -c $COOKIE http://localhost:8089/contact >/dev/null
TOKEN=$(curl -s -b $COOKIE http://localhost:8089/contact \
| sed -n 's/.*name="_csrf_token" value="\([^"]*\)".*/\1/p')
# 1) невалідний POST → рядок у validation.log
curl -s -b $COOKIE -c $COOKIE -o /dev/null -w '%{http_code}\n' -X POST http://localhost:8089/contact \
-d "name=a&email=bad&message=hi&_csrf_token=$TOKEN"
# 302
ls -la storage/logs/validation*.log
tail -n 20 storage/logs/validation*.log
# 2) валідний POST /contact або GET /api/stats → SQL у query.log
curl -s http://localhost:8089/api/stats
ls -la storage/logs/query*.log
tail -n 30 storage/logs/query*.log
# шукайте SELECT/INSERT; email у bindings має бути замаскований, якщо потрапив
Ланцюжок
POST /contact (invalid)
→ FormRequest / validator fails
→ ValidationLogger → storage/logs/validation-*.log (+ masker)
→ HandleValidationException → flash + redirect
→ app.log: тиша (це не exception для ErrorHandling)
POST /contact (valid) або GET /api/stats
→ Capsule SQL
→ query log → storage/logs/query-*.log (+ masker)
→ app.log: лише ваш info / errors, не кожен SELECT
Далі — 10. Casting і DTO:
FormRequest → typed DTO через toDto().
Усі кроки — Огляд.
Bricks —
Validation,
Database.