docs [AI]
This commit is contained in:
+21
-2
@@ -1,20 +1,37 @@
|
||||
// tools/log/src/error.rs
|
||||
//! Типизированная ошибка приложения и реестр кодов ошибок.
|
||||
//!
|
||||
//! [`AppError`] несёт стабильный машинный `code` (из реестра `ERR_*` ниже),
|
||||
//! раздельные сообщения для пользователя и для логов, произвольные метаданные и
|
||||
//! опциональную причину-источник. Коды используются в логах и метриках как
|
||||
//! устойчивые идентификаторы классов ошибок.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
|
||||
// Реестр кодов ошибок (Error Codes Registry)
|
||||
// ── Реестр кодов ошибок (стабильные машинные идентификаторы) ──
|
||||
/// Таймаут инфраструктуры (сеть/DNS/соединение).
|
||||
pub const ERR_INFRA_TIMEOUT: &str = "INFRA_TIMEOUT";
|
||||
/// Провал аутентификации (неверный auth-тег/payload).
|
||||
pub const ERR_AUTH_FAILED: &str = "AUTH_FAILED";
|
||||
/// Пакет отброшен из-за MTU в туннеле.
|
||||
pub const ERR_NET_MTU_DROP: &str = "NET_TUNNEL_MTU_DROP";
|
||||
/// Нарушение маскировки/целостности TLS (tampering, провал AEAD).
|
||||
pub const ERR_NET_TLS_TAMPER: &str = "NET_TLS_TAMPER";
|
||||
/// Необработанная паника (перехватывается логгером).
|
||||
pub const ERR_SYS_PANIC: &str = "SYS_UNHANDLED_PANIC";
|
||||
|
||||
/// Ошибка приложения с машинным кодом, раздельными сообщениями и контекстом.
|
||||
#[derive(Debug)]
|
||||
pub struct AppError {
|
||||
/// Стабильный код класса ошибки (один из `ERR_*`).
|
||||
pub code: &'static str,
|
||||
/// Сообщение для пользователя (может показываться в UI).
|
||||
pub user_msg: String,
|
||||
/// Техническое сообщение для логов/отладки.
|
||||
pub internal_msg: String,
|
||||
/// Произвольные пары ключ-значение с контекстом.
|
||||
pub metadata: HashMap<String, String>,
|
||||
/// Опциональная причина-источник (для цепочки ошибок).
|
||||
pub cause: Option<Box<dyn std::error::Error + Send + Sync>>,
|
||||
}
|
||||
|
||||
@@ -33,11 +50,13 @@ impl AppError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Добавляет пару ключ-значение в метаданные (builder-стиль).
|
||||
pub fn with_context(mut self, key: &str, value: &str) -> Self {
|
||||
self.metadata.insert(key.to_string(), value.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Прикрепляет причину-источник ошибки (builder-стиль).
|
||||
pub fn with_cause(mut self, err: impl std::error::Error + Send + Sync + 'static) -> Self {
|
||||
self.cause = Some(Box::new(err));
|
||||
self
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
//! # netrunner-logger — единый логгер и реестр ошибок проекта
|
||||
//!
|
||||
//! Тонкая обёртка над `tracing`/`tracing-subscriber`, дающая всем крейтам общий
|
||||
//! стиль логирования и типизированные ошибки ([`error`]). Особенности:
|
||||
//!
|
||||
//! - **Один глобальный [`Logger`]** (`OnceLock` + `Once`): инициализируется раз,
|
||||
//! уровень переключается на лету через reloadable-фильтр ([`set_level`](Logger::set_level)).
|
||||
//! - **Два режима**: production (JSON в файл с суточной ротацией + перехват паник)
|
||||
//! и debug (цветной вывод в консоль; на Android — `tracing_android`).
|
||||
//! - **PII-редактор** ([`PiiRedactorLayer`]) — слой-заготовка для маскировки IP и
|
||||
//! прочих чувствительных данных перед записью.
|
||||
//!
|
||||
//! Макросы `info!`/`error!`/… ре-экспортируются, чтобы во всех крейтах писать
|
||||
//! `netrunner_logger::info!` без прямой зависимости на `tracing`.
|
||||
|
||||
pub mod error;
|
||||
|
||||
use regex::Regex;
|
||||
@@ -21,6 +36,8 @@ pub use error::{
|
||||
|
||||
type ReloadableFilter = Handle<EnvFilter, Registry>;
|
||||
|
||||
/// Глобальный логгер: ручка перезагружаемого фильтра уровня + guard файлового
|
||||
/// аппендера (держит фоновый writer живым, пока жив логгер).
|
||||
pub struct Logger {
|
||||
filter_handle: ReloadableFilter,
|
||||
_guard: Option<WorkerGuard>,
|
||||
@@ -29,6 +46,7 @@ pub struct Logger {
|
||||
static INIT: Once = Once::new();
|
||||
static LOGGER: OnceLock<Logger> = OnceLock::new();
|
||||
|
||||
/// Слой маскировки PII (сейчас — заготовка; regex для IP готов к использованию).
|
||||
#[allow(dead_code)]
|
||||
struct PiiRedactorLayer {
|
||||
ip_regex: Regex,
|
||||
@@ -50,6 +68,9 @@ impl<S: tracing::Subscriber> Layer<S> for PiiRedactorLayer {
|
||||
}
|
||||
|
||||
impl Logger {
|
||||
/// Инициализирует глобальный логгер (идемпотентно — только первый вызов
|
||||
/// действует). `is_production` выбирает JSON-в-файл + перехват паник против
|
||||
/// цветного вывода в консоль; `log_dir` — куда писать файлы в прод-режиме.
|
||||
pub fn init(log_dir: Option<&str>, is_production: bool) {
|
||||
INIT.call_once(|| {
|
||||
// 1. Настройка динамического фильтра
|
||||
@@ -130,6 +151,7 @@ impl Logger {
|
||||
});
|
||||
}
|
||||
|
||||
/// Меняет уровень логирования на лету (например `"debug"`, `"info,foo=warn"`).
|
||||
pub fn set_level(&self, level: &str) {
|
||||
if let Ok(new_filter) = EnvFilter::try_new(level) {
|
||||
let _ = self.filter_handle.reload(new_filter);
|
||||
@@ -137,6 +159,7 @@ impl Logger {
|
||||
}
|
||||
}
|
||||
|
||||
/// Доступ к глобальному логгеру (паникует, если [`init`](Logger::init) не звали).
|
||||
pub fn global() -> &'static Logger {
|
||||
LOGGER.get().expect("Logger not initialized!")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user