Запись в журнал регистрации 1С нужна там, где ошибку некому показать: регламентные и фоновые задания, обмены с сайтом, HTTP-сервисы, пакетная обработка документов. Входы, изменения данных и необработанные ошибки платформа пишет сама, а свои события — «заказ не выгружен», «пачка откатилась» — пишет код методом ЗаписьЖурналаРегистрации. Ниже — параметры, модуль-обёртка, ошибки в Попытке, запись с клиента через БСП и режим транзакции.
Где открыть журнал, как отобрать свои события и прочитать их кодом — в статье «Где в 1С журнал регистрации и как искать ошибки». Здесь только запись.
Запись журнала регистрации в 1С 8.3 делает метод глобального контекста ЗаписьЖурналаРегистрации. Обязателен только первый параметр, а дату, пользователя, компьютер и сеанс журнал подставит сам.
| Параметр | Тип | Что передавать |
|---|---|---|
| ИмяСобытия | Строка | тип события «Группа.Событие»: точка задаёт иерархию; сочетания «_$» и «$_» зарезервированы за системными событиями |
| Уровень | УровеньЖурналаРегистрации | Ошибка, Предупреждение, Информация или Примечание; если не указать — Примечание |
| ОбъектМетаданных | ОбъектМетаданных | вид объекта: документ, справочник, регламентное задание, HTTP-сервис |
| Данные | ссылка, Число, Строка, Дата, Булево, Неопределено, Null, Тип | обычно ссылка на документ или элемент справочника |
| Комментарий | Строка | текст события, для ошибки — подробное представление |
| РежимТранзакции | РежимТранзакцииЗаписиЖурналаРегистрации | Независимая (по умолчанию) или Транзакционная |
Метод работает на сервере, в толстом клиенте и во внешнем соединении; в тонком и веб-клиенте нет ни его, ни перечисления УровеньЖурналаРегистрации. Два типовых вызова из серверного кода:
// Имя события — одно на все записи этой операции.
ИмяСобытия = НСтр("ru = 'Обмен с сайтом.Выгрузка заказов'", Метаданные.ОсновнойЯзык.КодЯзыка);
// Минимум: имя, уровень и комментарий. Выгружено — число выгруженных заказов.
ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Информация, , ,
"Выгружено заказов: " + Формат(Выгружено, "ЧН=0; ЧГ=0"));
// С документом Заказ: вид объекта — в «Метаданные», ссылка — в «Данные».
ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Предупреждение,
Заказ.Метаданные(), Заказ, "Телефон не заполнен, заказ выгружен без него");
Стандарт 1С «Использование журнала регистрации» советует группировать события по функции: «Обмен с сайтом.Выгрузка заказов», «Обмен с сайтом.Загрузка остатков». Конкретные значения в имя не кладут: с именем «Не выгружен заказ 125» каждая запись становится отдельным событием, и отбор по событию теряет смысл. Номер и ссылка идут в «Данные» и «Комментарий». Имя задают через НСтр с кодом основного языка — Метаданные.ОсновнойЯзык.КодЯзыка, в БСП ОбщегоНазначения.КодОсновногоЯзыка(), — чтобы у пользователя с другим языком интерфейса событие называлось так же.
Сохраняется не всё, что вызвали. В конфигураторе, в окне Администрирование → Настройка журнала регистрации, выбирают, что регистрировать, и наборы вложены: только ошибки, плюс предупреждения, плюс информация, плюс примечания. Отсюда приём: подробную трассировку пишите уровнем «Примечание» — на рабочей базе её отключают настройкой, не трогая код. А без второго параметра запись получит «Примечание» и при такой настройке не сохранится.
Ссылку передавайте в «Данные», а не в текст комментария: по ней журнал отбирает историю документа, а в форме журнала БСП двойной щелчок по колонке данных открывает сам объект. Вид объекта для «ОбъектМетаданных» берут из ссылки — Ссылка.Метаданные() или Метаданные.НайтиПоТипу(ТипЗнч(Ссылка)), для регламентного задания — его объект из Метаданные.РегламентныеЗадания.
Чтобы во всей конфигурации события писались одинаково, удобно завести один общий модуль. Готовый вариант:
// Общий модуль ЖурналСобытийСервер: флаги «Сервер» и «Внешнее соединение».
// Свои события конфигурация пишет через него: ссылка попадает в колонку «Данные»,
// вид объекта — в «Метаданные», слишком длинный текст обрезается.
// Имя события — константа «Группа.Событие» на основном языке конфигурации:
// НСтр("ru = 'Обмен с сайтом.Выгрузка заказов'", Метаданные.ОсновнойЯзык.КодЯзыка)
// Операция завершилась успешно.
Процедура ЗаписатьИнформацию(ИмяСобытия, Данные = Неопределено, Комментарий = "") Экспорт
ЗаписатьСобытие(ИмяСобытия, УровеньЖурналаРегистрации.Информация, Данные, Комментарий);
КонецПроцедуры
// Проблема, после которой работа продолжилась.
Процедура ЗаписатьПредупреждение(ИмяСобытия, Данные = Неопределено, Комментарий = "") Экспорт
ЗаписатьСобытие(ИмяСобытия, УровеньЖурналаРегистрации.Предупреждение, Данные, Комментарий);
КонецПроцедуры
// Подробности для разбора: сохраняются, только если журнал регистрирует примечания.
Процедура ЗаписатьПримечание(ИмяСобытия, Данные = Неопределено, Комментарий = "") Экспорт
ЗаписатьСобытие(ИмяСобытия, УровеньЖурналаРегистрации.Примечание, Данные, Комментарий);
КонецПроцедуры
// Ошибка из блока Исключение. ИнформацияОбОшибке() получите прямо в этом блоке
// и передайте параметром Информация.
Процедура ЗаписатьОшибку(ИмяСобытия, Информация, Данные = Неопределено, Пояснение = "") Экспорт
// Текст ошибки, модуль и номер строки, цепочка «по причине:».
Текст = ОбработкаОшибок.ПодробноеПредставлениеОшибки(Информация);
Если ЗначениеЗаполнено(Пояснение) Тогда
Текст = Пояснение + Символы.ПС + Текст;
КонецЕсли;
ЗаписатьСобытие(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка, Данные, Текст);
КонецПроцедуры
// Общая точка записи. Транзакционная = Истина привязывает запись к текущей транзакции.
Процедура ЗаписатьСобытие(ИмяСобытия, Уровень, Данные = Неопределено, Знач Комментарий = "",
Транзакционная = Ложь) Экспорт
// Вид объекта — по типу ссылки; для строки, числа или даты будет Неопределено.
ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипЗнч(Данные));
// Стандарт 1С советует держать комментарий в пределах 10 Кб.
МаксимумСимволов = 5000;
Если СтрДлина(Комментарий) > МаксимумСимволов Тогда
Комментарий = Лев(Комментарий, МаксимумСимволов) + Символы.ПС + "(текст обрезан)";
КонецЕсли;
Если Транзакционная Тогда
Режим = РежимТранзакцииЗаписиЖурналаРегистрации.Транзакционная;
Иначе
Режим = РежимТранзакцииЗаписиЖурналаРегистрации.Независимая;
КонецЕсли;
ЗаписьЖурналаРегистрации(ИмяСобытия, Уровень, ОбъектМетаданных, Данные, Комментарий, Режим);
КонецПроцедуры
Разбор ключевых строк:
ЗаписатьОшибку принимает ИнформацияОбОшибке параметром: справка требует вызывать ИнформацияОбОшибке() прямо в блоке Исключение — вне его функция возвращает пустую информацию.ОбработкаОшибок.ПодробноеПредставлениеОшибки — платформа 8.3.17 и новее. На старых релизах — глобальная ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()): с 8.3.17 она не рекомендуется, но работает.Метаданные.НайтиПоТипу(ТипЗнч(Данные)) заполняет колонку «Метаданные» сама. Для строки, числа или даты получится Неопределено — это допустимое значение.МаксимумСимволов — стандарт ориентирует на комментарий до 10 Кб: файлы и ответы веб-сервисов целиком в журнал не пишут. 5000 символов взяты с запасом, а Знач не даёт обрезке испортить переменную вызывающего кода.Вызов из серверного кода — одна строка: ЖурналСобытийСервер.ЗаписатьПредупреждение(ИмяСобытия, Заказ, "Телефон не заполнен, заказ выгружен без него").
Оборачивать каждый вызов в Попытку ради журнала не нужно: по стандарту 1С «Перехват исключений в коде» необработанное исключение платформа сама показывает пользователю и записывает в журнал. Попытка нужна там, где работа должна продолжиться после ошибки: пакет документов, обмен, регламентное задание. Вот тогда исключение обязательно пишут в журнал — пустой блок Исключение прячет проблему так, что потом её не разобрать. Пример — перепроведение документов по одному:
// Перепроведение документов по одному: ошибка в одном документе не останавливает
// остальные, и каждая попадает в журнал со ссылкой на документ.
// Модуль объекта обработки или серверный общий модуль; удобно запускать в фоне.
// Ссылки — массив ссылок на документы, возвращает число ошибок.
Функция ПерепровестиПоОдному(Ссылки) Экспорт
КодЯзыка = Метаданные.ОсновнойЯзык.КодЯзыка;
ИмяСобытия = НСтр("ru = 'Перепроведение.Ошибка проведения'", КодЯзыка);
Ошибок = 0;
Для Каждого Ссылка Из Ссылки Цикл
// Сбрасываем сообщения предыдущего документа, чтобы не приписать их этому.
ПолучитьСообщенияПользователю(Истина);
Попытка
ДокументОбъект = Ссылка.ПолучитьОбъект();
ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
Исключение
Ошибок = Ошибок + 1;
// Текст ошибки, модуль, номер строки и цепочка «по причине:».
Текст = ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
// Причины отказа документ выводит сообщениями пользователю,
// в текст исключения они не попадают — переносим их в комментарий.
Для Каждого Сообщение Из ПолучитьСообщенияПользователю(Истина) Цикл
Текст = Текст + Символы.ПС + Сообщение.Текст;
КонецЦикла;
ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка,
Ссылка.Метаданные(), Ссылка, Текст);
КонецПопытки;
КонецЦикла;
// Итог — отдельной записью: «Информация», если всё прошло, иначе «Предупреждение».
Если Ошибок = 0 Тогда
Уровень = УровеньЖурналаРегистрации.Информация;
Иначе
Уровень = УровеньЖурналаРегистрации.Предупреждение;
КонецЕсли;
ЗаписьЖурналаРегистрации(НСтр("ru = 'Перепроведение.Итог'", КодЯзыка), Уровень, , ,
СтрШаблон("Документов: %1, с ошибками: %2", Ссылки.Количество(), Ошибок));
Возврат Ошибок;
КонецФункции
ПолучитьСообщенияПользователю(Истина): причины отказа проведения — не хватает остатка, не заполнен склад — документ выводит сообщениями, в текст исключения они не попадают. Первый вызов сбрасывает сообщения предыдущего документа, второй забирает причины в комментарий и удаляет их: в фоновом задании показывать их некому.Ссылка.Метаданные() и Ссылка в третьем и четвёртом параметрах — ошибка найдётся отбором по документу.Такую функцию удобно запускать в фоне, чтобы не держать форму, — см. «Фоновое задание 1С».
Как записать в журнал регистрации из процедуры &НаКлиенте? Напрямую не выйдет: модуль формы не скомпилируется. Вариантов два — свой серверный вызов &НаСервереБезКонтекста или процедура БСП ЖурналРегистрацииКлиент.ДобавитьСообщениеДляЖурналаРегистрации. Пример — форма, которая раз в минуту обновляет статус обмена:
// Модуль формы «Монитор обмена» (конфигурация на БСП). Статус обновляется
// раз в минуту; ошибка в обработчике ожидания всплывала бы окном каждую минуту,
// поэтому её ловим, пишем в журнал и отключаем опрос.
&НаКлиенте
Процедура ПриОткрытии(Отказ)
ПодключитьОбработчикОжидания("ОбновитьСтатусОбмена", 60);
КонецПроцедуры
&НаКлиенте
Процедура ОбновитьСтатусОбмена()
Попытка
Элементы.ДекорацияСтатус.Заголовок = СтатусОбменаНаСервере(); // надпись на форме
Исключение
ОтключитьОбработчикОжидания("ОбновитьСтатусОбмена");
Инфо = ИнформацияОбОшибке();
// Уровень — строкой: перечисления УровеньЖурналаРегистрации на клиенте нет.
// Истина — записать сразу, не дожидаясь очереди.
ЖурналРегистрацииКлиент.ДобавитьСообщениеДляЖурналаРегистрации(
НСтр("ru = 'Обмен с сайтом.Монитор обмена'", ОбщегоНазначенияКлиент.КодОсновногоЯзыка()),
"Ошибка", ОбработкаОшибок.ПодробноеПредставлениеОшибки(Инфо), , Истина);
// Без БСП вместо вызова выше: ЗаписатьОшибкуКлиента(ОбработкаОшибок.ПодробноеПредставлениеОшибки(Инфо));
ПоказатьОповещениеПользователя(НСтр("ru = 'Статус обмена не обновляется'"), ,
ОбработкаОшибок.КраткоеПредставлениеОшибки(Инфо));
КонецПопытки;
КонецПроцедуры
&НаСервереБезКонтекста
Функция СтатусОбменаНаСервере()
// Здесь — чтение статуса из своего регистра или запрос к сайту.
Возврат "Проверено " + Формат(ТекущаяДатаСеанса(), "ДЛФ=DT");
КонецФункции
// Несколько событий — один серверный вызов: сообщения копятся в очереди БСП,
// последний вызов с ЗаписатьСобытия = Истина отправляет всю очередь.
// Замечания — массив строк.
&НаКлиенте
Процедура ЗаписатьЗамечанияПроверки(Замечания)
ИмяСобытия = НСтр("ru = 'Обмен с сайтом.Проверка настроек'", ОбщегоНазначенияКлиент.КодОсновногоЯзыка());
ДатаПроверки = ОбщегоНазначенияКлиент.ДатаСеанса(); // иначе в комментарии будет время отправки
Для Каждого Замечание Из Замечания Цикл
ЖурналРегистрацииКлиент.ДобавитьСообщениеДляЖурналаРегистрации(ИмяСобытия, "Предупреждение",
Замечание, ДатаПроверки);
КонецЦикла;
ЖурналРегистрацииКлиент.ДобавитьСообщениеДляЖурналаРегистрации(ИмяСобытия, "Информация",
СтрШаблон(НСтр("ru = 'Проверка завершена, замечаний: %1'"), Замечания.Количество()),
ДатаПроверки, Истина);
КонецПроцедуры
// Без БСП: свой серверный вызов, имя события и уровень задаются на сервере.
&НаСервереБезКонтекста
Процедура ЗаписатьОшибкуКлиента(Текст)
ЗаписьЖурналаРегистрации(НСтр("ru = 'Обмен с сайтом.Монитор обмена'", Метаданные.ОсновнойЯзык.КодЯзыка),
УровеньЖурналаРегистрации.Ошибка, , , Текст);
КонецПроцедуры
Как устроена процедура БСП (сверено по УТ 11.4.14 с БСП 3.1.5 и КА 2.5.20 с БСП 3.1.10):
ЗаписатьСобытия.ЗаписатьСобытия = Истина — сразу серверный вызов, по умолчанию сообщение ложится в очередь. В БСП 3.1.5 она уйдёт на сервер со следующим вызовом с Истина, в БСП 3.1.10 — ещё и сама, по комментарию в модуле обычно в течение 60 секунд (там же есть ЗаписатьСобытияВЖурналРегистрации()). Одиночную ошибку пишите с Истина.ДатаСобытия или дату сеанса на момент записи. Для очереди передавайте ОбщегоНазначенияКлиент.ДатаСеанса(), иначе в журнале будет время отправки, а не события.В обычном приложении (УТ 10.3) клиентский код работает в толстом клиенте, там ЗаписьЖурналаРегистрации вызывают напрямую.
По умолчанию запись независимая: она остаётся в журнале, что бы ни случилось с транзакцией, — так и пишут ошибки. Транзакционная запись привязана к текущей транзакции: в журнале у неё виден статус транзакции, а события отменённых транзакций стандартная форма журнала показывает серым. Это нужно для записей «что сделали с данными» внутри транзакции, в том числе в ОбработкаПроведения и ПриЗаписи, где транзакцию открывает сама платформа: если всё откатилось, такая запись не выглядит выполненным действием. Пример — отмена проведения пачки документов с причиной, которую платформа сама не запишет:
// Отмена проведения пачки документов одной транзакцией: все или ни одного.
// «Данные. Отмена проведения» платформа запишет сама, а причину знает только этот код.
// Записи с причиной привязаны к транзакции: если пачка откатится, они останутся
// со статусом «Отменена» и не будут выглядеть как выполненная отмена.
Процедура ОтменитьПроведениеСПричиной(Ссылки, Причина) Экспорт
ИмяСобытия = НСтр("ru = 'Закрытие периода.Отмена проведения'", Метаданные.ОсновнойЯзык.КодЯзыка);
ТекущийДокумент = Неопределено;
// Пачку держите небольшой: до фиксации транзакция держит блокировки.
НачатьТранзакцию();
Попытка
Для Каждого Ссылка Из Ссылки Цикл
ТекущийДокумент = Ссылка;
ДокументОбъект = Ссылка.ПолучитьОбъект();
ДокументОбъект.Записать(РежимЗаписиДокумента.ОтменаПроведения);
ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Информация,
Ссылка.Метаданные(), Ссылка, "Причина: " + Причина,
РежимТранзакцииЗаписиЖурналаРегистрации.Транзакционная);
КонецЦикла;
ЗафиксироватьТранзакцию();
Исключение
ОтменитьТранзакцию();
// Ошибка — в режиме по умолчанию (Независимая) и с документом, на котором всё упало.
ЗаписьЖурналаРегистрации(ИмяСобытия, УровеньЖурналаРегистрации.Ошибка,
Метаданные.НайтиПоТипу(ТипЗнч(ТекущийДокумент)), ТекущийДокумент,
"Пачка откатилась, проведение не отменено ни у одного документа." + Символы.ПС
+ ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение;
КонецПопытки;
КонецПроцедуры
ОтменитьТранзакцию() в режиме по умолчанию и с документом, на котором всё упало: он хранится в ТекущийДокумент.ВызватьИсключение после записи — вызывающий код и пользователь узнают, что пачка не обработана. Шаблон транзакции целиком и почему нельзя глушить исключение внутри неё — в статье «Транзакции в 1С».&НаКлиенте. Перенесите запись в серверный метод или используйте ЖурналРегистрацииКлиент._$Data$_.Update: системные события кодом не пишут, нужно своё имя.ИнформацияОбОшибке() или число. Нужна строка: ОбработкаОшибок.ПодробноеПредставлениеОшибки(Инфо).ПолучитьИспользованиеЖурналаРегистрации().ОписаниеОшибки() или КраткоеПредставлениеОшибки.ВыгрузитьЖурналРегистрации. Писать события это право не требует.Нет: код пишет события и под обычными пользователями. Право «Журнал регистрации» нужно для просмотра — в УТ 11.4 и КА 2.5 его даёт роль «Просмотр журнала регистрации».
Независимая — да: в режиме по умолчанию запись от транзакции не зависит. Транзакционная тоже останется, но со статусом «Отменена», и стандартная форма журнала покажет её серым.
Да, необработанное исключение платформа записывает сама, в фоновом задании — событием «Фоновое задание. Ошибка выполнения». Своя запись нужна, когда ошибку перехватили и работу продолжили или когда нужен контекст: какой документ, файл, строка обмена.
Пароли, токены, номера карт и персональные данные: журнал видят все, у кого есть право на просмотр, и хранится он долго. Файлы и ответы сервисов целиком тоже не пишите — стандарт ориентирует на 10 Кб на запись.
Журнал — для администратора: разбор ошибок, отбор по пользователю, событию и данным. Если протокол постоянно читает прикладной код или отчёт — статусы выгрузки по каждому заказу, история обмена в форме, — заведите регистр сведений: стандарт 1С не советует выборки из журнала там, где важна скорость.
Если регламентные задания падают без следов, обмен «иногда не доходит», а в журнале каша из событий с номерами в именах, логирование проще навести один раз по всей конфигурации: единые имена событий, ошибки с контекстом, протокол обмена. Разместите задачу на площадке — оценка бесплатная, ответ в течение рабочего дня. Другие доработки — в списке услуг.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.