Как перехватить сообщения, выводимые командой Сообщить в 1С:Предприятии 8

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки, отладки или автоматизации крупных систем на платформе 1С:Предприятие 8 часто возникает необходимость перехватить текстовые сообщения, выводимые стандартным методом Сообщить(). Например, это требуется при массовом проведении документов, выполнении длительных регламентных процедур или интеграции с внешними системами, когда весь ход выполнения процесса нужно зафиксировать в лог-файл, отправить на электронную почту или записать в базу данных.

Стандартный метод Сообщить() выводит информацию напрямую в окно «Служебные сообщения» текущего сеанса пользователя. Напрямую «подписаться» на это событие из встроенного языка платформы невозможно. Тем не менее существует несколько эффективных и надежных способов решения этой задачи — от архитектурно правильных методов до системных обходных путей. В этой статье мы подробно рассмотрим каждый из них, разберем примеры кода и проанализируем их применимость в различных условиях.

Метод 1. Сбор сообщений через ПолучитьСообщенияПользователю()

В современных версиях платформы 1С:Предприятие 8 (начиная с редакции 8.2 и в актуальной 8.3) механизм вывода сообщений был полностью переработан. Был введен объект СообщениеПользователю, который работает через специализированную очередь сообщений. Если код выполняется на стороне сервера, платформа автоматически преобразует вызовы устаревшего метода Сообщить() в системные сообщения пользователя и помещает их в этот буфер.

Мы можем программно прочитать этот буфер и очистить его с помощью метода ПолучитьСообщенияПользователю(). Передав в качестве параметра значение Истина, мы извлекаем накопленный массив сообщений и одновременно очищаем очередь, чтобы они не выводились на экран пользователю повторно.

Рассмотрим пример кода, который запускает проведение документов на сервере и перехватывает все выведенные сообщения:


// Вызываем серверный метод проведения документа
ПровестиДокументНаСервере(СсылкаНаДокумент);

// Извлекаем сообщения, сгенерированные в процессе проведения
МассивСообщений = ПолучитьСообщенияПользователю(Истина);

Для Каждого Сообщение Из МассивСообщений Цикл
    // Записываем перехваченные сообщения в собственный лог
    ЗаписьЖурналаРегистрации("ПроведениеДокументов", 
        УровеньЖурналаРегистрации.Информация, 
        , 
        СсылкаНаДокумент, 
        Сообщение.Текст);
КонецЦикла;

Важная особенность: этот метод гарантированно перехватывает сообщения, которые генерируются в коде, выполняющемся на стороне сервера (например, в модулях объектов документов при проведении). Если же вызов Сообщить() происходит непосредственно в коде клиентского приложения, данный метод их не перехватит.

Метод 2. Выполнение кода во внешнем фоновом задании

Наиболее надежным архитектурным решением для управляемого приложения является вынос длительных процедур (таких как групповое проведение документов) во **внешнее фоновое задание**. При выполнении кода в фоновом режиме на сервере интерактивный вывод на экран отсутствует в принципе. Все сообщения, сгенерированные методами Сообщить() и СообщениеПользователю, не пропадают, а буферизуются внутри контекста самого фонового задания.

Разберем по шагам, как запустить процедуру и прочитать сообщения из фонового задания:

  1. Запускаем фоновое задание, передав ему параметры (например, массив документов для проведения).
  2. Ожидаем завершения работы задания.
  3. Считываем массив сообщений из объекта фонового задания.

Посмотрим на практическую реализацию этого алгоритма:


// 1. Формируем параметры и запускаем задание на сервере
ПараметрыПроцедуры = Новый Массив;
ПараметрыПроцедуры.Добавить(МассивДокументов);

ИдентификаторЗадания = ФоновыеЗадания.Выполнить("МойОбщийМодульСервер.ПровестиПакетДокументов", ПараметрыПроцедуры);

// 2. Ожидаем завершения (например, в течение 60 секунд)
Попытка
    ИдентификаторЗадания.ОжидатьЗавершения(60);
Исключение
    // Обработка таймаута или аварийного завершения задания
КонецПопытки;

// 3. Получаем накопленные сообщения из выполненного задания
МассивСообщений = ИдентификаторЗадания.ПолучитьСообщенияПользователю(Истина);

Для Каждого ТекущееСообщение Из МассивСообщений Цикл
    // Записываем логи в текстовый файл или базу данных
    ЗаписатьСообщениеВФайлЛога(ТекущееСообщение.Текст);
КонецЦикла;

Использование фоновых заданий позволяет полностью изолировать процесс выполнения от интерфейса пользователя, предотвратить зависание клиентского приложения и собрать абсолютно все системные сообщения в единую таблицу для последующего анализа.

Метод 3. Замена вызовов Сообщить() на глобальную обертку (Рефакторинг)

Если вы разрабатываете собственную конфигурацию или имеете возможность изменять существующую (например, в режиме расширения или сняв ее с поддержки), лучшим решением с точки зрения качества кода будет полный отказ от прямого использования системной функции Сообщить().

Вместо этого создается специализированный общий модуль (например, ОбщегоНазначенияКлиентСервер), в котором объявляется экспортная процедура-обертка. В современных типовых конфигурациях фирмы 1С (ERP, КА, УТ, БП) такой механизм уже внедрен по умолчанию и называется ОбщегоНазначения.СообщитьПользователю().

Давайте проанализируем пример создания собственной процедуры логирования:


// В общем модуле ЛогированиеВывода (Клиент, Сервер, Внешнее соединение)
Процедура ЗаписатьСообщение(ТекстСообщения, Важность = "Информация") Экспорт
    
    // Стандартный вывод пользователю на экран
    Сообщить(ТекстСообщения);
    
    // Параллельное логирование в текстовый файл на сервере
    ИмяФайлаЛога = "C:\Logs\1C_UserMessages.log";
    ЗаписьТекста = Новый ЗаписьТекста(ИмяФайлаЛога, КодировкаТекста.UTF8, , Истина);
    ЗаписьТекста.ЗаписатьСтроку("" + ТекущаяДата() + " [" + Важность + "] " + ТекстСообщения);
    ЗаписьТекста.Закрыть();
    
КонецПроцедуры

Для внедрения этого метода необходимо выполнить глобальный поиск по всей конфигурации по ключевому слову Сообщить( и заменить все вызовы на вашу новую процедуру ЛогированиеВывода.ЗаписатьСообщение(. Это позволит централизованно управлять логированием: в любой момент вы сможете отключить запись в файл, перенаправить вывод в Журнал регистрации, Telegram-бота или внешнюю базу данных SQL, не меняя код в сотнях документов.

Метод 4. Программное чтение Журнала регистрации

Многие разработчики не знают, что в типовых конфигурациях 1С реализована встроенная опция дублирования всех служебных сообщений в Журнал регистрации. За это отвечает константа или настройка в административной панели **«Дублировать сообщения в журнал регистрации»**.

Если эта настройка включена, то при выполнении транзакций и вызове Сообщить() записи автоматически попадают в Журнал регистрации под определенным событием. Вы можете программно прочитать эти данные прямо из текущего сеанса с помощью метода ВыгрузитьЖурналРегистрации().

Давайте посмотрим, как можно отфильтровать и получить эти логи:


// Запоминаем время начала процесса проведения
ВремяНачала = ТекущаяДата();

// Выполняем алгоритм проведения
ПровестиДокументы();

// Настраиваем фильтр для выгрузки логов
ФильтрЖурнала = Новый Структура;
ФильтрЖурнала.Вставить("ДатаНачала", ВремяНачала);
ФильтрЖурнала.Вставить("Пользователь", ИмяПользователя());

ТаблицаЛогов = Новый ТаблицаЗначений;
ВыгрузитьЖурналРегистрации(ТаблицаЛогов, ФильтрЖурнала, "Дата, Сообщение, ИмяСобытия");

// Анализируем полученные данные
Для Каждого СтрокаЛога Из ТаблицаЛогов Цикл
    // Ищем записи, относящиеся к выводу системных сообщений
    Если Найти(СтрокаЛога.ИмяСобытия, "Message") > 0 Тогда
        ТекстСообщения = СтрокаЛога.Сообщение;
        // Обрабатываем сообщение по нашей логике
    КонецЕсли;
КонецЦикла;

Этот метод хорош тем, что он абсолютно безопасен, работает в текущем сеансе и не требует изменения программного кода существующих документов и обработок.

Метод 5. Перенаправление потока вывода с помощью параметра запуска /Out

Если вам требуется перехватывать абсолютно все служебные сообщения при работе системы в фоновом режиме, при запуске подсистемы планировщиком задач (например, Windows Task Scheduler) или при интеграции через командную строку, используйте встроенный параметр запуска платформы 1С /Out.

Вместо системного перенаправления консольного вывода операционной системы (символ > log.txt), который может работать некорректно с графическими приложениями Windows, у платформы 1С есть специализированный ключ:

Пример строки запуска:

1cv8.exe ENTERPRISE /F "C:\Bases\Trade" /N "РоботЛогирования" /P "Пароль" /Out "C:\Logs\system_output.txt" -NoTruncate

Интересно, что этот файл можно читать программно «на лету» прямо из встроенного языка 1С во время текущего сеанса. Для этого используется объект ЧтениеТекста:


ПутьКЛогу = "C:\Logs\system_output.txt";
ФайлЛога = Новый ЧтениеТекста(ПутьКЛогу, КодировкаТекста.ANSI);

СтрокаЛога = ФайлЛога.ПрочитатьСтроку();
Пока СтрокаЛога <> Неопределено Цикл
    // Обрабатываем строку, выведенную платформой
    СтрокаЛога = ФайлЛога.ПрочитатьСтроку();
КонецЦикла;

ФайлЛога.Закрыть();

Метод 6. Эмуляция интерфейсных действий через Windows Script Host (WSH)

Этот способ является «грязным хаком» и рекомендуется к использованию только в крайних случаях — когда конфигурацию категорически нельзя изменять, все процессы выполняются в интерактивном режиме в толстом или тонком клиенте на операционной системе Windows, и другие методы не применимы.

Метод основан на автоматизации интерфейса Windows с помощью объекта WScript.Shell. Мы эмулируем нажатие горячих клавиш для активации окна служебных сообщений, выделения всего текста и копирования его в буфер обмена Windows (клипборд).

Проанализируем пример программного вызова этих команд:


// Создаем COM-объект для отправки системных клавиш
Шелл = Новый COMОбъект("Wscript.Shell");

// Шаг 1. Активируем окно служебных сообщений в 1С (сочетание Ctrl + Alt + O)
Шелл.SendKeys("^%o"); 
ПриостановитьРабота(0.5); // Небольшая задержка, чтобы окно успело активироваться

// Шаг 2. Выделяем весь текст в открывшемся окне (сочетание Ctrl + A)
Шелл.SendKeys("^a");
ПриостановитьРабота(0.2);

// Шаг 3. Копируем выделенный текст в буфер обмена (сочетание Ctrl + C)
Шелл.SendKeys("^c");
ПриостановитьРабота(0.2);

// Шаг 4. Считываем данные из буфера обмена (через объект HTMLFile)
HTMLДокумент = Новый COMОбъект("htmlfile");
ТекстИзБуфера = HTMLДокумент.parentWindow.clipboardData.getData("Text");

Если ЗначениеЗаполнено(ТекстИзБуфера) Тогда
    // Записываем полученный лог
    ЗаписатьТекстВБазу(ТекстИзБуфера);
КонецЕсли;

Недостатки метода: крайне низкая стабильность. Если во время выполнения скрипта пользователь случайно кликнет мышкой в другую область экрана или откроется стороннее окно (например, уведомление антивируса), фокус ввода будет потерян, и комбинации клавиш отправятся в пустоту или другое приложение. Также этот метод не работает на серверах без графического окружения.

Сводная таблица выбора метода

Чтобы вам было проще определиться с выбором архитектурного решения для вашей задачи, мы свели все особенности рассмотренных методов в единую таблицу:

  1. ПолучитьСообщенияПользователю(): Идеально подходит для серверного кода и управляемых форм. Не требует изменения глобальной архитектуры конфигурации, но не перехватывает чисто клиентские сообщения.
  2. Фоновые задания: Рекомендуемый промышленный стандарт для управляемого приложения. Обеспечивает высокую стабильность и параллельность вычислений, но требует разработки серверных процедур.
  3. Глобальный рефакторинг (Обертка): Самый надежный способ для собственных разработок. Дает полный контроль над логами, но требует изменения исходного кода системы.
  4. Параметр запуска /Out: Отлично подходит для автоматических регламентных скриптов, запускаемых по расписанию снаружи 1С.
  5. Использование WSH (SendKeys): Вариант «последней надежды», когда система полностью закрыта для изменений, а логи нужны здесь и сейчас в текущем сеансе.

Выбирайте наиболее подходящий для вашего проекта метод, ориентируясь на требования к надежности, возможность изменения кода конфигурации и среду выполнения сеанса 1С.

← На главную