В протоколе STOMP вся коммуникация между клиентом и брокером строится вокруг текстовых фреймов. Каждый фрейм представляет собой структурированное сообщение, включающее команду, заголовки и тело. При разработке клиентской логики на STOMP.js именно эти фреймы становятся ключевой точкой наблюдения за системой: подписки, отправка сообщений, подтверждения доставки и ошибки проходят через них.
Логирование фреймов позволяет увидеть реальную картину обмена данными, а не только абстракции уровня API библиотеки. Это особенно важно при работе с брокерами сообщений (ActiveMQ, RabbitMQ, Artemis), где поведение может зависеть от заголовков, формата payload и состояния соединения.
Каждый STOMP-фрейм имеет строго определённую структуру:
Пример фрейма:
SEND
destination:/queue/orders
content-type:application/json
{"id":1,"status":"created"}
При логировании важно разделять эти части, поскольку анализ только тела сообщения часто не даёт полной картины происходящего. Ошибки нередко скрываются в заголовках: неверный destination, отсутствие ack, неправильный content-type.
STOMP.js предоставляет встроенный механизм отладочного вывода через
функцию debug. Она вызывается при каждом значимом событии
клиента: подключение, отправка, получение сообщений, ошибки.
Базовое включение логирования:
import { Client } from '@stomp/stompjs';
const client = new Client({
brokerURL: 'ws://localhost:15674/ws',
debug: function (str) {
console.log('[STOMP]', str);
}
});
Функция debug получает строковые сообщения, которые уже
сериализованы библиотекой. Это самый простой уровень наблюдения, но он
не даёт доступа к структурированному представлению фрейма.
Для более глубокого контроля используется разделение логики на входящие и исходящие события клиента.
Исходящие данные проходят через методы:
activate()publish()subscribe()deactivate()Пример перехвата отправки сообщений:
client.publish({
destination: '/queue/orders',
body: JSON.stringify({ id: 1 }),
headers: {
'content-type': 'application/json'
}
});
Для логирования можно обернуть метод publish:
const originalPublish = client.publish.bind(client);
client.publish = function (frame) {
console.log('[OUT]', frame.destination, frame.headers, frame.body);
return originalPublish(frame);
};
Такой подход позволяет фиксировать фактические данные до их сериализации в STOMP-фрейм.
Подписка на топики создаёт поток входящих сообщений:
client.subscribe('/topic/updates', (message) => {
console.log('[IN]', {
destination: message.headers.destination,
headers: message.headers,
body: message.body
});
});
Объект message уже является распакованным представлением
STOMP-фрейма. Однако важно учитывать, что:
body всегда строкаSTOMP.js не предоставляет прямого API для низкоуровневого перехвата сырого фрейма, но это можно реализовать через модификацию WebSocket.
const ws = new WebSocket('ws://localhost:15674/ws');
const originalSend = ws.send.bind(ws);
ws.send = function (data) {
console.log('[WS OUT]', data);
return originalSend(data);
};
Такой подход позволяет видеть реальные STOMP-фреймы до их передачи на сервер.
ws.addEventListener('message', (event) => {
console.log('[WS IN]', event.data);
});
На этом уровне данные ещё не преобразованы STOMP.js, что даёт возможность анализировать протокол в чистом виде.
При работе с большим количеством сообщений простого console.log недостаточно. Логи должны быть структурированы.
Рекомендуемый формат:
[STOMP OUT] COMMAND
destination: /queue/orders
headers: {...}
body: {...}
Пример реализации форматтера:
function logFrame(direction, frame) {
console.group(`[STOMP ${direction}]`);
console.log('destination:', frame.destination);
console.log('headers:', frame.headers);
console.log('body:', frame.body);
console.groupEnd();
}
Особое внимание требуется ERROR-фреймам. Они могут содержать:
STOMP.js обрабатывает ошибки через callback:
const client = new Client({
brokerURL: 'ws://localhost:15674/ws',
onStompError: (frame) => {
console.error('[STOMP ERROR]', frame.headers, frame.body);
}
});
Также возможны транспортные ошибки WebSocket:
client.onWebSocketEr ror = (event) => {
console.error('[WS ERROR]', event);
};
Помимо фреймов, важно отслеживать состояния клиента:
client.onConn ect = () => {
console.log('[STOMP] connected');
};
client.onDisconn ect = () => {
console.log('[STOMP] disconnected');
};
client.onWebSocketCl ose = () => {
console.log('[WS] closed');
};
Согласованное логирование состояния и фреймов позволяет восстановить полную картину поведения системы.
При логировании STOMP-фреймов часто попадают данные, которые не должны отображаться в консоли:
Механизм фильтрации:
function sanitize(headers) {
const copy = { ...headers };
if (copy.authorization) {
copy.authorization = '***';
}
if (copy.token) {
copy.token = '***';
}
return copy;
}
Использование:
console.log('[IN]', {
headers: sanitize(message.headers),
body: message.body
});
При большом потоке сообщений логирование становится узким местом. Основные проблемы:
Оптимизированный подход:
const logQueue = [];
function enqueueLog(entry) {
logQueue.push(entry);
}
setInterval(() => {
if (logQueue.length > 0) {
console.log(...logQueue.splice(0, 50));
}
}, 200);
Такой буферизированный подход снижает нагрузку на интерфейс.
Для практических систем логирование STOMP-фреймов обычно делится на уровни:
Пример конфигурации:
const LOG_LEVEL = 'DEBUG';
function log(level, ...args) {
const levels = ['ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE'];
if (levels.indexOf(level) <= levels.indexOf(LOG_LEVEL)) {
console.log(`[${level}]`, ...args);
}
}
Единая точка логирования упрощает сопровождение:
class StompLogger {
constructor(prefix = 'STOMP') {
this.prefix = prefix;
}
out(frame) {
console.log(`[${this.prefix} OUT]`, frame);
}
in(message) {
console.log(`[${this.prefix} IN]`, {
headers: message.headers,
body: message.body
});
}
error(err) {
console.error(`[${this.prefix} ERROR]`, err);
}
}
Интеграция:
const logger = new StompLogger();
client.subscribe('/topic/data', (msg) => logger.in(msg));
client.publish({ destination: '/queue/test', body: 'data' });
Системный анализ логов позволяет выявлять:
При достаточном уровне детализации логов STOMP-фреймов можно восстановить полную карту взаимодействия клиента и брокера, включая скрытые ошибки транспортного уровня и протокольные несоответствия.