События
Всё, что происходит, пока работает задача, — шаги запуска, сообщения монитора, числа нагрузочного потока, кадры Инспектора, завершение задачи — отправляется как событие. В браузере страница сервера получает их по одному WebSocket, /api/events; скрипт может слушать тот же сокет. Настольное приложение получает те же события с теми же именами и тем же содержимым внутри себя.
Подписка
Откройте WebSocket на /api/events сервера:
websocat -H "Authorization: Bearer $TOKEN" ws://127.0.0.1:1430/api/events- Аутентификация такая же, как у остального API: токен в
Authorization: Bearerили cookie сессии браузера. Без неё переход отклоняется с401auth.required. - Origin: клиент, который отправляет заголовок
Origin, должен отправить собственный источник сервера (хост и порт равныHost), иначе переход отклоняется с403auth.origin. Большинство библиотек WebSocket вне браузера его не отправляют. - Все события каждому клиенту. Подписываться не на что: каждый сокет получает каждое событие каждой задачи, кто бы её ни начал. Нужное выбирайте по
eventи поjob_idв содержимом. - Только слушать. Сервер игнорирует то, что отправляет клиент, кроме закрытия; сообщение больше 64 КиБ закрывает сокет.
- Поддержание связи. Сервер отправляет ping каждые 20 с, поэтому молчащий сокет остаётся открытым и через прокси. Когда сервер останавливается, он закрывает все сокеты.
- Ничего не воспроизводится заново. События, отправленные, пока клиент не был подключён, для него потеряны. Клиент, который переподключился, должен прочитать текущее состояние командами (
jobs_list,inspect_snapshot,emulator_exchanges…). - Отставание. Для одного сокета ожидают до 4096 событий. Клиент, который отстаёт сильнее, получает
server://laggedс числом пропущенных.
Формат сообщения
Каждое событие — одно текстовое сообщение с одним объектом JSON:
{ "event": "scan://open", "payload": { "job_id": 9, "ts": 1759600000123, "port": 8080, "banner": null } }| Поле | Что это |
|---|---|
event | Канал, см. ниже |
payload | Значения события; их состав зависит от канала |
Время (ts, first_ms и last_ms пира) — миллисекунды с 1970 года; задержки и другие длительности (*_latency_ms, p50_ms…, ms) — миллисекунды. Ошибки в содержимом — объекты EngineError; их коды перечислены в сообщениях об ошибках.
Каналы
| Канал | Кто отправляет | Когда |
|---|---|---|
experiment://step | Запуск | Шаг начинается, проходит, не проходит, повторяется или сообщает о нагрузке |
experiment://ended | Запуск | Один раз, когда запуск заканчивается сам |
job://ended | Любая задача | Один раз, когда задача заканчивается сама или с ошибкой |
osc://message | Монитор OSC | Каждый пакет |
osc://gen-tick | Генератор OSC | Каждое сообщение или от 30 до 45 раз в секунду при темпе выше 60 сообщений в секунду |
http://burst-progress | Нагрузочный поток HTTP | Каждые 100 мс и в конце |
ws://state | Соединение WebSocket | Подключено, закрыто |
ws://messages | Соединение WebSocket | Каждые 100 мс, если есть что-то новое |
mqtt://state | Соединение MQTT | Подключено, подписано, закрыто |
mqtt://messages | Соединение MQTT | Каждые 100 мс, если есть что-то новое |
mqtt://ack | Соединение MQTT | Публикация QoS 1/2 завершена; на отписку получен ответ |
broadcast://emit-stat | Маяк | Каждые 250 мс и в конце |
broadcast://peers | Приёмник обнаружения | Каждые 400 мс |
netsim://stat | Реле помех | Каждые 250 мс |
storm://stat | Шторм | Каждые 250 мс и в конце |
scan://open | Сканер | Каждый открытый порт |
scan://progress | Сканер | Примерно на каждый 1 % диапазона и в конце |
emulator://activity | Задача эмулятора | Каждые 200 мс, если есть что-то новое |
inspect://batch | Инспектор | Каждые 120 мс при новых кадрах, примерно раз в секунду в тишине, пока захват включён |
server://lagged | Сервер | Клиент отстал |
experiment://step
Один шаг запуска: узел начинается, проходит, не проходит, ждёт повторной попытки, повторяется или сообщает о ходе нагрузки. Запуск, начатый через /api/run, отправляет те же шаги в своём ответе (см. запуски).
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача запуска |
ts | число | Когда |
node_id | строка | Узел |
state | строка | running, passed, failed, retry (попытка не удалась, и шаг после паузы выполняется снова), repeating (ход повторяющегося действия, не чаще раза в секунду) или load (ход нагрузки, не чаще раза в секунду) |
detail | строка | Что произошло, по-английски; пусто для running и failed (см. error) |
message_key | строка или null | Текст интерфейса для этого — как ключ его словаря |
message_params | объект или null | Значения, которые называет message_key |
vars | объект | Переменные, которые записал шаг; пропускается, если их нет |
error | EngineError | Почему шаг не прошёл или почему не удалась попытка (retry); иначе пропускается |
frame | число | Кадр Инспектора для сообщения, которому соответствовало ожидание (или ожидаемый ответ на отправку), если захват был включён; иначе пропускается |
load | объект | Что измерила нагрузка, с прочитанными порогами — в последнем событии шага нагрузки, прошёл он или нет; иначе пропускается. См. нагрузку |
Узел Финиш запуска показывает running, когда его достигает первая ветка, и passed, когда все ветки закончились без сбоя. Значения секретов маскируются во всех полях.
experiment://ended
Запуск закончился сам: он прошёл, не прошёл или вышло время. Отправляется сразу после того же содержимого в job://ended. Запуск, остановленный через job_stop или Остановить всё, не отправляет ни то, ни другое и не сохраняет отчёт.
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача запуска |
kind | строка | experiment |
seed | число | seed, с которым он работал |
profile | строка или null | Его профиль |
overridden | логическое | Часть значений параметров пришла из Запустить с… или из overrides |
error | EngineError или null | Первый сбой запуска; null, если он прошёл |
report_path | строка или null | Его отчёт в runs/ папки данных |
report_error | EngineError или null | Почему отчёт не удалось записать |
job://ended
Задача закончилась сама или с ошибкой. Задача, остановленная через job_stop или jobs_stop_all, его не отправляет.
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача |
kind | строка | osc-monitor, osc-gen, http-burst, netsim, storm, scan, beacon, discovery, mqtt, websocket, emulator или experiment |
error | EngineError или null | Почему она закончилась, если что-то пошло не так |
job://ended запуска несёт и поля experiment://ended. Что заканчивает каждый вид:
kind | Заканчивается, когда | error |
|---|---|---|
osc-monitor | Сокет больше не может принимать | wait.receive_failed |
osc-gen | Её длительность вышла или отправка не удалась | null или transport.* |
http-burst | Достигнут его итог или длительность | null |
storm | Его длительность вышла | null |
scan | Опробован каждый порт диапазона | null |
beacon | Его круги или длительность вышли, либо больше 32 отправок не удались и ни одна не прошла | null или transport.* |
discovery | Сокет больше не может принимать | wait.receive_failed |
mqtt | Брокер закрыл соединение или оно потеряно | transport.* (transport.reset, когда соединение закрыл брокер) или mqtt.protocol |
websocket | Соединение закрылось | null или почему оно потеряно |
netsim | Реле больше не может работать | почему |
emulator | Его сокет вышел из строя | почему |
experiment | Запуск заканчивается | сбой запуска или null |
osc://message
Один пакет UDP, принятый монитором OSC, расшифрованный. Отправляется для каждого пакета, без пачек.
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача монитора |
ts | число | Когда он пришёл |
from | строка | Отправитель, IP:port |
bytes | число | Размер пакета |
messages | object[] | Каждое сообщение пакета (в бандле их несколько): address и args (OscArg[]) |
error | EngineError или null | osc.packet_malformed, если пакет не расшифрован (тогда messages пуст) |
osc://gen-tick
Ход работы генератора OSC: для каждого сообщения при темпе ниже 60 сообщений в секунду; выше — для каждого n-го, где n — темп, делённый на 30 и округлённый вниз, то есть от 30 до 45 раз в секунду.
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача генератора |
ts | число | Когда |
value | число | Только что отправленное значение, до округления до целого или до 32-битного числа с плавающей запятой |
sent | число | Сколько сообщений отправлено |
http://burst-progress
Числа нагрузочного потока HTTP каждые 100 мс, пока он работает, и ещё раз с done: true, когда он заканчивается сам.
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача потока |
ts | число | Когда |
sent | число | Запросов, на которые ответили или которые завершились сбоем |
ok | число | Из них получили ответ со статусом 2xx |
failed | число | Из них с любым другим статусом или без ответа |
missed | число | Запросы потока с заданным темпом, которые слишком долго ждали свободного исполнителя и были пропущены |
rps | число | Запросов в секунду за последние 100 мс; в последнем событии — за весь поток |
last_latency_ms, min_latency_ms, max_latency_ms, avg_latency_ms | число | Задержки на текущий момент |
p50_ms, p90_ms, p95_ms, p99_ms | число | Перцентили всех запросов на текущий момент, включая неудавшиеся, с точностью до 0,5 % |
done | логическое | Последнее событие потока |
ws://state
Соединение WebSocket, открытое через ws_connect, подключилось или закрылось. Соединение, чью задачу остановили, closed не отправляет.
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача соединения |
ts | число | Когда |
state | строка | connected или closed |
handshake | объект | url, peer, local, protocol (подпротокол, который выбрал сервер, или null) и ms (подключение и переход) |
closed | объект или null | При closed: code, reason, by (client, server или lost) и error |
ws://messages
Что соединение WebSocket отправило и приняло с прошлого события, каждые 100 мс, если есть что-то новое.
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача соединения |
ts | число | Когда |
messages | object[] | По порядку: ts, dir (rx принято, tx отправлено), kind (text или binary), text (первые 64 КиБ в UTF-8, у двоичного сообщения тоже; байты, не являющиеся UTF-8, превращаются в �), hex (первые 4096 байт двоичного сообщения в hex, иначе null), bytes (полный размер) и truncated (показано не всё: больше 64 КиБ текста, больше 4096 байт двоичных данных) |
dropped | число | Сообщения, не попавшие в это событие, потому что их было больше 2000; самые старые уходят первыми |
mqtt://state
Состояние соединения MQTT изменилось.
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача соединения |
ts | число | Когда |
state | строка | connected; subscribed после каждого ответа на подписку; closed, когда соединение закончилось (но не когда остановили его задачу) |
broker | строка | host:port |
error | EngineError или null | Почему закончилось соединение с closed (transport.reset, когда его закрыл брокер); иначе null |
grants | object[] | При subscribed: каждый запрошенный фильтр, с filter, qos (выданный) и accepted; иначе пусто |
mqtt://messages
Что соединение MQTT получило с прошлого события, каждые 100 мс, если есть что-то новое. Повторно доставленное сообщение QoS 2 показывается один раз.
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача соединения |
ts | число | Когда |
messages | object[] | ts, topic, payload (в UTF-8; байты, не являющиеся UTF-8, превращаются в �), bytes, qos, retain, dup |
dropped | число | Сообщения, пропущенные потому, что за 100 мс пришло больше 4000; самые старые уходят первыми |
mqtt://ack
Брокер завершил то, о чём просило соединение.
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача соединения |
ts | число | Когда |
kind | строка | published (публикация QoS 1 или 2 завершена) или unsubscribed |
packet_id | число | Идентификатор пакета MQTT |
topic | строка или null | Опубликованный топик; null для unsubscribed |
broadcast://emit-stat
Счётчики маяка каждые 250 мс и ещё раз, когда он заканчивается сам, с pps равным 0.
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача маяка |
ts | число | Когда |
targets | число | Адресатов в каждом круге |
rounds | число | Отправлено кругов |
packets, bytes | число | Отправлено датаграмм и байтов |
errors | число | Отправок, которые не удались |
pps | число | Датаграмм в секунду за последние 250 мс |
broadcast://peers
Что услышал приёмник обнаружения, каждые 400 мс.
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача приёмника |
ts | число | Когда |
peers | object[] | Недавно услышанные первыми: addr, proto, packets, bytes, first_ms, last_ms, last_summary, responded (его пакеты, на которые ответили, считая по мере прихода); не больше 512 |
packets, bytes | число | Всё принятое |
responses | число | Отправлено ответов |
netsim://stat
Счётчики реле помех каждые 250 мс. Реле узлов Сетевые помехи запуска сообщают о себе в отчёте запуска.
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача реле |
ts | число | Когда |
received, forwarded | число | Датаграммы или фрагменты на входе и на выходе |
dropped | число | Потеряно из-за loss, пачек или offline (UDP; реле TCP в состоянии offline держит поток и ничего не теряет) |
throttled | число | UDP: отброшено из-за ограничения полосы или потому, что в пути уже было слишком много. TCP: фрагменты, которые удерживали свой поток из-за ограничения полосы |
duplicated, corrupted, reordered | число | Что профиль сделал с ними |
bytes | число | Передано байтов |
connections, reset, stalled | число | TCP: соединений принято, сброшено, оставлено полуоткрытыми; пропускается, пока 0 |
profile | строка | Профиль, по которому оно вносит помехи сейчас, как его называет лента запуска: имя или то, что он делает (60 ms ±25 · loss 2%) |
storm://stat
Счётчики шторма каждые 250 мс и ещё раз, когда он заканчивается сам, с pps и mbps равными 0.
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача шторма |
ts | число | Когда |
packets, bytes | число | Отправлено датаграмм (или соединений TCP) и байтов |
errors | число | Отправки или соединения, которые не удались |
pps | число | В секунду за последние 250 мс |
mbps | число | Мегабит в секунду за последние 250 мс |
scan://open
Сканер нашёл открытый порт.
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача сканирования |
ts | число | Когда |
port | число | Порт |
banner | строка или null | Что служба отправила первым, если баннеры запрашивались и она что-то сказала в течение 400 мс |
scan://progress
Как далеко продвинулось сканирование: примерно на каждый 1 % диапазона и когда оно заканчивается само, с done, равным total (последнее может прийти дважды).
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача сканирования |
ts | число | Когда |
done | число | Опробовано портов |
total | число | Портов в диапазоне |
open | число | Найдено открытых портов |
emulator://activity
Что эмулятор, запущенный через emulator_start, принял и на что ответил с прошлого события, каждые 200 мс, если что-то изменилось (обмен, отключение или включение либо сообщение, которое брокер MQTT не смог доставить). Узлы Эмулятор запуска его не отправляют; их счётчики — в отчёте запуска.
| Поле | Тип | Значение |
|---|---|---|
job_id | число | Задача эмулятора |
ts | число | Когда |
counts | объект | total, unmatched, failed, down, hits (по правилам) и missed (MQTT; пропускается, пока 0) — как у emulator_exchanges |
forced | строка | unavailable, reset или timeout, пока он отключён; иначе пропускается |
exchanges | object[] | Новые обмены, как их перечисляет emulator_exchanges, но без data; не больше 200 |
dropped | число | Обмены сверх первых 200 за интервал, здесь не отправленные; emulator_exchanges по-прежнему хранит последние 500 |
inspect://batch
Новые кадры Инспектора. Отправляется только пока захват включён: каждые 120 мс, если есть новые кадры, и примерно раз в секунду, если их нет, чтобы счётчики оставались актуальными.
| Поле | Тип | Значение |
|---|---|---|
frames | object[] | Новые кадры, самые старые первыми, не больше 250; без их байтов (используйте inspect_payload) |
stats | объект | Счётчики захвата, CaptureStats |
skipped_now | число | Кадры, захваченные с прошлой пачки, но не вошедшие в эту: пришло больше 250 или буфер их отпустил. В экспорте они остаются, пока их держит буфер |
server://lagged
Только сервер. Этот клиент отстал больше чем на 4096 событий и часть пропустил. Прочитайте состояние заново командами.
| Поле | Тип | Значение |
|---|---|---|
skipped | число | Сколько событий он пропустил |