#Область ПрограммныйИнтерфейс
Процедура ВыгрузкаЖРвSentry(Отказ = Ложь) Экспорт
ЭтоНачалоДанных = Ложь;
Настройки = ПолучитьНастройки();
ДатаОкончания = ТекущаяДата();
ДатаГраницы = Настройки.ДатаГраницы;
СоответствиеЗаписиЖР = Неопределено;
ТЗ_ЖР = ВыбратьДанныеЖРЗаПериод(Настройки.ДатаГраницы-1, ДатаОкончания);
Если ТЗ_ЖР.Количество() = 0 Тогда
Возврат;
КонецЕсли;
Для каждого Стр_ЖР Из ТЗ_ЖР Цикл
Если НЕ ЭтоНачалоДанных Тогда
Если Настройки.ДатаГраницы > Стр_ЖР["Дата"] Тогда
Продолжить;
КонецЕсли;
ЭтоНачалоДанных = ПроверитьНачалоДанных(Настройки, Стр_ЖР);
Если ЭтоНачалоДанных Тогда
Продолжить;
КонецЕсли;
КонецЕсли;
Если СтрНайти(Стр_ЖР.ПредставлениеСобытия, "Транзакция") > 0 Тогда
Продолжить;
КонецЕсли;
СтруктураДанные = НормализоватьСтрокуЖР(Стр_ЖР);
СтрокаДанные = хк_КоннекторHTTP.ОбъектВJson(СтруктураДанные);
ДополнительныеПараметры = Новый Структура;
ДополнительныеПараметры.Вставить("Заголовки", Новый Соответствие);
ДополнительныеПараметры.Заголовки.Вставить("Content-Type", "application/json");
ПараметрыЗапроса = Новый Структура;
ПараметрыЗапроса.Вставить("sentry_key", Настройки.КлючПроекта);
ПараметрыЗапроса.Вставить("sentry_version", "7");
ДополнительныеПараметры.Вставить("ПараметрыЗапроса", ПараметрыЗапроса);
Попытка
Ответ = хк_КоннекторHTTP.Post(Настройки.ИмяСервера, СтрокаДанные, ДополнительныеПараметры);
Если Ответ.КодСостояния <> 200 Тогда
Отказ = Истина;
Прервать;
Иначе
СоответствиеЗаписиЖР = ПолучитьСоответствиеЗаписиЖР(Стр_ЖР);
ДатаГраницы = Стр_ЖР.Дата;
КонецЕсли;
Исключение
Прервать;
КонецПопытки;
КонецЦикла;
Если СоответствиеЗаписиЖР <> Неопределено Тогда
УстановитьСоответствиеЗаписиЖР(СоответствиеЗаписиЖР);
УстановитьДатуГраницы(ДатаГраницы);
КонецЕсли;
КонецПроцедуры
#КонецОбласти
#Область СлужебныйПрограммныйИнтерфейс
Процедура УстановитьДатуГраницы(ДатаГраницы) Экспорт
ХранилищеОбщихНастроек.Сохранить("Sentry", "ДатаГраницы", ДатаГраницы);
КонецПроцедуры
Процедура ДобавитьСтрокуРасширение(Расширение, Комментарий) Экспорт
Комментарий = Комментарий + Символы.ПС + "extension: " + Расширение + Символы.ПС;
КонецПроцедуры
Процедура ДобавитьСтрокуЗапросHTTP(ЗапросHTTP, Комментарий) Экспорт
ПараметрыЗаписи = Новый Структура;
ПараметрыЗаписи.Вставить("ПереносСтрок", ПереносСтрокJSON.Нет);
Комментарий = Комментарий + Символы.ПС + "request: " + хк_КоннекторHTTP.ОбъектВJson(ЗапросHTTP,,ПараметрыЗаписи) + Символы.ПС;
КонецПроцедуры
Функция НормализоватьЗапросHTTPИзСервиса(ЗапросHTTP) Экспорт
Структура = ПолучитьСтруктураЗапросHTTP();
Структура.method = ЗапросHTTP.HTTPМетод;
Структура.url = ЗапросHTTP.БазовыйURL;
Структура.query_string = ЗапросHTTP.ПараметрыURL;
Для Каждого Заголовок Из ЗапросHTTP.Заголовки Цикл
Если Заголовок.Ключ = "Authorization" Тогда
Структура.headers.Вставить(Заголовок.Ключ, Лев(Заголовок.Значение, 10));
ИначеЕсли Нрег(Заголовок.Ключ) = "cookie" Тогда
Структура.cookies = Заголовок.Значение;
Иначе
Структура.headers.Вставить(Заголовок.Ключ, Заголовок.Значение);
КонецЕсли;
КонецЦикла;
Структура.data = ЗапросHTTP.ПолучитьТелоКакСтроку();
Возврат Структура;
КонецФункции
// Соответствие для добавления в описание события sentry
// data - может быть соответствием
Функция ПолучитьСтруктураЗапросHTTP() Экспорт
ЗапросHTTP = Новый Структура;
ЗапросHTTP.Вставить("method", "GET");
ЗапросHTTP.Вставить("url", "http://absolute.uri/foo1");
ЗапросHTTP.Вставить("query_string", "query=foobar&page=2");
ЗапросHTTP.Вставить("data", "test data");
ЗапросHTTP.Вставить("cookies", "");
ЗапросHTTP.Вставить("headers", Новый Соответствие);
ЗапросHTTP.headers.Вставить("content-type", "text/html");
ЗапросHTTP.Вставить("env", Новый Соответствие);
Возврат ЗапросHTTP;
КонецФункции
Процедура ТестоваяОшибка() Экспорт
ПустаяСсылка = Справочники.Номенклатура.ПустаяСсылка();
Попытка
Массив1 = Новый Массив();
Массив1.Вставить("строка", "111");
Исключение
Комментарий = ОписаниеОшибки();
ДобавитьСтрокуРасширение("Happykids", Комментарий);
ЗапросHTTP = ПолучитьСтруктураЗапросHTTP();
ДобавитьСтрокуЗапросHTTP(ЗапросHTTP, Комментарий);
ЗаписьЖурналаРегистрации(
"хк_Sentry",
УровеньЖурналаРегистрации.Ошибка,, ПустаяСсылка, Комментарий);
КонецПопытки;
КонецПроцедуры
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
Функция ПолучитьНастройки()
Минута = 60;
Значение = ХранилищеОбщихНастроек.Загрузить("Sentry", "ДатаГраницы");
Если ТипЗнч(Значение) = Тип("Дата") Тогда
ДатаГраницы = Значение;
Иначе
ДатаГраницы = ТекущаяДата() - 5 * Минута;
КонецЕсли;
Настройки = Новый Структура;
СтрокаАдрес = "http://10.10.1.2:9000/api/2/security/?sentry_key=";
ДобавитьВНастройкиИзDSN(СтрокаАдрес, Настройки);
Настройки.Вставить("ДатаГраницы", ДатаГраницы);
Настройки.Вставить("СоответствиеЗаписиЖР", ХранилищеОбщихНастроек.Загрузить("Sentry", "СоответствиеЗаписиЖР"));
Возврат Настройки;
КонецФункции
Функция ДобавитьВНастройкиИзDSN(СтрокаАдрес, Настройки)
СтрокаАдрес = СтрЗаменить(СтрокаАдрес, "security", "store");
МассивПодстрок = СтрРазделить(СтрокаАдрес, "?");
КлючПроекта = СтрЗАменить(МассивПодстрок[1], "sentry_key=", "");
Настройки.Вставить("ИмяСервера", МассивПодстрок[0]);
Настройки.Вставить("КлючПроекта", КлючПроекта);
КонецФункции
#Область НормализацияСтрокиЖР
Функция НормализоватьСтрокуЖР(СтрокаЖурнала)
СтруктураДанные = Новый Структура;
СистемнаяИнформация = Новый СистемнаяИнформация;
СтрокаСоединения = СтрокаСоединенияИнформационнойБазы();
МассивСтрокаСоединения = СтрРазделить(СтрокаСоединения, ";");
ИмяБазы = МассивСтрокаСоединения[1];
ИмяБазы = СтрЗаменить(ИмяБазы, "Ref=", "");
ИмяБазы = СтрЗаменить(ИмяБазы, """", "");
Тело = Новый Структура();
TimeStamp = УниверсальноеВремя(СтрокаЖурнала.Дата) -'19700101';
Тело.Вставить("timestamp", TimeStamp);
Тело.Вставить("logger", "1с_logger");
Тело.Вставить("platform", "Other");
Тело.Вставить("level", ПолучитьУровень(СтрокаЖурнала.Уровень));
Тело.Вставить("transaction", Строка(СтрокаЖурнала.Транзакция));
Тело.Вставить("server_name", СтрокаЖурнала.РабочийСервер + "/" + Строка(ИмяБазы));
Тело.Вставить("release", Метаданные.Имя + "@" + Метаданные.Версия);
Тело.Вставить("modules", ПолучитьСоответствиеМодулей());
Окружение = "dev";
Попытка
ПараметрыБлокировки = Константы.ПараметрыБлокировкиРаботыСВнешнимиРесурсами.Получить().Получить();
Если ПараметрыБлокировки.РаботаСВнешнимиРесурсамиЗаблокирована = Ложь Тогда
Окружение = "prod";
КонецЕсли;
Исключение
КонецПопытки;
Тело.Вставить("environment", Окружение);
Тело.Вставить("contexts", Новый Структура);
Тело.contexts.Вставить("os", Новый Структура);
Если ОбщегоНазначения.ЭтоWindowsСервер() Тогда
Тело.contexts.os.Вставить("name", "windows");
Иначе
Тело.contexts.os.Вставить("name", "linux");
КонецЕсли;
Тело.contexts.Вставить("app", Новый Структура);
Тело.contexts.app.вставить("app_name", СтрокаЖурнала.ИмяПриложения);
Тело.contexts.app.вставить("app_version", СистемнаяИнформация.ВерсияПриложения);
Тело.contexts.Вставить("session_data", Новый Структура);
Тело.contexts.session_data.Вставить("session", СтрокаЖурнала.Сеанс);
Тело.contexts.session_data.Вставить("connection", СтрокаЖурнала.Соединение);
Тело.Вставить("message", СтрокаЖурнала.Комментарий);
//По ключу "exception" идет группировка ошибок
Тело.Вставить("exception", Новый Структура);
МодульОшибки = "";
СтрКомментарий = СтрокаЖурнала.Комментарий;
Начало = СтрНайти(СтрКомментарий, "{",НаправлениеПоиска.СКонца);
Если Начало > 0 Тогда
Окончание = СтрНайти(СтрКомментарий,")",,Начало);
МодульОшибки = Сред(СтрКомментарий,Начало + 1, Окончание-Начало);
КонецЕсли;
Если ПустаяСтрока(МодульОшибки) Тогда
МодульОшибки = СтрокаЖурнала.ПредставлениеМетаданных;
КонецЕсли;
Расширение = "";
СтрокаФункции = "";
Стек = Новый Структура;
Стек.Вставить("frames", ПолучитьМассивТрассировкиСтека(СтрокаЖурнала.Комментарий, Расширение, СтрокаФункции));
Если Расширение = "" Тогда
Расширение = ПолучитьПодстрокуПоТегу("extension", СтрокаЖурнала.Комментарий);
КонецЕсли;
Тело.exception.Вставить("values", Новый Массив);
ЗначениеОшибки = Новый Структура;
ЗначениеОшибки.Вставить("type", СтрокаЖурнала.ПредставлениеСобытия);
ЗначениеОшибки.Вставить("value", ?(СтрокаФункции = "", Строка(МодульОшибки), СтрокаФункции));
Если Стек.frames.Количество() > 0 Тогда
ЗначениеОшибки.Вставить("stacktrace", Стек);
КонецЕсли;
Тело.exception.values.Добавить(ЗначениеОшибки);
Если Расширение <> "" И Расширение <> "main" Тогда
Попытка
Тело.Вставить("release", Расширение + "@" + Тело.modules[Расширение]);
Тело.modules.Вставить(Метаданные.Имя, Метаданные.Версия);
Исключение
КонецПопытки;
Тело.Вставить("tags", Новый Соответствие);
Тело.tags.Вставить("extension", Расширение);
КонецЕсли;
Тело.Вставить("extra", Новый Соответствие);
Тело.extra.Вставить("metadata", СтрокаЖурнала.ПредставлениеМетаданных);
Тело.extra.Вставить("record", СтрокаЖурнала.ПредставлениеДанных);
Тело.Вставить("user", Новый Структура);
Если ПустаяСтрока(СтрокаЖурнала.ИмяПользователя) И СтрокаЖурнала.ИмяПриложения = "Фоновое задание" Тогда
ИмяПользователя = СтрокаЖурнала.ИмяПользователя + СтрокаЖурнала.Компьютер;
Иначе
ИмяПользователя = СтрокаЖурнала.ИмяПользователя;
КонецЕсли;
Тело.user.Вставить("id", ИмяПользователя);
Тело.user.Вставить("username", ИмяПользователя);
ПодстрокаЗапрос = ПолучитьПодстрокуПоТегу("request", СтрокаЖурнала.Комментарий);
Если СтрДлина(ПодстрокаЗапрос) > 0 Тогда
СоответствиеЗапрос = хк_КоннекторHTTP.JsonВОбъект(ПодстрокаЗапрос);
Тело.Вставить("request", СоответствиеЗапрос);
КонецЕсли;
Возврат Тело;
КонецФункции
Функция ПолучитьПодстрокуПоТегу(знач Тег, СтрокаДанные)
Тег = Тег + ": ";
Подстрока = "";
СимволН = СтрНайти(СтрокаДанные, Тег);
Если СимволН > 0 Тогда
СимволК = СтрНайти(СтрокаДанные, Символы.ПС,,СимволН);
Если СимволК > 0 Тогда
Подстрока = Сред(СтрокаДанные, СимволН + СтрДлина(Тег), СимволК - СимволН - СтрДлина(Тег));
КонецЕсли;
КонецЕсли;
Возврат Подстрока;
КонецФункции
Функция ПолучитьУровень(УровеньЖР)
СоответствииУровней = Новый Соответствие;
СоответствииУровней.Вставить(УровеньЖурналаРегистрации.Ошибка, "error");
СоответствииУровней.Вставить(УровеньЖурналаРегистрации.Предупреждение, "warning");
Уровень = СоответствииУровней.Получить(УровеньЖР);
Возврат Уровень;
КонецФункции
Функция ПолучитьСоответствиеМодулей()
СоответствиеМодулей = Новый Соответствие;
Расширения = РасширенияКонфигурации.Получить();
Для каждого Расширение Из Расширения Цикл
Если Строка(Расширение.Назначение) <> "Исправление" Тогда
СоответствиеМодулей.Вставить(Расширение.Имя, Расширение.Версия);
КонецЕсли;
КонецЦикла;
Возврат СоответствиеМодулей;
КонецФункции
Функция ПолучитьМассивТрассировкиСтека(Комментарий, РасширениеВнеш, СтрокаФункцииВнеш)
МассивТрассировкиСтека = Новый Массив;
МассивПодстрок = СтрРазделить(Комментарий, Символы.ПС);
НачалоКодаСчитано = Ложь;
Для каждого Подстрока Из МассивПодстрок Цикл
СоответствиеТрассировки = Новый Соответствие;
// в комментарии может дублироватся стек вызова, выбираем первую пачку строк
Если Лев(Подстрока, 1) <> "{" Тогда
Если НачалоКодаСчитано Тогда
Прервать;
Иначе
Продолжить;
КонецЕсли;
КонецЕсли;
НачалоКодаСчитано = Истина;
Подстрока = СтрЗаменить(Подстрока, "}:", Символ(29));
ПодстрокиПроцедуры = СтрРазделить(Подстрока, "" + Символ(29));
Если ПодстрокиПроцедуры.Количество() <> 2 Тогда
Продолжить;
КонецЕсли;
СоответствиеТрассировки.Вставить("in_app", Истина);
СоответствиеТрассировки.Вставить("context_line", ПодстрокиПроцедуры[1]);
ПодстрокаПути = ПодстрокиПроцедуры[0];
ПодстрокаПути = СтрЗаменить(ПодстрокаПути, "{", "");
//ПодстрокаПути = СтрЗаменить(ПодстрокаПути, "}", "");
МассивподстрокПути = СтрРазделить(ПодстрокаПути, "(", Истина);
Если МассивподстрокПути.Количество() <> 2 Тогда
Продолжить;
КонецЕсли;
СтрокаФункции = МассивподстрокПути[0];
МассивПодстрокФункции = СтрРазделить(СтрокаФункции, " ");
Если МассивПодстрокФункции.Количество() = 2 Тогда
СтрокаФункции = МассивПодстрокФункции[1];
Расширение = МассивПодстрокФункции[0];
Иначе
Расширение = "";
КонецЕсли;
СоответствиеТрассировки.Вставить("filename", ?(Расширение = "", "main", Расширение));
СоответствиеТрассировки.Вставить("function", СтрокаФункции);
Попытка
СоответствиеТрассировки.Вставить("lineno", Число(СтрЗаменить(МассивподстрокПути[1], ")", "")));
МассивТрассировкиСтека.Добавить(СоответствиеТрассировки);
Исключение
КонецПопытки;
КонецЦикла;
Для Инд = 0 По Цел(МассивТрассировкиСтека.Количество() / 2) - 1 Цикл
Эл = МассивТрассировкиСтека[Инд];
МассивТрассировкиСтека[Инд] = МассивТрассировкиСтека[МассивТрассировкиСтека.Количество() - 1 - Инд];
МассивТрассировкиСтека[МассивТрассировкиСтека.Количество() - 1 - Инд] = Эл;
КонецЦикла;
Если МассивТрассировкиСтека.Количество() > 0 Тогда
РасширениеВнеш = МассивТрассировкиСтека[МассивТрассировкиСтека.ВГраница()]["filename"];
СтрокаФункцииВнеш = МассивТрассировкиСтека[МассивТрассировкиСтека.ВГраница()]["context_line"];
КонецЕсли;
Возврат МассивТрассировкиСтека;
КонецФункции
#КонецОбласти
#Область ВыборкаДанных
Функция ВыбратьДанныеЖРЗаПериод(ДатаНачала, ДатаОкончания)
Попытка
Отбор = Новый Структура;
Отбор.Вставить("ДатаНачала", ДатаНачала);
Отбор.Вставить("ДатаОкончания", ДатаОкончания);
МассивУровней = Новый Массив;
МассивУровней.Добавить(УровеньЖурналаРегистрации.Ошибка);
МассивУровней.Добавить(УровеньЖурналаРегистрации.Предупреждение);
Отбор.Вставить("Уровень", МассивУровней);
ТЗ_ЖР = Новый ТаблицаЗначений;
ВыгрузитьЖурналРегистрации(ТЗ_ЖР, Отбор);
Возврат ТЗ_ЖР;
Исключение
Ошибка = ОписаниеОшибки();
ЗаписьЖурналаРегистрации("Sentry.ВыборкаЖР",
УровеньЖурналаРегистрации.Ошибка, ,
"Логи ЖР. Ошибка чтения ЖР",
СтрокаСоединенияИнформационнойБазы()+" период "+ ДатаНачала + " " + ДатаОкончания
);
ВызватьИсключение Ошибка;
КонецПопытки;
КонецФункции
Процедура УстановитьСоответствиеЗаписиЖР(СоответствиеЗаписиЖР)
ХранилищеОбщихНастроек.Сохранить("Sentry", "СоответствиеЗаписиЖР", СоответствиеЗаписиЖР);
КонецПроцедуры
Функция ПолучитьСоответствиеЗаписиЖР(ЗаписьЖР)
МассивРеквизитовДляСравнения = МассивРеквизитовДляСравнения();
СоответствиеЗаписиЖР = Новый Соответствие;
Для Каждого Реквизит Из МассивРеквизитовДляСравнения Цикл
СоответствиеЗаписиЖР.Вставить(Реквизит, ЗаписьЖР[Реквизит]);
КонецЦикла;
Возврат СоответствиеЗаписиЖР;
КонецФункции
Функция МассивРеквизитовДляСравнения()
СтрокаРеквизиты =
"Уровень
|Дата
|Пользователь
|Компьютер
|ИмяПриложения
|Событие
|Комментарий
|Сеанс
|Соединение
|РабочийСервер";
Возврат СтрРазделить(СтрокаРеквизиты, Символы.ПС);
КонецФункции
Функция ПроверитьНачалоДанных(Настройки, Стр_ЖР)
НачалоДанных = Истина;
Если Настройки.СоответствиеЗаписиЖР = Неопределено
Тогда
Возврат НачалоДанных;
КонецЕсли;
Если Настройки.ДатаГраницы < Стр_ЖР["Дата"] Тогда
Возврат НачалоДанных;
КонецЕсли;
МассивРеквизитовДляСравнения = МассивРеквизитовДляСравнения();
Для Каждого Реквизит Из МассивРеквизитовДляСравнения Цикл
Если Настройки.СоответствиеЗаписиЖР[Реквизит] <> Стр_ЖР[Реквизит] Тогда
НачалоДанных = Ложь;
Прервать;
КонецЕсли;
КонецЦикла;
Возврат НачалоДанных;
КонецФункции
#КонецОбласти
#КонецОбласти