Проблема
При попытке выполнить данный код для объекта Elma365 (в текущем случае для произвольного приложения)
Вылезает следующая ошибка
Это связано с тем, что в каких-то полях содержится циклическая ссылка, из-за чего stringify не удаётся преобразовать объект в строку
UPD. В новых версиях это ошибка уже не падает (проверял на 2024.6.0 Saas), но вывод для большей наглядности можно всё равно улучшить
Решение
Среди возможных решений можно выделить следующие:
1) Использовать console.log. Но такой способ не отработает на серверных скриптах и к тому же логи не сохранятся в переменную процесса
2) Передавать в JSON.stringify не объекты Elma. Например, JSON.stringify(item.id). Но это неудобно каждый раз передавать необходимый список полей
3) Использовать replacer. С помощью него мы переопределим стандартное поведение JSON.stringify.
Сначала напишем вспомогательные функции.
Код:
//Проверяем, что item - объект Elma, а не примитивный тип
function IsRefItem(item: any): item is RefItem<any> {
if (!item || typeof item !== "object") return false;
return ('namespace' in item && typeof item.namespace === "string") && ('code' in item && typeof item.code === "string")
}
Код:
//Проверяем, что у нашего объекта Elma есть поле data
function IsItemData(item: RefItem<any>): item is Item<any> {
return 'data' in item;
}
Код:
//Проверяем, входит ли key в список исключающих ключей
function isSkipKey(key: string) {
return ["factory", "_fields", "fields", "field", "_fieldsDataDiff"].some(i => i === key);
}
Через isSkipKey мы отфильтруем избыточные поля для логов. Как раз в некоторых из них и хранится цикличная ссылка
Хочу отметить, что при текущей реализации, если у элемента есть поля с такими ключами, то они тоже исчезнут
Теперь напишем саму функцию replacer:
Код:
/**
* Функция для логирования
* @param title - строковое описание лога
* @param m - содержание лога
* @param show_name - флаг, который указывает на необходимость отображения названия объекта. По умолчанию Да. Если у переменной m не будет поля data, то поле name не будет выведено
* @param show_data- флаг, который указывает на необходимость отображения data объекта. По умолчанию Нет. Если у переменной m не будет поля data, то данное поле не будет выведено
* @param pretty - флаг, который указывает на необходимость отступов в строковом представлении объекта. По умолчанию Да
* @param indentation_num - размер отступа. По умолчанию 4
*/
function log(title: string, m: any, { show_name = true, show_data = false, pretty = true, indentation_num = 4 } = {}) {
if (!Context.data.debug) Context.data.debug = "";
Context.data.debug += `title: ${title}:\n ${JSON.stringify(m, replacer, pretty ? indentation_num : undefined)}\n\n`
function replacer(key: any, value: any) {
if (isSkipKey(key)) return undefined;
if (value instanceof Map) {
return {
dataType: 'Map',
value: Array.from(value.entries())
};
}
if (IsRefItem(value)) {
const output_obj = {
dataType: 'ref_item',
value: { item_id: value.id, item_code: value.code, item_namespace: value.namespace }
};
if (IsItemData(value)) {
if (show_name) (output_obj.value as any).name = value.data.__name;
if (show_data) (output_obj.value as any).data = value.data;
}
return output_obj;
}
return value;
}
}
Попробуем применить нашу функцию: