Skip to content

Refine THProviderErrorMapper error mapping and MDC tests#78

Merged
karle0wne merged 1 commit intoepic/trace-enhancemetsfrom
fixes-asd3
Oct 10, 2025
Merged

Refine THProviderErrorMapper error mapping and MDC tests#78
karle0wne merged 1 commit intoepic/trace-enhancemetsfrom
fixes-asd3

Conversation

@karle0wne
Copy link
Contributor

@karle0wne karle0wne commented Oct 10, 2025

существующие тесты пропустили
для ускорения дебага // @test todo

Details

⛬ Как сейчас работает THProviderErrorMapper (по шагам)

  1. Что он делает в целом

THProviderErrorMapper — это центральный «переводчик» между внутренними исключениями/метаданными Woody и тем, что уходит
наружу: либо как HTTP-ответ (через THResponseInfo), либо как WErrorDefinition, который дальше разбирают слушатели и клиенты.

──────────────────────────────────────────

  1. Когда есть готовый WErrorDefinition (например, в метаданных)

getResponseInfo(ContextSpan span)

  1. Смотрим WErrorDefinition в метаданных спана (ERROR_DEFINITION).
  2. Нет определения → отдадим «200, всё хорошо».
  3. Есть определение → по типу и generationSource решаем HTTP-статус:
    • BUSINESS_ERROR → всегда 200, errClass = "business_error".
    • PROVIDER_ERROR:
    • Если это ошибка ещё на входе (запрос не успели обработать) → статус 4xx в зависимости от TErrorType/метаданных:
    • PROTOCOL → 400
    • TRANSPORT → mapTransportErrorStatus (405 / 415 / 400)
    • Остальное → 400
    • Если это уже в ответе → 500, если генерили сами (internal), или 502, если проксировали внешний сбой.
    • UNAVAILABLE_RESULT → 503 (internal) или 502 (external).
    • UNDEFINED_RESULT → 504 (internal) или 502 (external).
    • UNEXPECTED_ERROR → 500 (internal) или 502 (external).
  4. errReason берётся из WErrorDefinition.
  5. Если в спане лежит THRequestInterceptionException (например, перехватчик не пропустил запрос) — статус и тип ошибки
    перебиваются на транспортный (400/405/415), потому что до сервиса мы не дошли.
  6. Результат возвращаем в виде THResponseInfo.

──────────────────────────────────────────

  1. Когда нужно построить WErrorDefinition по HTTP-ответу

createErrorDefinition(THResponseInfo info, Supplier invalidErrClass)

  1. Смотрим статус:
    • 200 + errClass=business_error → создаём WErrorDefinition:
    • generationSource = EXTERNAL
    • errorType = BUSINESS_ERROR
    • errorSource = INTERNAL (ошибка бизнес-логики сервиса)
    • 503 → UNAVAILABLE_RESULT, generationSource = EXTERNAL, errorSource = INTERNAL.
    • 504 → UNDEFINED_RESULT, generationSource = EXTERNAL, errorSource = INTERNAL.
    • 502 → UNEXPECTED_ERROR (если не указано лучше), и обе стороны EXTERNAL. Если внутри 502 всё-таки сидит business_error,
    вызываем invalidErrClass.get() — сигнал, что кто-то неправильно вернул бизнес-ошибку через 502.
    • Любой другой статус (например, 400/500) → UNEXPECTED_ERROR, generationSource = EXTERNAL, errorSource = INTERNAL.
  2. Сообщение нормализуем:
    • Для 4xx ставим стандартные текстовые Reason-Phrase (Bad Request, Method Not Allowed, т. п.).
    • Если у 5xx пустой errClass и есть errReason, чтобы не светить 500 без содержания, понижаем сообщение до «Bad Request».
    • Если совсем ничего нет, используем дефолтный Reason-Phrase по статусу.
  3. Возвращаем новый WErrorDefinition (или null, если статус 200 без бизнес-ошибки — там реально «всё ок»).

──────────────────────────────────────────

  1. Когда есть исключение

mapToDef(Throwable t, ContextSpan span)

  1. Если исключение — thrift-ошибка (или транспорт Woody), пытаемся достать уже готовое определение из метаданных (чтобы не
    переписывать то, что поставили расширения/перехватчики). Если есть и исключение не THRequestInterceptionException, берём
    его.
  2. Иначе строим новое через createDefFromWrappedError(metadata, err):
    • TApplicationException.PROTOCOL_ERROR → TErrorType.PROTOCOL, причина «thrift protocol error».
    • TApplicationException.UNKNOWN_METHOD → UNKNOWN_CALL, причина «unknown method: <имя>».
    • Остальные TApplicationException → UNKNOWN, причина «unknown provider error: …».
    • TProtocolException → PROTOCOL, причина «thrift protocol error».
    • TTransportException → TRANSPORT, причина «thrift transport error».
    • Если в метаданных статус 4xx → считаем, что это внешняя ошибка (generationSource = EXTERNAL, errorSource = EXTERNAL).
    • Если статуса нет, но сообщение похоже на пустой ответ HTTP (HTTP response code: …, «No more data available.») → тоже
    внешняя.
    • THRequestInterceptionException (до сервиса не дошли) → TRANSPORT, подставляем подтип (BAD_HEADER, BAD_CONTENT_TYPE и т.
    д.), генератором считаем:
    • клиентский контекст → generationSource = INTERNAL (мы сами забраковали свой outbound запрос);
    • серверный контекст → EXTERNAL (пришёл мусор извне).
    • errorSource ставим равным generationSource, а причину формируем читаемо («bad header: X-Test», «content type
    wrong/missing» и т. д.).
    • Любое другое исключение → UNKNOWN, причина «internal thrift application error».
  3. Новая дефиниция всегда получает errorType = PROVIDER_ERROR, errorSource соответствует определённому generationSource,
    errorReason/errorMessage — по исключению.
  4. В метаданные записываем TH_ERROR_TYPE (и при необходимости TH_ERROR_SUBTYPE), чтобы getResponseInfo понимал, как
    обрабатывать.

──────────────────────────────────────────

  1. Вспомогательные моменты
    • mapTransportErrorStatus переводит транспортные ошибки в коды: BAD_REQUEST_TYPE → 405, BAD_CONTENT_TYPE → 415, остальные →
    400.
    • isNoPayloadTransportError ловит текстовые сообщения TTransportException без полезной нагрузки («HTTP Response code: …»,
    «No more data available.») и помечает их как внешние.
    • Если перехватчик (ContextUtils.getInterceptionError) уже прервал запрос, getResponseInfo обязательно отдаёт 4xx,
    независимо от дальнейших событий.

──────────────────────────────────────────

  1. Что изменилось (кратко)
    • Внешний шум (apстрим/сетевые ошибки) теперь показывается как generationSource=EXTERNAL, errorSource=EXTERNAL.
    • Настоящие ошибки сервиса (бизнес, undefined/unavailable результат, внутренние 500) — generationSource=EXTERNAL, но
    errorSource=INTERNAL, что отражает, что сбой произошёл у нас.
    • Сообщения по 4xx/5xx нормализованы, чтобы клиент видел понятные Reason-Phrase.
    • Тесты (THProviderErrorMapperTest, MDCLogTest, TestClientEventHandling, TestNginxError и пр.) покрывают эти комбинации и
    подтверждают согласованность.

В итоге любой внешний потребитель по WErrorDefinition и HTTP-статусу точно понимает, кто виноват (мы или апстрим), и что за
проблема случилась.

- extend MDC utils and interceptor logic, adding MdcUtilsExtendedTest coverage
- align THProviderErrorMapper errorSource/generationSource rules and normalize HTTP messages using EnglishReasonPhraseCatalog
- add dedicated THProviderErrorMapper tests and adjust thrift test suite expectations
- declare missing httpcore5/httpclient5 dependencies
@karle0wne karle0wne requested a review from a team as a code owner October 10, 2025 11:55
@karle0wne karle0wne merged commit 59320bc into epic/trace-enhancemets Oct 10, 2025
3 checks passed
karle0wne added a commit that referenced this pull request Oct 31, 2025
* init plan

* guarantee MDC cleanup plus span termination when traces are absent  (#73)

* bump test log level

* Update dependency org.apache.commons:commons-lang3 to v3.18.0 [SECURITY] (#70)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Replace dependency org.slf4j:slf4j-log4j12 with org.slf4j:slf4j-reload4j (#67)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Update all non-major maven dependencies (#51)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* add missing pom sonatype  elements

* Refine THProviderErrorMapper error mapping and MDC tests (#78)

- extend MDC utils and interceptor logic, adding MdcUtilsExtendedTest coverage
- align THProviderErrorMapper errorSource/generationSource rules and normalize HTTP messages using EnglishReasonPhraseCatalog
- add dedicated THProviderErrorMapper tests and adjust thrift test suite expectations
- declare missing httpcore5/httpclient5 dependencies

* refactor: unify trace metadata and rpc handling and add MDC propagation tests (#79)

* feat: align OTel trace propagation across transport bundles and asynс flows (#80)

* fixes

* codacy

* codacy

* codacy

* codacy

* codacy

* add otel attributes (#81)

* second put mdc after filling metadata (#82)

* add MdcRefreshInterceptor (#83)

* bump

* bump

* bump

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
@karle0wne karle0wne deleted the fixes-asd3 branch October 31, 2025 10:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments