Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

masterror

Фреймворк-независимые типы ошибок для приложений со стабильными кодами, отображениями HTTP/gRPC и встроенной телеметрией

Русский English 한국어

Crates.io docs.rs MSRV License


Что такое masterror?

masterror — это рабочее пространство для обработки ошибок в сервисах на Rust, которым нужно больше, чем Display и source(). Там, где thiserror ограничивается генерацией реализаций трейтов, а anyhow — распространением ошибок со стёртым типом, masterror доводит ошибку до самой транспортной границы:

  • AppError — насыщенное значение ошибки с семантической категорией (AppErrorKind), стабильным машиночитаемым кодом (AppCode), опциональным безопасным публичным сообщением, структурированными метаданными и транспортными подсказками (Retry-After, WWW-Authenticate).
  • Консервативные отображения HTTP и gRPC — каждый вид и код детерминированно отображается в HTTP-статус, дискриминант tonic::Code и URI type по RFC 7807.
  • Типизированная телеметрия — метаданные хранятся как типизированные поля (строки, целые числа, числа с плавающей точкой, длительности, IP-адреса, UUID, JSON) с политиками редактирования для каждого поля, а не как самодельные String-карты.
  • Нативные derive-макросы#[derive(Error)] повторяет синтаксис thiserror, а #[app_error(...)] и #[derive(Masterror)] встраивают доменные ошибки в AppError с кодами, категориями, редактированием и таблицами отображений.
  • Редактирование по замыслу — источники никогда не сериализуются для клиентов; сообщения, детали и поля метаданных могут быть удалены, захешированы или замаскированы на границе.

Без unsafe, с зафиксированным MSRV и поддержкой no_std при отключённой функции std, включённой по умолчанию.

Какую проблему он решает

Задачаthiserroranyhowmasterror
Derive для Display / source()ДаДа (тот же синтаксис)
Распространение со стёртым типом и контекстомДаДа (.ctx() / .context())
Стабильные машиночитаемые коды ошибокВручнуюВручнуюAppCode, часть wire-контракта
Отображение в HTTP-статусВручнуюВручнуюAppErrorKind::http_status(), стабильная таблица
Отображение в gRPC-статусВручнуюВручнуюCODE_MAPPINGS, конверсия в tonic::Status
RFC 7807 problem+jsonВручнуюВручнуюProblemJson::from_app_error
Структурированные типизированные метаданныеMetadata + конструкторы field::*
Редактирование секретов на границеMessageEditPolicy, FieldRedaction
Эмиссия tracing / metrics / backtraceЗа флагами возможностей, автоматически при создании

Enum, произведённый thiserror, говорит, что случилось. masterror дополнительно решает, что увидит клиент (статус, код, безопасное сообщение, problem+json), что увидят операторы (структурированные поля, события tracing, счётчики) и что никогда не утечёт (источники, отредактированные поля).

Основные возможности

ОбластьЧто вы получаете
Базовая таксономияAppError, AppErrorKind (23 стабильные категории), AppCode (коды в SCREAMING_SNAKE_CASE, поддержка собственных кодов), AppResult<T>
Derive-макросы#[derive(Error)], #[derive(Masterror)], #[app_error(...)], #[masterror(...)], провайдеры телеметрии #[provide(...)]
Управление потокомensure! / fail! — типизированные ранние возвраты без выделения памяти
КонтекстResultExt::ctx / ResultExt::context, билдер Context с отслеживанием вызывающей стороны
Wire-полезные нагрузкиErrorResponse (устаревший JSON), ProblemJson (RFC 7807) с подсказками повторных попыток и аутентификации
ТранспортыAxum IntoResponse, Actix ResponseError/Responder, tonic::Status, WASM JsValue, схема OpenAPI
Интеграцииsqlx, redis, reqwest, validator, config, tokio, teloxide, init data Telegram Mini Apps, Turnkey
НаблюдаемостьСобытия tracing, счётчики metrics, ленивый захват backtrace, цветной вывод в терминале, DisplayMode (prod/staging/local)

Быстрый пример

use masterror::{AppError, AppErrorKind, AppResult, ProblemJson, field};

fn find_user(id: u64) -> AppResult<()> {
    masterror::ensure!(id != 0, AppError::bad_request("id must be non-zero"));

    Err(AppError::not_found("user not found")
        .with_field(field::u64("user_id", id))
        .with_field(field::str("request_id", "abc123")))
}

let err = find_user(42).unwrap_err();
assert_eq!(err.kind, AppErrorKind::NotFound);
assert_eq!(err.kind.http_status(), 404);

let problem = ProblemJson::from_app_error(err);
assert_eq!(problem.status, 404);
assert_eq!(problem.code.as_str(), "NOT_FOUND");
assert_eq!(problem.grpc.expect("grpc").name, "NOT_FOUND");

Или объявите доменную ошибку один раз и позвольте derive-макросу выполнить отображение:

use masterror::{AppCode, AppError, AppErrorKind, Error};

#[derive(Debug, Error)]
#[error("missing flag: {name}")]
#[app_error(kind = AppErrorKind::BadRequest, code = AppCode::BadRequest, message)]
struct MissingFlag {
    name: &'static str
}

let app: AppError = MissingFlag { name: "feature" }.into();
assert!(matches!(app.kind, AppErrorKind::BadRequest));

Крейты рабочего пространства

КрейтРоль
masterrorОсновные типы ошибок, метаданные, транспорты, интеграции, прелюдия
masterror-deriveПроцедурные макросы за #[derive(Error)] и #[derive(Masterror)] (подключается автоматически)
masterror-templateОбщий парсер шаблонов #[error("...")]

Документация

Начало работы

Основные концепции

Интеграции

Продвинутое