Тема
ТЗ — Отладка/профилирование (cms/debug)
Слой: 🔵 инфра-модуль · Зрелость доноров: ★★ · Донор: — Статус: ТЗ к разработке
Назначение и возможности
Модуль отладки и профилирования для разработчиков и studio-инженеров: журналирование SQL-запросов, детект N+1 в реальном времени, интеграция с инструментами трассировки. Активен только вне production либо по явной studio-роли.
- Query-log с подсветкой медленных запросов (порог из настроек)
- Детект N+1 (повторяющиеся запросы в рамках одного запроса/джобы)
- Интеграция с Laravel Telescope (детальная трассировка)
- Интеграция с Laravel Pail (стрим логов в реальном времени)
- Панель профилирования запроса (время, память, число SQL)
- Дамп контекста джобы при падении в очереди
- Экспорт снятого профиля для приложения к тикету
- Жёсткое отключение в production без studio-роли
- Маскирование чувствительных значений в query-log перед записью (пароли/токены/email)
- Настраиваемый порог детекта N+1 (число одинаковых запросов подряд)
Зависимости и выключение
requires: ядро (Логирование)
Поведение при выключении: панель профилирования и query-log недоступны, ошибки логируются штатным логом ядра без деталей SQL — деградация, не поломка.
Стоимость внешних API (матрица v2.2): не применимо — Telescope и Pail интеграции локальные (внутри инфраструктуры проекта), модуль не вызывает платных внешних сервисов и не тратит чужую квоту.
Модель данных
Своих таблиц нет — использует хранилище Telescope (telescope_entries) при включённой интеграции, иначе только файловые логи; снимки cms:debug:snapshot — файлы на диске с собственным ретеншном debug.snapshot_retention_days. Отдельного индексирования модуль не вносит — ретеншн Telescope управляется его штатной командой очистки по debug.retention_days. Полный ПДн-паспорт (что может временно попасть в query-log/снимки и как гейтится доступ к ним) — см. «Безопасность».
Входные и выходные данные
Входы
| Источник | Данные/поля | Чем валидируется |
|---|---|---|
| Query listener ядра (внутренний перехват) | SQL, bindings, длительность, контекст запроса/джобы | не пользовательский ввод; bindings не логируются, если содержат отмеченные конфигом чувствительные поля |
GET /api/v1/admin/debug/profile | заголовок текущего запроса (для профилирования) | permission debug.view/studio-роль, гейт по окружению |
cms:debug:snapshot (CLI) | без пользовательского ввода | доступ на уровне сервера |
| Filament: экспорт снятого профиля | snapshot_id | permission debug.export; snapshot_id — существующий снимок |
Выходы
| Потребитель | Данные | Формат |
|---|---|---|
| Filament-панель профилирования | время, память, число SQL, query-log | Blade через сервис модуля |
| Экспорт для тикета | снятый профиль (запросы, N+1-паттерны, память) | файл (JSON/HTML), скачивается администратором |
| Telescope (если включён) | детальная трассировка | telescope_entries (сторонний пакет) |
| Laravel Pail (если включён) | стрим логов в реальном времени | стандартный вывод/лог-канал |
Настройки (группа debug)
| Ключ | Тип | Дефолт | affectsPageCache | Описание |
|---|---|---|---|---|
debug.enabled | bool | false | нет | Глобальный включатель модуля |
debug.slow_query_ms | int | 200 | нет | Порог медленного запроса в мс |
debug.n1_detection | bool | true | нет | Включить детект N+1 |
debug.telescope_enabled | bool | false | нет | Включить интеграцию Telescope |
debug.allowed_roles | array | ["studio"] | нет | Роли с доступом к панели вне local |
debug.retention_days | int | 3 | нет | Хранение записей Telescope |
debug.scrub_bindings_enabled | bool | true | нет | Маскирование чувствительных значений (password, token, email) в query-log перед записью |
debug.n1_threshold | int | 5 | нет | Число одинаковых запросов подряд для срабатывания N1PatternDetected |
debug.snapshot_retention_days | int | 14 | нет | Хранение файлов cms:debug:snapshot на диске |
Лимиты и квоты (матрица v2.2): debug.slow_query_ms и debug.n1_threshold — пороги чувствительности детекта, оба с дефолтом выше; debug.retention_days и debug.snapshot_retention_days — ретеншн накопленных диагностических данных. Превышение порогов не роняет модуль и не режет данные молча — только меняет объём того, что попадает в лог/детект, либо (для ретеншна) удаляется штатной очисткой по расписанию.
Kill-switch (матрица v2.2): debug.enabled и есть аварийный выключатель модуля — перевод в false мгновенно останавливает query-listener и скрывает панель без полного disable пакета через менеджер модулей; отдельный флаг не заводится, это одна из трёх частей гейта из «Безопасность».
API
Отдельного публичного API нет — админ-CRUD через Filament. Панель профилирования — служебный роут /api/v1/admin/debug/profile под studio-ролью.
Компоненты
Виджеты: Filament-виджет «Последние медленные запросы» (с индикатором «нормально/ медленно/критично» по slow_query_ms). Filament: страница профилирования запроса (время, память, число SQL, группировка повторяющихся запросов). Команды: cms:debug:snapshot --json (снять срез логов в файл, готовый к приложению к тикету), cms:debug:clear --json (очистка накопленных логов/снимков), cms:debug:doctor --json (включён/выключен, активность Telescope, согласованность allowed_roles с реальным доступом).
Демо-сидеры (матрица v2.2): не применимо — модуль не работает с контентными данными, галерея блоков и playground его не касаются; диагностическая ценность видна только на реальном трафике или в тестовом сценарии с сознательно медленным/повторяющимся запросом, не на demo-сидере с фиктивными данными.
События и обмен
| Событие | Когда | Payload |
|---|---|---|
SlowQueryDetected | запрос превысил порог | sql, bindings, duration_ms, context |
N1PatternDetected | обнаружен повторяющийся паттерн запросов | model, count, trace |
FilterBus и provides-контракты не используются. Слушает: манифест не декларирует зависимостей от событий других модулей — источник данных внутренний (query listener ядра).
Таблица взаимодействий
| Сущность/модуль | Канал | Направление | Что происходит |
|---|---|---|---|
| Query listener ядра | внутренний перехват (вне пяти каналов, hook на уровне DB-соединения) | in | Источник данных для query-log и детекта N+1 |
| Все модули (косвенно, через их SQL-запросы) | пассивное наблюдение, не активный обмен | in | Медленные/N+1-запросы любого модуля попадают в лог debug без участия модуля-источника |
| Telescope (сторонний пакет, если включён) | прямая интеграция пакета, не канал CMS | out | Детальная трассировка складывается в telescope_entries |
| Laravel Pail (сторонний пакет, если включён) | прямая интеграция пакета, не канал CMS | out | Стрим текущего лог-канала в реальном времени |
Фоновая работа
Своих очередей и джобов нет: query-listener и детект N+1 работают синхронно в рамках текущего запроса/джобы (лёгкий инструмент, без внешних вызовов). Ретеншн Telescope выполняется штатной командой пакета по расписанию через ScheduleRegistrar ядра.
Метрики и алерты (§15)
Минимальный набор — диагностический инструмент, не источник алертов сам по себе: число SlowQueryDetected за период, число N1PatternDetected за период, оверхед query-listener (среднее время перехвата на SQL-запрос — рост означает, что инструмент сам стал источником замедления). Оба события можно агрегировать в Pulse как сигнал деградации производительности других модулей; отдельного алертинга debug не заводит — решает cms/health, если оба события молчат при заведомо медленном трафике.
Мини-ранбук
| Симптом | Что проверить | Команда |
|---|---|---|
Всплеск SlowQueryDetected | нагрузка БД, не занижен ли debug.slow_query_ms | cms:debug:snapshot --json — снять срез для анализа |
Ложные N1PatternDetected на легитимный батч похожих запросов | эвристика детекта не различает похожие-но-разные запросы | скорректировать debug.n1_threshold |
debug.telescope_enabled=true, но данных в Telescope нет | пакет установлен как dev-зависимость? | cms:debug:doctor --json |
telescope_entries разрослась на staging | применяется ли debug.retention_days | cms:debug:clear --json |
Снимки cms:debug:snapshot копятся на диске | применяется ли debug.snapshot_retention_days | cms:debug:clear --json |
Восстановительные команды (cms:debug:clear, cms:debug:snapshot) идемпотентны и безопасны на живом сайте, без даунтайма.
Бэкап/рестор: модуль не вносит данных, подлежащих обязательному бэкапу — снимки на диске и файловые логи диагностические и одноразовые, их отсутствие после рестора не считается потерей; при включённом Telescope telescope_entries бэкапится/восстанавливается в рамках политики самого пакета Telescope, не этого модуля.
Производительность и кеш
Объёмы: query-log — весь SQL текущего запроса/джобы, живёт только в памяти процесса (не персистится, кроме явного cms:debug:snapshot); при включённом Telescope — telescope_entries растёт пропорционально трафику dev/staging-окружения (там модуль и должен быть активен), ретеншн debug.retention_days обязателен, иначе таблица растёт неограниченно на активно используемом staging. Горячий путь самого модуля — оверхед query-listener на каждый SQL-запрос обязан быть минимальным (микросекунды на запрос), иначе инструмент диагностики сам становится источником замедления, которое он должен обнаруживать у других; детект N+1 не должен вести собственный N+1 (например, отдельный запрос в БД на каждый перехваченный SQL для классификации). Собственных тегов page-cache нет — участвует только косвенно (модуль отключён в production, где живёт page-cache).
Безопасность
Границы входа: панель профилирования — только служебный роут /api/v1/admin/debug/profile под тройным гейтом (ниже), входных форм с пользовательскими данными нет. Доступ к панели и экспорту — только через явные permissions, не по строке роли.
Тройной гейт от прода
По аналогии с cms/dev-panel (тот же принцип в парке инфра-модулей) доступ защищён тремя независимыми факторами, ни один из которых не даёт доступа сам по себе. Проверка — в middleware, до рендера панели и до активации query-listener на соединении БД:
| # | Фактор | Условие | Где проверяется |
|---|---|---|---|
| 1 | env | APP_ENV не production, либо production с явным допуском | middleware, на каждый запрос к панели/профилю |
| 2 | настройка | debug.enabled=true и debug.allowed_roles не пуст | middleware, чтение из кеша группы настроек (0 запросов на горячем пути) |
| 3 | роль | роль текущего пользователя входит в debug.allowed_roles | middleware, до рендера страницы и до подписки query-listener |
Отсутствие любого одного фактора — отказ (403 до рендера), остальные два не спасают: APP_ENV=local без debug.enabled=true не активирует ни панель, ни перехват запросов (env один не открывает гейт); debug.enabled=true на production без нужной роли — 403 (настройка одна не открывает гейт); нужная роль при debug.enabled=false — 403 (роль одна не открывает гейт). Тот же гейт закрывает не только Filament-панель, но и активацию query-listener и Pail-стрим — перехват SQL не работает «в фоне», пока не пройдены все три фактора.
Специфичные векторы: query-log с bindings — прямой канал утечки ПДн и секретов (пароли в формах, токены в заголовках) в лог/Telescope/экспортируемый тикет — bindings обязаны проходить тот же скраббинг чувствительных полей, что и cms/sentry breadcrumbs, до записи, не после; экспорт профиля для тикета — файл может попасть во внешнюю систему (трекер) при вложении к тикету, поэтому маскирование обязательно даже строже, чем во внутреннем логе; Pail-стрим логов в реальном времени — доступен только под тем же тройным гейтом, что и панель, не отдельным незащищённым каналом.
ПДн-паспорт
Модуль не хранит ПДн как основную сущность. Query-log с SQL и bindings может временно содержать ПДн (email, пароли форм) до срабатывания скраббинга (debug.scrub_bindings_enabled); сам query-log живёт только в памяти процесса текущего запроса/джобы и не персистится, кроме явного cms:debug:snapshot (файл на диске) и включённого Telescope (telescope_entries). Ретеншн: снимки — debug.snapshot_retention_days, Telescope — debug.retention_days. Хуки ядра «выгрузить всё по субъекту»/«забыть по запросу» (152-ФЗ) — не применимы: диагностические данные не привязаны к конкретному ФЛ как основной сущности и не переживают собственный ретеншн; по истечении retention_days/ snapshot_retention_days данные удаляются штатной очисткой, отдельного per-субъект стирания не требуется.
Матрица ролей
Доступ к любому действию модуля — только роли из debug.allowed_roles (по умолчанию ["studio"], сам список — часть гейта, см. выше); остальные штатные роли парка (админ клиента, менеджер, редактор) доступа не получают ни при какой комбинации остальных настроек:
| Permission | studio (роль в debug.allowed_roles) | админ/менеджер/редактор клиента |
|---|---|---|
debug.view (панель, query-log) | да | нет |
debug.manage (изменение настроек группы debug) | да | нет |
debug.export (скачивание снятого профиля) | да | нет |
Права: debug.view, debug.manage, debug.export — все три проверяются поверх тройного гейта, а не вместо него.
UX-требования
Админ (разработчик/studio-инженер): пустое состояние «медленных запросов не было» на виджете вместо пустого списка; профилирование запроса — цифры (время, память, число SQL) сопровождаются понятной шкалой «нормально/медленно/критично», не голые числа без контекста; экспорт профиля — одна кнопка «Скачать для тикета», результат сразу готов к вложению, без ручной сборки; предупреждение при попытке включить модуль на production («вы уверены — на проде это может замедлить запросы») даже под studio-ролью, где это формально разрешено.
Посетитель: не применимо — модуль не должен быть виден и не должен влиять на поведение публичного сайта ни при каких условиях.
Крайние случаи и типовые баги
- случайное включение на проде →
debug.enabled=trueв.env/настройках на проде без нужной роли не даёт доступа — тройной гейт (env,debug.enabled+debug.allowed_roles, роль пользователя) проверяется на уровне middleware одновременно, ни один из трёх факторов не достаточен сам по себе; - утечка ПДн/секретов через query-log → bindings со значениями из полей вроде
password/token/emailмаскируются до попадания в лог/Telescope/экспорт — тест на утечку аналогичен тестуcms/sentry, но применяется к SQL-параметрам, не breadcrumbs; - детект N+1 ложно триггерится на легитимный паттерн (например, батч из нескольких похожих, но не идентичных запросов) → порог/эвристика детекта настраиваемы, событие
N1PatternDetectedвключает достаточно контекста (модель, стек), чтобы разработчик быстро отличил реальный N+1 от совпадения; - query-listener сам создаёт нагрузку на нагруженном dev/staging → оверхед на запрос обязан быть измерен и минимален; включённый
n1_detectionне должен удваивать время ответа — если бюджет превышен, это баг модуля, не приемлемая цена диагностики; - выключение модуля посреди снятия snapshot →
cms:debug:snapshot— синхронная CLI-операция, не оставляющая промежуточного состояния: либо снимок создан целиком, либо не создан вовсе, отключение модуля между запуском и завершением команды не предусмотрено (команда выполняется в рамках одного процесса); - отсутствие Telescope при
debug.telescope_enabled=true(пакет не установлен как dev-зависимость) → self-test enable обязан явно сообщить о несоответствии, не включать интеграцию молча в состояние «включена, но не работает»; - пустой query-log (запрос вообще не делал SQL) → панель показывает «0 запросов» — валидный результат, не ошибка рендера;
- огромный query-log (сотни запросов на один тяжёлый экран/джобу) → UI обязан группировать повторяющиеся запросы (это и есть детект N+1), не выводить плоский список из сотен строк без агрегации;
- измерения locale/city/site — модуль не работает с контентными данными и не несёт этих измерений; сам SQL, который он логирует, может относиться к любому из измерений — это не касается debug напрямую, разработчик видит измерение в контексте запроса (
RequestContext), не в отдельном поле модуля; - противоречивая комбинация настроек:
debug.enabled=true, ноdebug.allowed_rolesпуст ([]) → эффективно никто не имеет доступа, при этом query-listener всё равно перехватывает запросы вхолостую — стоит явно указать вcms:debug:doctor, что включённый модуль без разрешённых ролей — это, вероятно, ошибка конфигурации, а не осознанное состояние.
Донорский код
Донор: — (новая разработка). Миграция legacy-данных (§16, матрица v2.2): не применимо — модуль не хранит собственных бизнес-данных, переносимых со старых платформ (диагностический инструмент без предметных сущностей), импорт-профиль cms:debug:import-legacy не нужен и не заводится.
Тесты и приёмка
- [ ] Модуль недоступен в production без studio-роли (контрактный тест)
- [ ] Health-чек модуля отражает включён/выключен и активность Telescope
- [ ] При выключении модуля приложение работает штатно, ошибки идут в общий лог
- [ ] Права
debug.view/debug.manageразграничивают доступ к панели - [ ] Детект N+1 не создаёт собственных N+1 (сам инструмент лёгкий)
- [ ] Ретеншн записей Telescope применяется автоматически по
debug.retention_days - [ ] Bindings с чувствительными полями (
password,token,email) маскируются в query-log, Telescope и экспорте - [ ] Контрактный тест env-only:
APP_ENVне production, ноdebug.enabled=false— доступа нет (фактор env в одиночку гейт не открывает) - [ ] Контрактный тест setting-only:
debug.enabled=trueиdebug.allowed_rolesзаполнен, ноAPP_ENV=productionбез явного допуска — доступа нет (фактор настройки в одиночку гейт не открывает) - [ ] Контрактный тест role-only: роль пользователя входит в
debug.allowed_roles, ноdebug.enabled=false— доступа нет (фактор роли в одиночку гейт не открывает) - [ ] Включённый
telescope_enabledбез установленного пакета Telescope диагностируетсяcms:debug:doctor, не включается молча - [ ] Контрактный набор
cms-testingпройден, пакет протестирован в testbench-изоляции - [ ] Feature-тест на каждый роут модуля; тестовая БД только
debug_test,migrate:freshзапрещён