From ea01aa7e8d98163248b70d8a9063d1e4c9982b64 Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Wed, 9 Sep 2026 00:09:41 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BA=D0=BE=D1=80=D0=BE=D1=82=D0=BA=D0=B8=D0=B5?= =?UTF-8?q?=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D1=8B=20=D0=B4=D0=B8=D0=B0?= =?UTF-8?q?=D0=B3=D0=BD=D0=BE=D1=81=D1=82=D0=B8=D0=BA=D0=B8=20=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=B1=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...21\210\320\270\320\261\320\276\320\272.md" | 61 ++++++++- ...21\210\320\270\320\261\320\272\320\270.md" | 9 +- ...21\210\320\270\320\261\320\272\320\270.os" | 62 +++++++++ ...21\210\320\270\320\261\320\276\320\272.os" | 62 +++++++++ ...21\210\320\270\320\261\320\276\320\272.os" | 128 +++++++++++++++++- 5 files changed, 311 insertions(+), 11 deletions(-) diff --git "a/docs/\320\224\320\270\320\260\320\263\320\275\320\276\321\201\321\202\320\270\320\272\320\260\320\236\321\210\320\270\320\261\320\276\320\272.md" "b/docs/\320\224\320\270\320\260\320\263\320\275\320\276\321\201\321\202\320\270\320\272\320\260\320\236\321\210\320\270\320\261\320\276\320\272.md" index e69899a..3cdbb4c 100644 --- "a/docs/\320\224\320\270\320\260\320\263\320\275\320\276\321\201\321\202\320\270\320\272\320\260\320\236\321\210\320\270\320\261\320\276\320\272.md" +++ "b/docs/\320\224\320\270\320\260\320\263\320\275\320\276\321\201\321\202\320\270\320\272\320\260\320\236\321\210\320\270\320\261\320\276\320\272.md" @@ -2,7 +2,7 @@ Публичный модуль для быстрого поиска и представления ошибок. Если нужна одна операция, используйте короткие методы модуля. Для нескольких обращений к одной цепочке сначала создайте -[`РазборОшибки`](РазборОшибки.md), чтобы не разбирать её повторно. +[`РазборОшибки`](РазборОшибки.md), чтобы переиспользовать подготовленные им результаты. ## Разобрать @@ -16,9 +16,9 @@ | --- | --- | --- | | **Ошибка** | `ИнформацияОбОшибке` | Внешний кадр разбираемой цепочки | -**Возвращаемое значение:** +#### Возвращаемое значение -`РазборОшибки` - материализованный разбор цепочки. +`РазборОшибки`: ленивый разбор цепочки. ## Код эффективной ошибки @@ -26,9 +26,55 @@ Код = ДиагностикаОшибок.КодОшибки(<Ошибка>) ``` -Возвращает машинный код первой классифицированной ошибки в цепочке или `Неопределено`, +#### Возвращаемое значение + +`Строка`, `Неопределено`: машинный код первой классифицированной ошибки в цепочке или +`Неопределено`, если классифицированной ошибки нет. + +## Данные эффективной ошибки + +```bsl +Данные = ДиагностикаОшибок.ДанныеОшибки(<Ошибка>) +``` + +Поиск завершается на первой классифицированной ошибке. +Данные не копируются. Изменение полученного объекта изменяет исходные данные кадра. + +#### Возвращаемое значение + +`Произвольный`, `Неопределено`: пользовательские данные эффективной ошибки или `Неопределено`, +если классифицированной ошибки нет. + +## Эффективная ошибка + +```bsl +Снимок = ДиагностикаОшибок.ЭффективнаяОшибка(<Ошибка>) +``` + +Обход завершается при первой классифицированной ошибке. + +#### Возвращаемое значение + +`СнимокКадраОшибки`, `Неопределено`: снимок эффективной ошибки или `Неопределено`, если классифицированной ошибки нет. +## Корневая причина + +```bsl +Корень = ДиагностикаОшибок.КорневаяПричина(<Ошибка>) +``` + +Корнем может быть внешний неклассифицированный кадр. Для усечённой или непрочитанной цепочки +корень недоступен. + +#### Возвращаемое значение + +`СнимокКадраОшибки`, `Неопределено`: снимок фактической корневой причины или `Неопределено`, +если цепочка усечена или не прочитана полностью. + +`ДанныеОшибки` и `ЭффективнаяОшибка` не создают полный разбор. `КорневаяПричина` просматривает +цепочку до конца, но создаёт снимок только для возвращаемого кадра. + ## Проверка цепочки ```bsl @@ -55,7 +101,12 @@ Возвращает первый [`СнимокКадраОшибки`](СнимокКадраОшибки.md) с указанным кодом при обходе от внешнего кадра к корневому. Полноценный снимок создаётся только для найденного физического -кадра. Если совпадений нет, возвращает `Неопределено`. +кадра. + +#### Возвращаемое значение + +`СнимокКадраОшибки`, `Неопределено`: первый снимок с указанным кодом или `Неопределено`, +если совпадений нет. ## Представления diff --git "a/docs/\320\240\320\260\320\267\320\261\320\276\321\200\320\236\321\210\320\270\320\261\320\272\320\270.md" "b/docs/\320\240\320\260\320\267\320\261\320\276\321\200\320\236\321\210\320\270\320\261\320\272\320\270.md" index 2047c0f..903ffe2 100644 --- "a/docs/\320\240\320\260\320\267\320\261\320\276\321\200\320\236\321\210\320\270\320\261\320\272\320\270.md" +++ "b/docs/\320\240\320\260\320\267\320\261\320\276\321\200\320\236\321\210\320\270\320\261\320\272\320\270.md" @@ -1,10 +1,11 @@ # РазборОшибки -Материализованный разбор физической цепочки ошибки. Создаётся через +Ленивый разбор физической цепочки ошибки. Создаётся через [`ДиагностикаОшибок.Разобрать`](ДиагностикаОшибок.md). -Состав и свойства кадров фиксируются при создании разбора, но пользовательские данные -кадров сохраняются по ссылке и могут изменяться владельцем исходного объекта. +При создании разбор проверяет и сохраняет исходную ошибку. Физическая цепочка и снимки кадров +фиксируются при первой операции, которой нужен полный разбор. Пользовательские данные кадров +сохраняются по ссылке и могут изменяться владельцем исходного объекта. ## Создание @@ -18,7 +19,7 @@ | --- | --- | --- | | **Ошибка** | `ИнформацияОбОшибке` | Внешний кадр разбираемой цепочки | -При создании разбор просматривает не более 64 физических кадров. +Полный разбор просматривает не более 64 физических кадров. ## Цепочка diff --git "a/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\236\320\261\321\205\320\276\320\264\321\207\320\270\320\272\320\246\320\265\320\277\320\276\321\207\320\272\320\270\320\236\321\210\320\270\320\261\320\272\320\270.os" "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\236\320\261\321\205\320\276\320\264\321\207\320\270\320\272\320\246\320\265\320\277\320\276\321\207\320\272\320\270\320\236\321\210\320\270\320\261\320\272\320\270.os" index 8768e9f..9a3bc35 100644 --- "a/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\236\320\261\321\205\320\276\320\264\321\207\320\270\320\272\320\246\320\265\320\277\320\276\321\207\320\272\320\270\320\236\321\210\320\270\320\261\320\272\320\270.os" +++ "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\236\320\261\321\205\320\276\320\264\321\207\320\270\320\272\320\246\320\265\320\277\320\276\321\207\320\272\320\270\320\236\321\210\320\270\320\261\320\272\320\270.os" @@ -132,6 +132,68 @@ КонецФункции +// Возвращает первую распознанную ошибку при обходе от внешнего кадра к причине. +// +// Параметры: +// Ошибка - ИнформацияОбОшибке - Внешний кадр просматриваемой цепочки. +// +// Возвращаемое значение: +// ИнформацияОбОшибке, Неопределено - Первая распознанная ошибка или Неопределено. +Функция НайтиПервуюОшибку(Ошибка) Экспорт + + _ПроверяющийКадрыОшибок.ПроверитьОшибку(Ошибка); + + ТекущийКадр = Ошибка; + + Для НомерКадра = 1 По МаксимальноеКоличествоКадров() Цикл + Метаданные = _ЧитательКадраОшибки.ПрочитатьМетаданные(ТекущийКадр); + Если Метаданные <> Неопределено И Метаданные.Вид = ВидыКадровОшибок.Ошибка() Тогда + Возврат ТекущийКадр; + КонецЕсли; + + Причина = Неопределено; + Если Не ПопытатьсяПрочитатьПричину(ТекущийКадр, Причина) + Или Причина = Неопределено Тогда + Возврат Неопределено; + КонецЕсли; + + ТекущийКадр = Причина; + КонецЦикла; + + Возврат Неопределено; + +КонецФункции + +// Возвращает корневую причину полностью прочитанной цепочки. +// +// Параметры: +// Ошибка - ИнформацияОбОшибке - Внешний кадр просматриваемой цепочки. +// +// Возвращаемое значение: +// ИнформацияОбОшибке, Неопределено - Корень или Неопределено для усечённой цепочки. +Функция НайтиКорневуюПричину(Ошибка) Экспорт + + _ПроверяющийКадрыОшибок.ПроверитьОшибку(Ошибка); + + ТекущийКадр = Ошибка; + + Для НомерКадра = 1 По МаксимальноеКоличествоКадров() Цикл + Причина = Неопределено; + Если Не ПопытатьсяПрочитатьПричину(ТекущийКадр, Причина) Тогда + Возврат Неопределено; + КонецЕсли; + + Если Причина = Неопределено Тогда + Возврат ТекущийКадр; + КонецЕсли; + + ТекущийКадр = Причина; + КонецЦикла; + + Возврат Неопределено; + +КонецФункции + // Возвращает первый физический кадр ошибки с одним из указанных кодов. // // Параметры: diff --git "a/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\224\320\270\320\260\320\263\320\275\320\276\321\201\321\202\320\270\320\272\320\260\320\236\321\210\320\270\320\261\320\276\320\272.os" "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\224\320\270\320\260\320\263\320\275\320\276\321\201\321\202\320\270\320\272\320\260\320\236\321\210\320\270\320\261\320\276\320\272.os" index 43bb5f8..b392afa 100644 --- "a/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\224\320\270\320\260\320\263\320\275\320\276\321\201\321\202\320\270\320\272\320\260\320\236\321\210\320\270\320\261\320\276\320\272.os" +++ "b/src/\320\234\320\276\320\264\321\203\320\273\320\270/\320\224\320\270\320\260\320\263\320\275\320\276\321\201\321\202\320\270\320\272\320\260\320\236\321\210\320\270\320\261\320\276\320\272.os" @@ -28,6 +28,68 @@ КонецФункции +// Возвращает по ссылке пользовательские данные эффективной ошибки. +// +// Параметры: +// Ошибка - ИнформацияОбОшибке - Внешний кадр разбираемой цепочки. +// +// Возвращаемое значение: +// Произвольный, Неопределено - Данные эффективной ошибки или Неопределено. +Функция ДанныеОшибки(Ошибка) Экспорт + + Обходчик = СервисыБиблиотекиОшибок.ОбходчикЦепочкиОшибки(); + Кадр = Обходчик.НайтиПервуюОшибку(Ошибка); + + Если Кадр = Неопределено Тогда + Возврат Неопределено; + КонецЕсли; + + Метаданные = СервисыБиблиотекиОшибок.ЧитательКадраОшибки().ПрочитатьМетаданные(Кадр); + + Возврат Метаданные.Данные; + +КонецФункции + +// Возвращает первую распознанную ошибку. +// +// Параметры: +// Ошибка - ИнформацияОбОшибке - Внешний кадр разбираемой цепочки. +// +// Возвращаемое значение: +// СнимокКадраОшибки, Неопределено - Эффективная ошибка или Неопределено. +Функция ЭффективнаяОшибка(Ошибка) Экспорт + + Обходчик = СервисыБиблиотекиОшибок.ОбходчикЦепочкиОшибки(); + Кадр = Обходчик.НайтиПервуюОшибку(Ошибка); + + Если Кадр = Неопределено Тогда + Возврат Неопределено; + КонецЕсли; + + Возврат Новый СнимокКадраОшибки(Кадр); + +КонецФункции + +// Возвращает фактическую корневую причину полностью просмотренной цепочки. +// +// Параметры: +// Ошибка - ИнформацияОбОшибке - Внешний кадр разбираемой цепочки. +// +// Возвращаемое значение: +// СнимокКадраОшибки, Неопределено - Корень или Неопределено для усечённой цепочки. +Функция КорневаяПричина(Ошибка) Экспорт + + Обходчик = СервисыБиблиотекиОшибок.ОбходчикЦепочкиОшибки(); + Кадр = Обходчик.НайтиКорневуюПричину(Ошибка); + + Если Кадр = Неопределено Тогда + Возврат Неопределено; + КонецЕсли; + + Возврат Новый СнимокКадраОшибки(Кадр); + +КонецФункции + // Проверяет наличие ошибки с указанным машинным кодом во всей цепочке. // // Параметры: diff --git "a/tests/\320\242\320\265\321\201\321\202\321\213\320\224\320\270\320\260\320\263\320\275\320\276\321\201\321\202\320\270\320\272\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" "b/tests/\320\242\320\265\321\201\321\202\321\213\320\224\320\270\320\260\320\263\320\275\320\276\321\201\321\202\320\270\320\272\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" index 462c495..a06da04 100644 --- "a/tests/\320\242\320\265\321\201\321\202\321\213\320\224\320\270\320\260\320\263\320\275\320\276\321\201\321\202\320\270\320\272\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" +++ "b/tests/\320\242\320\265\321\201\321\202\321\213\320\224\320\270\320\260\320\263\320\275\320\276\321\201\321\202\320\270\320\272\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" @@ -34,6 +34,123 @@ КонецПроцедуры +&Тест +Процедура ТестДолжен_ВернутьДанныеЭффективнойОшибкиПоСсылке() Экспорт + + // Подготовка + Данные = Новый Структура("Узел", "db.example.org"); + Причина = (Новый ПостроительОшибки("connection.timeout", "Превышено время ожидания")) + .ПроизвольныеДанные(Данные) + .Построить(); + Ошибка = ФабрикаОшибок.ОбернутьКонтекстом(Причина, "Вызов базы данных"); + + // Действие + Результат = ДиагностикаОшибок.ДанныеОшибки(Ошибка); + Результат.Узел = "cache.example.org"; + + // Проверка + Ожидаем.Что(Результат).Равно(Данные); + Ожидаем.Что(Данные.Узел).Равно("cache.example.org"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВернутьЭффективнуюОшибку() Экспорт + + // Подготовка + Причина = ФабрикаОшибок.Создать("config.invalid", "Ошибка конфигурации"); + Ошибка = ФабрикаОшибок.ОбернутьКонтекстом(Причина, "Загрузка конфигурации"); + + // Действие + Снимок = ДиагностикаОшибок.ЭффективнаяОшибка(Ошибка); + + // Проверка + Ожидаем.Что(ТипЗнч(Снимок)).Равно(Тип("СнимокКадраОшибки")); + Ожидаем.Что(Снимок.ИсходнаяОшибка()).Равно(Причина); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВернутьВнешнююКорневуюПричину() Экспорт + + // Подготовка + Корень = ПомощникТестированияОшибок.ВнешняяОшибка("Сетевой сбой"); + Ошибка = ФабрикаОшибок.Обернуть(Корень, "service.unavailable", "Сервис недоступен"); + + // Действие + Снимок = ДиагностикаОшибок.КорневаяПричина(Ошибка); + + // Проверка + Ожидаем.Что(Снимок.ИсходнаяОшибка()).Равно(Корень); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВернутьПустыеРезультатыДляВнешнейОшибки() Экспорт + + // Подготовка + Ошибка = ПомощникТестированияОшибок.ВнешняяОшибка("Внешняя ошибка"); + + // Действие + Данные = ДиагностикаОшибок.ДанныеОшибки(Ошибка); + Эффективная = ДиагностикаОшибок.ЭффективнаяОшибка(Ошибка); + Корневая = ДиагностикаОшибок.КорневаяПричина(Ошибка); + + // Проверка + Ожидаем.Что(Данные).Равно(Неопределено); + Ожидаем.Что(Эффективная).Равно(Неопределено); + Ожидаем.Что(Корневая.ИсходнаяОшибка()).Равно(Ошибка); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_РазличитьКореньИУсеченнуюЦепочку() Экспорт + + // Подготовка + Цепочка64 = ПомощникТестированияОшибок.ДлиннаяЦепочка(64); + Цепочка65 = ПомощникТестированияОшибок.ДлиннаяЦепочка(65); + + // Действие + Корень64 = ДиагностикаОшибок.КорневаяПричина(Цепочка64.Ошибка); + Корень65 = ДиагностикаОшибок.КорневаяПричина(Цепочка65.Ошибка); + + // Проверка + Ожидаем.Что(Корень64.ИсходнаяОшибка()).Равно(Цепочка64.Корень); + Ожидаем.Что(Корень65).Равно(Неопределено); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ОграничитьПоискЭффективнойОшибкиШестьюдесятьюЧетырьмяКадрами() Экспорт + + // Подготовка + Данные = Новый Структура("Граница", 64); + ОшибкаНаГранице = ФабрикаОшибок.Создать("core.failed", "Ошибка на границе", Данные); + Для НомерКадра = 1 По 63 Цикл + ОшибкаНаГранице = ПомощникТестированияОшибок.ВнешняяОшибка( + "Внешний кадр", + ОшибкаНаГранице + ); + КонецЦикла; + ОшибкаЗаГраницей = ПомощникТестированияОшибок.ВнешняяОшибка( + "Лишний кадр", + ОшибкаНаГранице + ); + + // Действие + СнимокНаГранице = ДиагностикаОшибок.ЭффективнаяОшибка(ОшибкаНаГранице); + СнимокЗаГраницей = ДиагностикаОшибок.ЭффективнаяОшибка(ОшибкаЗаГраницей); + ДанныеНаГранице = ДиагностикаОшибок.ДанныеОшибки(ОшибкаНаГранице); + ДанныеЗаГраницей = ДиагностикаОшибок.ДанныеОшибки(ОшибкаЗаГраницей); + + // Проверка + Ожидаем.Что(СнимокНаГранице.КодОшибки()).Равно("core.failed"); + Ожидаем.Что(СнимокЗаГраницей).Равно(Неопределено); + Ожидаем.Что(ДанныеНаГранице).Равно(Данные); + Ожидаем.Что(ДанныеЗаГраницей).Равно(Неопределено); + +КонецПроцедуры + &Тест Процедура ТестДолжен_ОграничитьЧтениеКодаШестьюдесятьюЧетырьмяКадрами() Экспорт @@ -55,19 +172,26 @@ КонецПроцедуры &Тест -Процедура ТестДолжен_ПропуститьЧужойИПоврежденныйКонвертыПриЧтенииКода() Экспорт +Процедура ТестДолжен_ПропуститьЧужойИПоврежденныйКонвертыПриДиагностике() Экспорт // Подготовка - ВнутренняяОшибка = ФабрикаОшибок.Создать("core.failed", "Корневая ошибка"); + ДанныеОшибки = Новый Структура("Источник", "Тест"); + ВнутренняяОшибка = ФабрикаОшибок.Создать("core.failed", "Корневая ошибка", ДанныеОшибки); Образец = ФабрикаОшибок.Создать("sample.error", "Образец"); ЧужойМаркер = ПомощникТестированияОшибок.ОшибкаСЧужимМаркером(Образец, ВнутренняяОшибка); Ошибка = ПомощникТестированияОшибок.ОшибкаСПоврежденнымКонвертом(Образец, ЧужойМаркер); // Действие КодОшибки = ДиагностикаОшибок.КодОшибки(Ошибка); + Снимок = ДиагностикаОшибок.ЭффективнаяОшибка(Ошибка); + Данные = ДиагностикаОшибок.ДанныеОшибки(Ошибка); + Корень = ДиагностикаОшибок.КорневаяПричина(Ошибка); // Проверка Ожидаем.Что(КодОшибки).Равно("core.failed"); + Ожидаем.Что(Снимок.ИсходнаяОшибка()).Равно(ВнутренняяОшибка); + Ожидаем.Что(Данные).Равно(ДанныеОшибки); + Ожидаем.Что(Корень.ИсходнаяОшибка()).Равно(ВнутренняяОшибка); КонецПроцедуры