Глава 6 / 51
Том 1 · Старт и ментальная модель (финал)

Отладка: логи, список fail-fast и учения

В XServer нет админ-панели; отладка держится на трёх вещах: структурных логах старта, ошибках fail-fast, называющих точное поле, и тестовых / ученийных базлайнах, которые вы можете запустить сами. Глава превращает их в ваш первый справочник отладчика.

Обзор

Позиционирование: заключительная глава тома 1. Нового синтаксиса не будет — выдаются три оружия: словарь чтения логов (6.2), таблица ошибок конфигурации (6.3) и базлайны для проверки «не только ли у меня сломано» (6.4). Сюда стоит возвращаться из каждого следующего тома. Отладка на уровне памяти (падения / выход за границы / утечки) — по прямым ссылкам в учебник xrt.

6.1 Введение

Сервер без интерфейса управления обязан вырастить отладочные возможности в другом месте. Выбор XServer: сделать диагностику детерминированными событиями — одна и та же конфигурация и состояние всегда дают одни и те же строки лога в том же порядке; ошибки конфигурации всегда называют конкретное поле при старте и завершают процесс, никогда не «работая с дефектом». Детерминизм = сопоставимость: у каждой аномальной строки, которую вы видите, есть близнец в тест-кейсах исходников.

Три секции покрывают три домена отказов: период старта (порядок лога + список fail-fast), период остановки (последовательность корректной остановки) и период работы (тестовые и ученийные базлайны).

6.2 Система логирования

Словарь последовательности старта

Глава 2 разбирала нормальную последовательность построчно; здесь добавляем аномальные ветви — что значит, если строка «не появилась» или «выглядит иначе»:

НаблюдениеДиагнозДействие
Остановилось на config load failedНедопустимое поле xs.json (см. 6.3)Исправить поле, названное в ошибке
Нет engine startedНе поднялся движок конкурентности (редко)Проверить ресурсы системы / лимиты потоков
Нет script loadedПровал компиляции TCC — ошибка содержит вывод компилятораЧинить C-код; ошибки указывают строки devfile
Строки с [префиксом приложения] нетServiceInit не выполнился или не логируетПроверить devfile и экспорт Init
Остановилось на bind failedПорт занят / не хватает правnetstat по порту; при нескольких слушателях занятые откатываются
gen N растёт снова и сноваИдут горячие перезагрузки (или рестарты)Выяснить, кто подаёт reload

Последовательность остановки

Пять фаз после Ctrl+C / SIGTERM (тестовый базлайн содержит утверждения на последние две строки):

Корректная остановка · вид лога
[xs] stopping
(прекратить приём и утилизировать скрипты → дождаться терминальных состояний сети → утилизировать custom → вычистить очередь деструкций)
[xs] engine stopped
[xs] bye
У остановки нет принудительного демонтажа по тайм-ауту: пока длинные соединения не закрыты, процесс просто ждёт. Это осознанный размен «корректность прежде всего» — во внешнюю страховку по тайм-ауту в продакшне выступает сервис-менеджер (xs.service / systemd; глава 37).

Откуда берутся логи приложения

Строки с префиксом вроде [demo] — это ваш собственный printf: логирование скриптов XServer — стандартный вывод. Хотите структуру — берите семейство logger из xrt (76 API: уровни, форматирование, несколько приёмников), см. блок «Что дальше».

6.3 Список ошибок fail-fast

У валидации xs.json 21 тест-кейс (A-матрица func_test), все с утверждением «exit=1 и ошибка называет конкретное поле». Частые выдержки:

Сообщение (выдержка)ПричинаИсправление
field 'services' expect arrayКорневой services не массивПроверить структуру JSON и правописание
duplicate name ...Повтор имени server / hostимена server глобально уникальны; host — уникальны внутри server
missing portУ сервиса не-custom нет portДобавить port (1–65535)
tls on udpСервису udp назначен tlsУбрать — в UDP нет понятия TLS
recv limit out of rangeНапример udp recv_limit > 65535Уменьшить до диапазона
static headers зарезервированный заголовокВ static.headers попал зарезервированный заголовок вроде Transfer-EncodingУбрать один из 8 зарезервированных
ключ error_pages вне диапазонаКод состояния вне 400–599Исправить ключ
корень статики отсутствуетКаталог в host path не существуетСоздать каталог или сменить path
certificate uses RSA but private key uses ECНесовпадение семейств алгоритмов сертификата и ключаВзять парные cert/key (диагностика называет оба)
expects exactly one supported PEM blockВ файле ключа несколько / битых блоков PEMОставить ровно один полный поддерживаемый блок

Проектная позиция: лучше отказать в старте, чем молча деградировать — вы никогда не встретите самый опасный тихий отказ «настроили TLS, а фактически работает открытый текст».

6.4 Остановка и учения

«Не только ли у меня сломано?» — встроенные в репозиторий базлайны и есть контрольная группа для ответа:

БазлайнСодержимоеПрименение
test.bat / test.shСборка → дымовой тест (эхо HTTP/WS/TCP/UDP + 11 утверждений по логу) → функциональная матрица → матрица перезагрузок, одной командойПервым делом при подозрении на среду
func_test40 сценариев с утверждениями: 21 случай fail-fast конфигурации, векторы обхода путей, штормы reloadТочная локализация поведения на границах протоколов
reload_matrixПроверка передачи поколений на 7 эндпоинтах, четыре протокола × TLSСверка поведения горячей перезагрузки
drillШестичасовые учения: slowloris, бомбы пайплайнов, бомбы перезагрузок битых скриптов + замеры RSSПодозрения на память / стабильность
routebenchСравнение задержек на одной машине с Go / PHP / FastAPIОценка регрессий производительности

Отладка на уровне памяти (падения, выход за границы, утечки) использует систему отладки памяти xrt: канарейки спереди и сзади, очереди изоляции UAF, кольцевой журнал событий аллокации, отчёты TEXT/JSON — включается в конфигурации сборки хоста; см. главу 6 учебника xrt по ссылке в конце.

6.5 Контракт

  • Детерминизм: одинаковый вход всегда даёт одинаковую последовательность лога — любая «мистика» указывает на конфигурацию или среду.
  • Fail-fast: ошибки старта все называют поле и завершают процесс; устойчивые ошибки времени работы проявляются кодами состояния на уровне соединения / разрывами и не роняют процесс.
  • Порядок отладки: сначала лог (найти фазу) → затем список fail-fast (сопоставить сообщение) → в конце базлайны (изолировать факторы среды).

Частые ошибки

Поменяли конфиг и ждёте автоприменения
Симптом
Изменили port / workers / добавили host — поведение не меняется.
Причина
Большинство полей xs.json собирается при старте; в рантайме горячо обновляются только скрипты и сертификаты (через reload). Структурные изменения конфигурации требуют рестарта.
✗ Ошибка
/* правим xs.json и просто ждём */
vim xs.json   # порт не сменился?
✓ Правильно
/* скрипты/сертификаты: подать reload (том 4)
 * структурные изменения: рестарт процесса */
«Фантомный заголовок» в dev_inc
Симптом
Скрипт подключает старый заголовок, которого нет по правильному пути; компиляция ведёт себя странно (используются неверные объявления).
Причина
Относительный поиск по dev_inc при откате цепляет одноимённый файл из текущего рабочего каталога — в тестовом наборе есть специальный кейс с «заголовком-приманкой», охраняющий эту границу.
✗ Опасно
#include <config.h>
/* куда попало: includes/config.h
 * или другой config.h из CWD? */
✓ Правильно
/* уникальные имена вместо общих */
#include <myapp_config.h>
«Не выходит после Ctrl+C» принято за дедлок
Симптом
После Ctrl+C процесс долго не исчезает.
Причина
Корректная остановка ждёт естественного завершения живых соединений (без принудительного демонтажа). Длинные соединения (WebSocket / push) растягивают ожидание.
✗ Ошибка диагностики
/* «дедлок!» → сразу kill -9
 * теряем и место отладки, и семантику корректности */
✓ Правильно
/* посмотреть, на какой фазе лог;
 * дать приложению /shutdown для завершения соединений;
 * внешний тайм-аут — от сервис-менеджера (глава 37) */

Упражнения

БазовыйТри ошибки нарочно и в починку

Намеренно создайте три ошибки конфигурации из списка 6.3 (например dup name, tls on udp, error_pages вне диапазона), зафиксируйте сообщения и исправьте.

Приёмка: все три сообщения находятся в таблице 6.3, после исправления старт проходит.
ПродвинутыйСборка лога из фраз

Перемешайте нормальный лог старта и отдайте напарнику восстановить порядок по фазам сборки; затем удалите одну строку — пусть скажет, «какого шага не хватает и где застрянет следующий».

Приёмка: место каждой строки и последствия её отсутствия объясняются по словарю 6.2.
ВызовЛог запросов для своего приложения

Напишите модуль логирования на уровне modules: на каждый запрос выводить метод, путь, код состояния, длительность (подсказка: высокоточные часы xrt; в рамках контрактных линий главы 3 определите, что ему доступно). По канареечной логике drill проверьте, что функциональность не задета.

Приёмка: лог различает порядок завершения двух конкурентных запросов; под нагрузкой нет функциональных регрессий.

Итоги

ТезисВывод
Три оружияДетерминированные логи (6.2), список fail-fast (6.3), тестовые базлайны (6.4)
Словарь стартаУ отсутствия каждой строки лога есть ясная диагностическая ветвь
Fail-fast21 кейс конфигурации называют поле и завершаются — работать «с дефектом» нельзя
ОстановкаПять фаз без демонтажа; внешний тайм-аут — от сервис-менеджера
Отладка памятиСистема отладки памяти xrt (канарейки / UAF / отчёты), ссылки в учебник xrt
Что дальше по xrt