Всем привет!
Сегодня продолжим рассматривать новый механизм платформы по отображения ошибок в 1С.
Ранее я показывал, как это в базе 1С выглядит со стороны пользователя, администратора и разработчика. А сегодня поговорим о сервисе регистрации ошибок.
Данная статья - текстовый вариант свежего видеоролика по теме.
Содержание
- Видео на YouTube
- Введение
- Разработка HTTP-сервиса
- Смотрим на метод getInfo()
- Смотрим метод pushReport()
- Постобработка данных
- Выводы
- Понравилась статья?
Введение ^
Что это такое? Это некий опубликованный http-сервис, на который автоматически будут отправляться возникающие у пользователей ошибки. Сервис будет их принимать, обрабатывать и хранить. А ответственные за разбор ошибок люди будут далее просматривать накопленные данные.
На чём мы это будем делать? Естественно, на 1С! И начнём прямо сейчас.
Для начала создадим новую базу для разработки. Назовём её "Сервис регистрации ошибок". Запускать её будем с версией 8.3.17. Так же сразу для удобства я сделаю себе хранилище разработки. И теперь можно разрабатывать.
Разработка HTTP-сервиса ^
Первое, что нам нужно в базе - создать новый HTTP-сервис. Назовём его "Основной Сервис". Укажем корневой URL. Пусть будет "main".
Теперь нам нужно добавить новый шаблон URL. Назовём его ПолучитьИнформацию. И в шаблоне укажем "/getInfo". Добавим в шаблон новый метод POST-метод с таким же названием и сразу создадим для него обработчик.
Но для работы сервиса нам нужен ещё один метод. Создадим новый шаблон URL и назовём его "ОтправитьОтчет". Шаблон у него будет "/pushReport". Аналогично прошлому шаблону, создадим POST-метод с обработчиком.
Публикация сервиса ^
В данном видео мы не будем рассматривать процесс установки сервера, публикации базы и так далее. По этой теме есть множество информации и публикация нашего сервиса ничем не отличается от других. По разворачиванию сервера могу посоветовать видео на канале Ильи Низамова. Лично я делал по этому видео: https://nizamov.school/server-1s-apache-ustanovka-apache/
Так что просто примем как факт, что для работы нашего сервиса я уже установил сервер, опубликовал базу и мы готовы начинать.
Смотрим на метод getInfo() ^
Я открою ту тестовую демо-базу, которую мы использовали в прошлой статье. Необходимо, чтобы в её настройках был указан наш сервис регистрации ошибок.
Откроем в демо-базе нашу обработку, которая будет просто совершать ошибку. Появилось новое окно об ошибке и мы можем перейти в отладку нашего сервиса.
Мы сейчас находимся в отладке на методе "ПолучитьИнформацию" ("getInfo"). Давайте посмотрим, что нам пришло в запросе. Для этого выполним метод Запрос.ПолучитьТелоКакСтроку().
Что мы видим? Этим запросом 1С говорит нам информацию о себе. Какая конфигурация, какая платформа и версия приложения. И далее 1С ожидает ответа от этого первого метода. Нужно ли действительно отправлять на сервис регистрации текущую ошибку? Давайте теперь сделаем так, чтобы наш метод отвечал "да".
Разработка метода getInfo() ^
Я подготовил простую функцию, которая поможет нам в этом. Она нужна для преобразования значений 1С в JSON.
Текст функции
// Возвращает значение в виде JSON-строки.
// Преобразованы в JSON-строку (сериализованы) могут быть только те объекты,
// для которых в описании указано, что они сериализуются.
//
// Параметры:
// Значение - Произвольный. Значение, которое необходимо сериализовать в JSON-строку.
//
// Возвращаемое значение:
// Строка - JSON-строка представления значения в сериализованном виде.
//
Функция ЗначениеВСтрокуJSON(Значение) Экспорт
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Значение);
Возврат ЗаписьJSON.Закрыть();
КонецФункции
Теперь создадим структуру ДанныеОтвета. В неё будем помещать передаваемые в качестве ответа данные. Комментарии к свойствам я буду брать из ИТС.
Функция ПолучитьИнформацию(Запрос)
ДанныеОтвета = Новый Структура;
//данная ошибка должна быть отправлена в сервис регистрации
ДанныеОтвета.Вставить("needSendReport", Истина);
//Текст, который будет показан пользователю в качестве дополнительной информации об ошибке (подробнее см. здесь).
//https://its.1c.ru/db/v8317doc#bookmark:dev:TI000002261
//Текст желательно возвращать на том языке, который будет понятен пользователю.
//Информацию о локализации можно получить из запроса, который поступает в метод getInfo.
//Если желательно, чтобы пользователь отправил отчет о конкретной ошибке в сервис регистрации ошибок,
//то с помощью данного свойства можно показать пользователю рекомендацию, мотивирующую на отправку отчета об ошибке.
ДанныеОтвета.Вставить("userMessage", "Чем быстрее разработчик узнает об ошибке, тем скорее она будет исправлена =)");
//тип дампа, который нужно приложить к отчету об ошибке (аналогичен значению атрибута type элемента dump файла logcfg.xml)
ДанныеОтвета.Вставить("dumpType", 1);
ДанныеОтветаСтрокой = ЗначениеВСтрокуJSON(ДанныеОтвета);
Ответ = Новый HTTPСервисОтвет(200);
Ответ.УстановитьТелоИзСтроки(ДанныеОтветаСтрокой);
Возврат Ответ;
КонецФункции
needSendReport - Булево.
Этот параметр говорит 1С, нужно ли дальше отправлять отчет об ошибке или сервис отказывается его принимать. В нашем случае всегда будет "Да".
UserMessage - Строка
В этот параметр можно передать текст, который будет показан пользователю в качестве дополнительной информации. Мы сюда поместим мотивирующую строку
"Чем быстрее разработчик узнает об ошибке, тем скорее она будет исправлена =)"
Так же есть ещё свойство dumpType - это тип дампа, который необходимо приложить к отчету об ошибке. Его мы сейчас рассматривать не будем.
Когда данные готовы, мы можем отправить их в качестве ответа. Но для начала нужно преобразовать их в JSON. Для этого и понадобится та функция, которую я показывал ранее.
Метод getInfo готов.
Обновим базу и снова сымитируем ошибку.
Мы в отладке. Посмотрим, как выглядят данные, которые мы даём в качестве ответа.
И её строковый вариант в формате JSON.
Смотрим метод pushReport() ^
В окне об ошибке мы видим нашу мотивирующую строку. Это значит, что метод getInfo сработал корректно и 1С готова к отправке отчета об ошибке.
Добавим в него комментарий пользователя и нажмём "Отправить".
Теперь мы в отладке на методе "ОтправитьОтчет" ("pushReport"). Посмотрим содержимое объекта "Запрос". Сначала попробуем выполнить метод ПолучитьТелоКакСтроку().
Содержимое явно не строковое. Всё верно - в этом запросе 1С присылает нам файл-архив с отчётом об ошибке. В нём будет содержаться то, что мы рассматривали в прошлом видео - вся информация об случившемся исключении. Получим же его в виде двоичных данных с помощью метода ПолучитьТелоКакДвоичныеДанные().
Это и есть необходимый нам файл. И, фактически, наша цель достигнута - мы создали сервис, который получает отчёты об ошибках пользователей. Но пока что никак их не обрабатываем.
Разработка метода pushReport() ^
И так, мы на входе имеем двоичные данные с архивом, в котором содержится нужная нам информация. Очевидно, что нам нужно её извлечь, обработать и сохранить в базе. Но мы не будем делать это сразу. Потому что на это может потребоваться время. В которое 1С будет ожидать ответа от сервиса. Так что длительные операции мы будем выполнять после. А в самом методе http-сервиса будем просто складывать полученные данные в специальный регистр. Такая запись потребует минимум времени. Не нагружает сервис регистрации ошибок, и при этом гарантирует сохранность данных.
Для начала добавим регистр сведений. Каждый отчёт об ошибке будет сохранён в отдельную запись регистра. Добавим измерение. Значение в нём должно быть максимально уникальным, ведь запросы могут происходить часто. Поэтому сделаем его типом "УникальныйИдентификатор". Можно, конечно, сделать строковый тип с длиной достаточной для идентификатора, но мы же делаем просто прототип и сейчас не будем заморачиваться в таких тонкостях.
И так, у нас есть регистр с измерением. Сами данные отчета будут храниться в ресурсе с типом ХранилищеЗначения.
Теперь создадим методы для работы с регистром. Перейдём в его модуль менеджера. Вставим для красоты "стандартный" БСПшный текст с директивами и областями.
Стандартные области
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
#Область ПрограммныйИнтерфейс
#КонецОбласти
#Область СлужебныйПрограммныйИнтерфейс
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
#КонецОбласти
#КонецЕсли
И создадим экспортную процедуру ДобавитьЗапись(). Входными параметрами будут данные отчёта и идентификатор. При этом второй необязательный - если не передавать, то его значение будет автоматически генерироваться. Внутри метода будет создаваться и записываться новая запись регистра.
А так же процедуру по удалению записи регистра. Она нам понадобится позднее
Модуль менеджера
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
#Область ПрограммныйИнтерфейс
Процедура ДобавитьЗапись(Содержимое, Идентификатор = Неопределено, Дата = Неопределено) Экспорт
МенеджерЗаписи = РегистрыСведений.ПолученныеОтчетыОбОшибках.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Активность = Истина;
МенеджерЗаписи.Идентификатор = ?(Идентификатор = Неопределено, Новый УникальныйИдентификатор, Идентификатор);
МенеджерЗаписи.Содержимое = Содержимое;
МенеджерЗаписи.Дата = ?(Дата = Неопределено, ТекущаяДата(), Дата);
МенеджерЗаписи.Записать(Истина);
КонецПроцедуры
Процедура УдалитьЗапись(Идентификатор) Экспорт
МенеджерЗаписи = РегистрыСведений.ПолученныеОтчетыОбОшибках.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Идентификатор = Идентификатор;
МенеджерЗаписи.Прочитать();
МенеджерЗаписи.Удалить();
КонецПроцедуры
#КонецОбласти
#Область СлужебныйПрограммныйИнтерфейс
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
#КонецОбласти
#КонецЕсли
Вернёмся в метод http-сервиса. Сначала получим двоичные данные из входящего запроса. Это и есть наш файл. Запакуем его в хранилище значения и передадим в наш новый метод регистра сведений. Ответ менять не будем - просто вернём код 200 (успешное выполнение).
Функция ОтправитьОтчет(Запрос)
ДанныеОтчета = Запрос.ПолучитьТелоКакДвоичныеДанные();
ДанныеОтчета = Новый ХранилищеЗначения(ДанныеОтчета);
РегистрыСведений.ПолученныеОтчетыОбОшибках.ДобавитьЗапись(ДанныеОтчета);
Возврат Новый HTTPСервисОтвет(200);
КонецФункции
Теперь обновим базу и зайдём в неё. У нас появился регистр, записей в нём пока нет.
В демо-базе выполним нашу обработку с генерацией ошибки. Нажмём "Отправить" и сразу видим надпись "Отчет отправлен".
Хорошо, перейдём в сервис регистрации. Вот наша запись регистра.
Посмотрим как он будет заполняться в отладке. Для этого поставим точку останова и снова вызовем ошибку в демо-базе.
В ресурсе "Содержимое" находится ХранилищеЗначения. Попробуем извлечь его методом Получить(). Видно, что данные там есть.
Отпускаем отладку. Запись в регистр добавилась.
А что сейчас в демо-базе? Кнопка "Отправить" теперь называется "Отправить (был отправлен)".
Если сейчас нажать на неё повторно, то она снова отправит тот же самый отчёт об ошибке. И мы видим, что в регистре уже три записи.
Постобработка данных ^
Теперь у нас есть быстрый сервис, который просто пишет входящие данные в регистр. И нам нужно их как-то обрабатывать.
В нашем прототипе сервиса регистрации сделаем новый вид документов "ОтчетыОбОшибках". В них будут превращаться двоичные данные из нашего буферного регистра. В качестве прототипа нам будет достаточно получать содержимое основного текстового файла из архива. Выделим для него реквизит ТекстОтчета с неограниченной строкой.
И сразу поправим одну недоработку. Было бы удобно, если в буферный регистр будет писаться помимо самого файла ещё и дата, когда он был получен. Добавим в регистр дату и заполним.
Теперь у нас есть источника данных (регистр сведений) и приёмник (документ). Нам нужен инструмент, который будет брать записи регистра и создавать на их основе документы. Добавим новую обработку. Сделаем ей форму и основную команду, которая будет выполнять метод модуля обработки.
А вот уже в модуле обработки будет содержаться логика превращения записи регистра в документ.
Логика выполнения такая:
Сначала нам нужно выбрать накопленные записи регистра сведений. И каждую запись попытаться обработать. Если это не удастся, то сообщить об ошибке и заодно сделать соответствующую запись в журнале регистрации.
Сама обработка записи будет заключаться в следующем. Сначала мы извлечём двоичные данные во временный файл. Затем откроем чтение Zip-файла и извлечём его содержимое в специально подготовленный временный каталог. Как только удалось это сделать, мы откроем в текстовом документе файл "report.json". Этот файл содержит основные данные отчета об ошибке. Содержимое этого файла и есть наш отчёт.
Теперь, когда мы имеем нужные нам данные, создадим новый документ, заполним его ими и запишем в базу. А временные файлы удалим.
Если запись регистра успешно была обработана, то удалим её тоже. Она нам больше не нужна.
Под капотом код с прототипа:
#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда
#Область ПрограммныйИнтерфейс
Процедура ВыполнитьОсновноеДействие() Экспорт
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ
| *
|ИЗ
| РегистрСведений.ПолученныеОтчетыОбОшибках";
РезультатЗапроса = Запрос.Выполнить();
Если НЕ РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
Попытка
ОбработатьДанныеПолученногоОтчетаОбОшибке(Выборка);
Исключение
ТекстОшибки = "Не удалось обработать отчет об ошибке с идентификатором %1 по причине %2";
ТекстОшибки = СтрШаблон(ТекстОшибки, Выборка.Идентификатор, ОписаниеОшибки());
ДобавитьСообщение(ТекстОшибки);
КонецПопытки;
КонецЦикла;
КонецЕсли;
КонецПроцедуры
#КонецОбласти
#Область ОбработчикиСобытий
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
Процедура ОбработатьДанныеПолученногоОтчетаОбОшибке(ДанныеОбОтчете)
ДанныеОтчета = ДанныеОбОтчете.Содержимое.Получить();
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(".zip");
ДанныеОтчета.Записать(ИмяВременногоФайла);
Архиватор = Новый ЧтениеZipФайла(ИмяВременногоФайла);
ВременныйКаталог = Новый Массив;
ВременныйКаталог.Добавить(КаталогВременныхФайлов());
ВременныйКаталог.Добавить("ОбработкаПолученныхОтчетовОбОшибках");
ВременныйКаталог.Добавить(ДанныеОбОтчете.Идентификатор);
ВременныйКаталог = СтрСоединить(ВременныйКаталог, "\");
Архиватор.ИзвлечьВсе(ВременныйКаталог);
Архиватор.Закрыть();
УдалитьФайлы(ИмяВременногоФайла);
ИмяОсновногоФайла = СтрШаблон("%1\%2", ВременныйКаталог, "report.json");
ТекстовыйДокумент = Новый ТекстовыйДокумент;
ТекстовыйДокумент.Прочитать(ИмяОсновногоФайла, КодировкаТекста.UTF8);
ТекстОсновногоФайла = ТекстовыйДокумент.ПолучитьТекст();
УдалитьФайлы(ВременныйКаталог);
СсылкаНаОтчет = Документы.ОтчетыОбОшибках.ПолучитьСсылку(ДанныеОбОтчете.Идентификатор);
ОтчетОбОшибке = СсылкаНаОтчет.ПолучитьОбъект();
Если ОтчетОбОшибке = Неопределено Тогда
ОтчетОбОшибке = Документы.ОтчетыОбОшибках.СоздатьДокумент();
ОтчетОбОшибке.УстановитьСсылкуНового(СсылкаНаОтчет);
КонецЕсли;
ОтчетОбОшибке.ТекстОтчета = ТекстОсновногоФайла;
ОтчетОбОшибке.Дата = ДанныеОбОтчете.Дата;
ОтчетОбОшибке.Записать();
РегистрыСведений.ПолученныеОтчетыОбОшибках.УдалитьЗапись(ДанныеОбОтчете.Идентификатор);
КонецПроцедуры
Процедура ДобавитьСообщение(ТекстСообщения)
Сообщить(ТекстСообщения);
ЗаписьЖурналаРегистрации("ОбработкаПолученныхОтчетовОбОшибках",
УровеньЖурналаРегистрации.Ошибка,,,ТекстСообщения);
КонецПроцедуры
#КонецОбласти
#КонецЕсли
Выполним обработку.
У нас создались три документа. В каждом хранится содержимое файла report.json. И теперь такую обработку можно поставить на регламентное выполнение и она будет превращать накопленные в регистре отчёты об ошибках в документы.
Выводы ^
Мы разработали простой сервис регистрации ошибок и теперь понимаем, как работать с новым механизмом от 1С.
Данный прототип можно развивать до более сложного инструмента, который поможет в вашей компании обрабатывать ошибки в базах.
А далее мы рассмотрим более подробно извлечение содержимого из архива с отчётом об ошибке.
Понравилась статья? ^
Не будьте равнодушными! Поставьте лайк плюс, оставьте комментарий.
Не забудьте посмотреть видео по этой теме, в нём я наглядно показываю всё то, что говорится в статье: Сервис регистрации ошибок
И переходите к другим публикациям:
-
Как ограничить поля отбора в динамическом списке и ничего не сломать
-
"Меньше копипаста!", или как Вася универсальную процедуру писал