Comments 19
у каждой ошибки должен быть свой code типа int;
Удачи в согласовании этих кодов в продукте на десятки и сотни раработчиков.
ошибки объединены в интервалы code:
1 - 99 // зарезервированы под внутренние ошибки кода твоего сервиса
А если у меня ошибок сильно больше?
200-299 // под вторую интеграцию, например базы данных
А если у меня "интеграций" неизвестное количество, и они определяются в рантайме?
Такая система лишена всех выше описанных недостатков.
А как же вот с этим недостатком:
Система ошибок подразумевает объединение нескольких причин возникновения не целевого поведения в рамках одной сущности или класса-ошибки. [...] отслеживать - какие причины объединены случайно, а какие специально, порой неочевидно, особенно спустя n-месяцев;
У вас заведомо какие-то ошибки будут объединяться в группы, иначе вам кодов не хватит при первой же интеграции.
1 - с числом можно работать интервалами, если смежнику достаточно знать, что ошибка произошла и только, то он просто зашьется на факт получения кода.
2 - размер интервала приведен в качестве примера.
3 - главное иметь свой интервал на каждую, чтобы они были сгруппированы по смежникам, иначе ориентироваться в причинах будет сложно, особенно если смежников "неизвестное количество", что тоже странно, при написании кода ошибки можно сгруппировать по какому либо принципу, главное чтобы он был.
4 - см. 2 и 3.
с числом можно работать интервалами
Чтобы работать с интервалами, нужно их знать. А чтобы их знать, нужно их согласовать.
размер интервала приведен в качестве примера.
Так откуда ж мне его знать на момент начала работы тогда?
главное иметь свой интервал на каждую
Чтобы иметь интервал на каждую, нужно знать, какие они могут быть. А откуда мне это знать, если они в рантайме настраиваются, и не обязательно мной?
4 - см. 2 и 3.
Нет, не решает. Более того, вы явно пишете "при написании кода ошибки можно сгруппировать по какому либо принципу" - т.е. вы привносите эту проблему.
1 - все зависит от бизнес потребностей, повторюсь, такой подход позволяет выбирать, смежник может ограничиться ошибками уровня протокола (GRPC/HTTP/WSS opt коды etc.), может отталкиваться от деталей твоего приложения на уровне интервала или на уровне интервала + совпадения по каким либо конкретным кодам.
2 - в современной парадигме микросервисной архитектуры сложно представить сервис, у которого будет больше 1000 ошибок (к примеру если клиент прислал поле с неверным типом - не обязательно каждому полю в огромном json-е при несовпадении типа присваивать свой код).
3 - для этого и нужен первый код в рамках интервала, он так сказать "слив" для всех неизвестных ошибок с каким либо смежником (опять же "смежник" это один из принципов объединения).
4 - объединение требуется только для того, чтобы привнести порядок в хаос, например если 1 - ошибка данных поля входящего json, 2 - таймаут на базе, 3 - невалидный сертификат от твоего сервиса в качестве ошибки от Кафки, 4 - клиент прислал не json, в то время как ожидается обратное, 5 - SELECT запрос на базе содержит неожиданное для таблицы поле (если к примеру клиент может присылать sql) etc. как видно все перемешано и в этом крайне сложно ориентироваться всем, тебе в том числе, тем сложнее, чем больше вариантов ошибок.
такой подход позволяет выбирать, смежник может ограничиться ошибками уровня протокола
А при чем тут "смежник"? Я про одну систему говорю, в которой много разработчиков.
в современной парадигме микросервисной архитектуры сложно представить сервис, у которого будет больше 1000 ошибок
Не все работают в микросервисной архитектуре. У вас статья про "универсальную систему ошибок приложения", без уточнений про микросервисы (а для микросервисов все еще более отдельно).
для этого и нужен первый код в рамках интервала, он так сказать "слив" для всех неизвестных ошибок с каким либо смежником (опять же это один из принципов объединения).
Еще раз говорю: неизвестно, сколько у меня интеграций. Не-из-вест-но. Как мне выбрать интервал для каждой?
объединение требуется только для того, чтобы привнести порядок в хаос
У разных людей разное представление о порядке. Это автоматически приводит к проблеме "разные люди систематизируют по-разному" - которую ваша система не решает, несмотря на ваши заверения об обратном.
1 - любая система состоит из аспектов, модулей и т.д. главное, что не спагетти, есть какая то архитектура, объединение по какому либо принципы.
2 - повторюсь, интервалы могут быть больше, Вы архитектор, основываясь на своем обширном опыте не угадать с размером интервала сложно (даже если ошиблись по итогу 10 лет эксплуатации и развития на порядок, больше необходимого, это не проблема).
3 - см. 2
4 - принцип агрегации необходим, в противном случае будет хаос (в первом комментарии Вам привел пример), а когда "правила игры" известны, можно от чего то отталкиваться.
В качестве примера, писал оркестратор, 40+ интеграций со смежными системами, где за каждой единицей - отдельная система со своим протоколом и API (скрывающая 10+ других интеграций), придерживаясь описанного в статье принципа все сложилось в стройную картину, 5 лет прошло, сервис продолжает развиваться, заложились в интервалах с запасом, все четко, как часы, отдел сопровождения знает без обращения к разработке - что к чему и почему, быстро локализует проблему, клиентские решения тоже продолжают приростать - заложенная гибкость позволяет им писать рефлексию как описал выше, клиентами выступают как 1 разработчик со своим решением, так и целая компания в 100+ разработчиков, исключение - новые интеграции, в интервалах кодов негативных исходов с которыми не все еще прокопано.
любая система состоит из аспектов, модулей и т.д. главное, что не спагетти, есть какая то архитектура, объединение по какому либо принципы.
И что?
повторюсь, интервалы могут быть больше, Вы архитектор, основываясь на своем обширном опыте не угадать с размером интервала сложно
Легко можно ошибиться, особенно учитывая, что я не контролирую все места системы.
см. 2
Я спрашиваю не про размер интервала, а про то, какой интервал выбрать, когда у меня падает соединение с облаком - третий или четвертый.
принцип агрегации необходимо
Значит, озвученная вами проблема останется нерешенной, вопреки вашим же заверениям.
1 - единственно как я могу ответить на такое возражение - продумывание архитектуры и написание кода ее реализующего не должно порождать хаос второго порядка, в противном случае компании нужно что то делать с архитекторами и разработчиками, которые его породили.
2 - хм, есть много способов рабочий процесс вынести за рамки ручного управления.
3 - основное это внести определенность в принцип выбора интервала, от этого зависит его размер, если принцип - внешняя зависимость (облако, сервис, база, шина обмена событиями между сервисами etc.), то это один порядок, если - бизнес (не отработал сценарий 1, сценарий 2, сценарий 3 обработки пользовательского запроса и вся логика, пусть и вне сервиса, объединена по этому принципу), то другой. Степень неизвестности тем выше, чем менее ясно, что и зачем команда реализует в коде для бизнеса и что за архитектуру в решении планируется/получается заложить.
единственно как я могу ответить на такое возражение - продумывание архитектуры и написание кода ее реализующего не должно порождать хаос второго порядка
Что такое "хаос второго порядка"?
хм, есть много способов рабочий процесс вынести за рамки ручного управления.
Есть, но далеко не все из них применимы во всех случаях (а вы претендуете именно на универсальное решение).
основное это внести определенность в принцип выбора интервала, от этого зависит его размер, если принцип - внешняя зависимость (облако, сервис, база, шина обмена событиями между сервисами etc.), то это один порядок, если - бизнес (не отработал сценарий 1, сценарий 2, сценарий 3 обработки пользовательского запроса и вся логика, пусть и вне сервиса, объединена по этому принципу), то другой.
Мне кажется, вы даже не понимаете, о чем я спрашиваю.
Вот вы пишете в статье:
ошибки объединены в интервалы code:
1 - 99 // зарезервированы под внутренние ошибки кода твоего сервиса;
100 - 199 // зарезервированы под первую интеграцию, например ошибки клиентских сторон;
200-299 // под вторую интеграцию, например базы данных;
300-399 // под третью интеграцию, например кафки;
400-499 // под четвертую интеграцию, например смежный сервис 1;
500-599 // под третью интеграцию, например смежный сервис 2;
В каком интервале должен быть код вызова ошибки сервиса X, который (вызов) расположен в динамически подключаемом модуле Y?
Мне вот интересно модуль Y вообще не поддается спецификации в Вашей архитектуре? Что угодно подключили? И делает что угодно?
Ваши доводы приводят меня к такой мысли: когда ничего неизвестно, то и архитектура похожа на метод result doEverything(arg1), где arg1 все, что угодно, result - тоже, в таких условиях действительно невозможно написать сколько нибудь осмысленную систему ошибок, кроме как "что то делали, почему то не вышло из за какой то ошибки."
Мне вот интересно модуль Y вообще не поддается спецификации в Вашей архитектуре?
Почему же, поддается. Он специфицирован по АПИ взаимодействия.
И делает что угодно?
А вот сделать он действительно может почти что угодно.
в таких условиях действительно невозможно написать сколько нибудь осмысленную систему ошибок
Да нет, вполне возможно. Просто не в вашей парадигме.
Да, там после пункта сделать сontrol flow на exception'ах нужно бежать с такого проекта
если есть необходимость использовать в архитектуре сквозную систему кодов ошибок и управляющих статусов, то -int будет ошибкой, а +int управляющим статусом
Диапазон значений не нужен если явно добавить поле category (только теперь у разработчиков 2 проблемы, т.к нужно согласовать значения 2 полей)
Предлагаю добавить функцию, которая возвращает код последней возникшей ошибки, и назвать её, например, errno.
Обработка ошибок внутри программы и формат сообщений об ошибках наружу это две разные проблемы. Прежде, чем изобретать кастомные решения, я бы рекомендовал посмотреть стандартные.
В первом случае реализация сильно зависит от конкретного языка программирования, фреймворка, принятых соглашений и способов интеграции. Способы, принятые в Java встанут по перек горла, если вы пишите на Golang. Кастомные способы обработки ошибок открывают разработчикам большой простор наделать новых на неизвестном им поле.
Во втором лучше ориентироваться на требования ваших систем мониторинга. Если заранее про них ни чего не известно, то можно брать OpenTelemetry как универсальное решение - сейчас худо-бедно его поддерживают практически все. Здесь же терминология (event, log, span, trace) и необходимые атрибуты, и с большой степенью вероятности- готовые SDK под вашу платформу.
Писал на таких языках, как С#, Java, Kotlin, Scala, Node.js
С опытом работы на таком стеке, на Scala вы скорее всего писали как на Java с альтернативным синтаксисом, нежели как на функциональном языке под JVM?
Если подразумевается: функциональное = с математическими функциями, где математическими = pipe, преобразующий одно значение в другое, то да, на функциональном языке под JVM в рамках задач на Hadoop кластере (преимущественно HDFS + Spark + Yarn + Hive), с полноценным MapReduce Big даты в виде всей истории + потока изменений новостей/статей/постов/комментариев/лайков/эмоций в 50000+ интернет СМИ + 20+ социальных сетях + 10000+ форумах.
Паттерн написания универсальной системы ошибок приложения