Как трассировать и отлаживать сложные процессы и функционал
⚠️ Важно перед началом
Ниже приведён реальный рабочий плагин, который используется в проектах Metabot для трассировки и отладки сложной логики.
Этот код:
- не является учебным примером «в вакууме»
- не упрощён специально для урока
- отражает практический инженерный подход, применяемый в боевых сценариях
Вы можете:
- использовать этот плагин как есть в своих проектах
- брать его в качестве основы для собственных трассировщиков
- адаптировать под свои таблицы, поля и процессы
⚠️ Обратите внимание: версия плагина, опубликованная ниже, может отличаться от версии, используемой в текущем продакшене. В боевых окружениях код может эволюционировать, дополняться или оптимизироваться под конкретные задачи.
Рассматривайте этот материал как:
- пример архитектурного подхода
- рабочий шаблон
- отправную точку для собственных решений
Ниже — описание принципов и полный исходный код плагина.
Универсальный трассировщик событий
Автор: Art Yg
Tracer предназначен для записи любых диагностических событий в таблицы базы данных:
- ошибок
- проверок
- ветвлений логики
- внутренних состояний
- отладочной информации
Tracer не знает:
- что такое сессия
- что такое скрипт, команда или триггер
- какие поля считаются «правильными»
Он записывает ровно те данные, которые ему передали.
Основные принципы
- Stateless — не хранит состояние
- Schema-agnostic — не требует фиксированной схемы
- Zero mandatory fields — нет обязательных полей
- Opt-in — работает только если включён
- Side-effect only — не влияет на выполнение кода
Tracer можно удалить из проекта — бизнес-логика продолжит работать.
Минимальные требования
Для начала работы достаточно:
- Создать любую таблицу в БД
(даже без полей, кроме
id) - Добавить атрибут бота
TRACER_CONFIG - Вызвать
trace()
Рекомендуемое, но не обязательное поле таблицы:
-
created_atс автозаполнением (NOW)
Tracer не управляет временем. Если поле есть — БД заполнит его автоматически. Если нет — запись всё равно создаётся.
Конфигурация
Tracer настраивается через один JSON в атрибутах бота: TRACER_CONFIG.
{
"navigation": {
"enabled": true,
"table": "nav_trace"
},
"ai": {
"enabled": false,
"table": "ai_trace"
}
}
- каждый tracer имеет имя (
navigation,ai,apiи т.д.) - у каждого tracera своя таблица (или общая)
- выключенный tracer ничего не пишет
Использование
const Tracer = require("Common.DX.Tracer");
Tracer.trace("navigation", {
category: "NAVIGATION",
component: "Actor",
action: "hasAchievement",
level: "OK",
payload: {
actor_id: 42,
achievement: "first_step",
result: true
}
});
Если tracer выключен — метод молча завершится.
Методы Tracer
Tracer предоставляет несколько эквивалентных методов:
-
trace(name, data)— базовый метод записи -
log(name, data)— алиас для читаемости -
info(name, data)— добавляетlevel: "INFO" -
error(name, data)— добавляетlevel: "ERROR"
Все методы:
- не выбрасывают ошибок
- не изменяют переданные данные
- не влияют на бизнес-логику
Данные события
Tracer принимает любой объект.
Все поля:
- опциональны
- именуются произвольно
- записываются «как есть»
Рекомендуемые (но не обязательные):
-
category— область (NAVIGATION, AI, API) -
component— компонент -
action— действие -
source— источник (system, user, webhook) -
level— уровень ошибки -
payload— любые данные (тип поля TEXTAREA)
Если таблица не содержит поле — БД вернёт ошибку.
В таком случае используйте payload.
Работа со временем
Tracer:
- не добавляет timestamp
- не требует поля времени
- позволяет передать своё время
{
event_time: "2026-01-16T12:00:00Z"
}
или
{
created_at: "2026-01-16T12:00:00Z"
}
Когда использовать
- метод возвращает
true / false, но нужна диагностика - не хочется усложнять ответы ошибками
- важно понять, почему логика не сработала
- нужна отладка без влияния на сценарии
Когда не использовать
- как бизнес-лог
- как аудит-лог
- как аналитику или метрики
Итог
Common.DX.Tracer — простой и ненавязчивый способ видеть, что происходит внутри системы.
Никакой магии. Никаких обязательств. Никакой боли.
/**
* Common.DX.Tracer
*
* Universal stateless event tracer for Metabot.
*
* Version: 1.0.0
* Author: Art Yg
*
* Principles:
* - Stateless
* - Schema-agnostic
* - Zero mandatory fields
* - Opt-in only
* - Side-effect only
*
* Tracer writes exactly what it is given.
* If disabled — does nothing.
*/
class Tracer {
/**
* Internal: get tracer config from bot attributes
*
* Expected bot attr: TRACER_CONFIG (JSON)
*
* Example:
* {
* "navigation": { "enabled": true, "table": "nav_trace" },
* "ai": { "enabled": false, "table": "ai_trace" }
* }
*
* @returns {Object}
*/
static _getConfig() {
try {
const raw =
bot.getJsonAttr('TRACER_CONFIG') ||
bot.getAttr('TRACER_CONFIG');
if (!raw) return {};
if (typeof raw === 'object') return raw;
return JSON.parse(raw);
} catch (e) {
// Tracer must never break execution
return {};
}
}
/**
* Check whether tracer is enabled
*
* @param {string} tracerName
* @returns {Object|null} tracer config or null
*/
static _getTracer(tracerName) {
if (!tracerName) return null;
const cfg = Tracer._getConfig();
const tracer = cfg[tracerName];
if (!tracer) return null;
if (!tracer.enabled) return null;
if (!tracer.table) return null;
return tracer;
}
/**
* Write trace event
*
* @param {string} tracerName - logical tracer name (navigation, ai, api, etc)
* @param {Object} data - arbitrary payload
*
* @returns {boolean} true if written, false otherwise
*/
static trace(tracerName, data = {}) {
const tracer = Tracer._getTracer(tracerName);
if (!tracer) return false;
// We do NOT mutate data
// We do NOT enforce schema
// We do NOT inject fields
try {
table.createItem(tracer.table, data);
return true;
} catch (e) {
// Silent failure by design
return false;
}
}
/**
* Alias for semantic readability
* (optional, but often nice)
*/
static log(tracerName, data = {}) {
return Tracer.trace(tracerName, data);
}
/**
* Explicit error tracer helper
* (pure sugar, no logic)
*/
static error(tracerName, data = {}) {
return Tracer.trace(tracerName, {
...data,
level: data.level || 'ERROR'
});
}
/**
* Explicit info tracer helper
*/
static info(tracerName, data = {}) {
return Tracer.trace(tracerName, {
...data,
level: data.level || 'INFO'
});
}
}
module.exports = Tracer;
Нет комментариев