При разработке подсистем логирования, отладки и аудита часто возникает задача зафиксировать точное место, откуда был вызван тот или иной метод. В языках программирования с развитой рефлексией (например, C# с атрибутом CallerMemberName или C++ с макросом __FUNCTION__) имя вызывающего метода можно получить автоматически без лишних усилий. Разберем, как обстоят дела с подобным функционалом в платформе 1С:Предприятие и какие существуют практические способы решения этой задачи.
В платформе 1С:Предприятие штатный механизм получения имени исполняемой процедуры или вызывающего контекста «из коробки» отсутствует. Встроенный язык не предоставляет специализированных системных функций или псевдопеременных, которые могли бы вернуть имя текущего метода одной строкой кода. Поэтому программисту приходится выбирать между несколькими альтернативными подходами в зависимости от требований к производительности и удобству поддержки кода.
Самый надежный, легко читаемый и производительный подход — явная передача контекста или имени метода в качестве параметра в вызываемую функцию логирования.
Рассмотрим, как выглядит реализация этого способа на практике:
Процедура РассчитатьСкидкуКлиента()
ИмяТекущегоМетода = "РассчитатьСкидкуКлиента";
ЗаписатьСобытиеВЛог("Начало расчета скидки", ИмяТекущегоМетода);
// Основной алгоритм процедуры
ЗаписатьСобытиеВЛог("Завершение расчета скидки", ИмяТекущегоМетода);
КонецПроцедуры
Процедура ЗаписатьСобытиеВЛог(ТекстСообщения, ИсточникВызова = "")
ЗаписьЖурналаРегистрации(
"ЛогированиеОпераций",
УровеньЖурналаРегистрации.Информация,
,
,
Строка(ИсточникВызова) + ": " + ТекстСообщения
);
КонецПроцедуры
Преимущества: максимальная скорость работы, отсутствие неявных ошибок, предсказуемость поведения в любых режимах отладки.
Недостатки: необходимость вручную прописывать имя метода при создании и рефакторинге процедур.
Если требуется получить имя вызывающего метода динамически без ручной передачи строки, можно воспользоваться механизмом обработки исключений. Платформа 1С формирует подробный стек вызовов при возникновении ошибки.
Разберем по шагам, как извлечь стек вызовов программно:
Попытка ... Исключение.Исключение получаем объект ИнформацияОбОшибке().ПодробноеПредставлениеОшибки() преобразуем информацию об ошибке в текстовый вид.СтрРазделить() и извлекаем строку стека, предшествующую вызову служебной функции.Посмотрим на пример программной реализации:
Функция ПолучитьИмяВызывающегоМетода() Экспорт
ТекстСтека = "";
Попытка
// Искусственно вызываем ошибку для фиксации стека вызовов
ВызватьИсключение "";
Исключение
Информация = ИнформацияОбОшибке();
ТекстСтека = ПодробноеПредставлениеОшибки(Информация);
КонецПопытки;
// Разбираем текст стека по строкам
МассивСтрок = СтрРазделить(ТекстСтека, Символы.ПС, Ложь);
// В массиве анализируем строки и возвращаем нужный уровень вызова
Если МассивСтрок.Количество() >= 2 Тогда
Возврат МассивСтрок[1];
КонецЕсли;
Возврат "Не удалось определить метод";
КонецФункции
Важные ограничения данного метода:
ВызватьИсключение, что сделает интерактивную отладку крайне неудобной.Чтобы упростить ручной ввод и не терять в производительности, разработчики часто используют средства автоматизации ввода:
Если задача заключается не в выводе данных пользователю, а в системном аудите и поиске узких мест, оптимальным решением является настройка Технологического журнала платформы. При включении сбора событий CALL или EXCP с флагом вывода контекста встроенного языка (p:processName) платформа самостоятельно сохраняет полный стек вызовов в лог-файлы на сервере без необходимости доработки прикладного кода конфигурации.