log-my-errors — как устроена браузерная консоль для PHP

log-my-errors (LME) — небольшой проект @jsmarkus 2013 года для просмотра PHP-ошибок в браузере. Здесь разобран путь сообщения: PHP перехватывает событие, Node.js принимает его, браузер показывает строку и stack trace. Это пример устройства отладочного инструмента, а не рекомендация подключать старый пакет к рабочему PHP-сервису.

Первоначальная задача возникла при разборе старого PHP-проекта с неудобным журналом ошибок. Автор хотел быстро видеть сообщения и значения переменных, не переключаясь между файлами журнала. Такой просмотр дополняет debugger, но не заменяет пошаговую отладку.

Xdebug позволяет останавливаться в нужном месте программы и исследовать состояние. Консоль LME решает более узкую задачу: показывает переданные ей сообщения. Это разные инструменты, и наличие красивой строки в браузере не доказывает, что обработаны все классы ошибок PHP.

Возможности перехвата исключений, предупреждений и фатальных ошибок зависят от обработчиков и версии PHP. Не обещайте, что произвольный вставленный snippet перехватит абсолютно любую ошибку. Проверяйте каждый нужный тип события отдельным тестом.

Так появилась утилита log-my-errors (сокращенно LME, npm: log-my-errors, github: jsmarkus/log-my-errors).

Установка и границы эксперимента

Исходный проект: jsmarkus/log-my-errors. Перед запуском старой версии прочитайте её исходники, зависимости, состояние поддержки и PHP-snippet. Установка через sudo npm -g для этого не требуется и здесь не предлагается.

Если нужно воспроизвести историческое поведение, используйте отдельную одноразовую VM или контейнерную лабораторию без рабочих данных и доступа к рабочей сети. Пакет устанавливают локально в этой лаборатории, фиксируя выбранную версию и lockfile; актуальная совместимость со свежими Node.js/PHP в этой статье не заявляется.

До передачи сообщений убедитесь, что слушатель привязан только к 127.0.0.1/::1. Открытие в браузере адреса 127.0.0.1 само по себе этого не доказывает: процесс может одновременно слушать все интерфейсы. На Linux адрес слушателя показывает ss -ltnp, на macOS — lsof -nP -iTCP:7001 -sTCP:LISTEN. Если виден wildcard-адрес, не начинайте передачу данных: сначала ограничьте bind в изученной копии кода и повторите проверку.

В исходной версии веб-интерфейс использовал локальный порт 7001. Это номер исторического примера, а не требование открыть порт на сервере. На изолированном loopback-экземпляре интерфейс выглядел так:

Веб-консоль log-my-errors: начальный экран и кнопка Get code

Кнопка Get code выдаёт PHP-код подключения. Сначала прочитайте его целиком: куда он отправляет данные, какие обработчики заменяет и какие побочные эффекты имеет. Не копируйте код автоматически в приложение.

Вставка кода и перехват ошибок

Подключайте изученный snippet только к отдельному учебному PHP-проекту. Endpoint должен оставаться на loopback той же лаборатории. Создайте синтетическую ошибку без паролей, cookies, токенов, персональных данных и фрагментов рабочих запросов.

Проверьте, что ожидаемое тестовое событие появилось ровно один раз. Отдельно проверьте поведение без запущенного приёмника: отладочная интеграция не должна менять ответ приложения или скрывать исходный журнал ошибок. Не отправляйте в LME ошибки production-системы: stack trace и значения переменных могут содержать секреты.

Вот, примерно, так:

Веб-консоль log-my-errors: список перехваченных PHP ошибок

В исходном интерфейсе строка раскрывалась и показывала stack trace. Проверяйте и такой вывод только на синтетических данных: пути файлов и аргументы вызовов тоже могут быть чувствительной информацией.

Веб-консоль log-my-errors: раскрытый стек PHP ошибки

Логгирование

В исходном PHP-snippet есть LME::log($something) для передачи значения в интерфейс. Передавайте короткое тестовое значение, например строку demo; не отправляйте целиком $_SERVER, $_COOKIE, объект сессии или конфигурацию. В отличие от локального console.log, здесь данные проходят через отдельный процесс.

Веб-консоль log-my-errors: вывод значений LME::log

Вот пока и всё, что умеет log-my-errors.

Технологии

В проекте использованы Node.js, Socket.IO и Knockout.js. PHP-код передаёт сообщение приёмнику, приёмник уведомляет браузер, интерфейс отображает событие. Эта цепочка объясняет, почему адрес слушателя и состав данных нужно проверять до интеграции.

В общем

Завершая опыт, удалите snippet из учебного проекта и остановите приёмник. Для текущей рабочей отладки используйте поддерживаемый инструментарий и официальную документацию Xdebug. Исторический LME полезен прежде всего как небольшой пример разделения сборщика событий, канала доставки и браузерного представления.

Заметки и перевод: @jsmarkus