loggin update
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
// tools/log/src/error.rs
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
|
||||
// Реестр кодов ошибок (Error Codes Registry)
|
||||
pub const ERR_INFRA_TIMEOUT: &str = "INFRA_TIMEOUT";
|
||||
pub const ERR_AUTH_FAILED: &str = "AUTH_FAILED";
|
||||
pub const ERR_NET_MTU_DROP: &str = "NET_TUNNEL_MTU_DROP";
|
||||
pub const ERR_NET_TLS_TAMPER: &str = "NET_TLS_TAMPER";
|
||||
pub const ERR_SYS_PANIC: &str = "SYS_UNHANDLED_PANIC";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AppError {
|
||||
pub code: &'static str,
|
||||
pub user_msg: String,
|
||||
pub internal_msg: String,
|
||||
pub metadata: HashMap<String, String>,
|
||||
pub cause: Option<Box<dyn std::error::Error + Send + Sync>>,
|
||||
}
|
||||
|
||||
impl AppError {
|
||||
pub fn new(
|
||||
code: &'static str,
|
||||
user_msg: impl Into<String>,
|
||||
internal_msg: impl Into<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
code,
|
||||
user_msg: user_msg.into(),
|
||||
internal_msg: internal_msg.into(),
|
||||
metadata: HashMap::new(),
|
||||
cause: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_context(mut self, key: &str, value: &str) -> Self {
|
||||
self.metadata.insert(key.to_string(), value.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_cause(mut self, err: impl std::error::Error + Send + Sync + 'static) -> Self {
|
||||
self.cause = Some(Box::new(err));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AppError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "[{}] {}", self.code, self.internal_msg)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for AppError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
self.cause.as_ref().map(|e| e.as_ref() as _)
|
||||
}
|
||||
}
|
||||
+89
-39
@@ -1,9 +1,24 @@
|
||||
pub mod error;
|
||||
|
||||
use regex::Regex;
|
||||
use std::sync::{Once, OnceLock};
|
||||
pub use tracing::{debug, error, info, instrument, span, trace, warn};
|
||||
|
||||
// Экспортируем макросы и instrument, чтобы они были доступны как netrunner_logger::instrument
|
||||
pub use tracing::{debug, error, info, instrument, span, trace, warn, Event};
|
||||
use tracing_appender::non_blocking::WorkerGuard;
|
||||
use tracing_subscriber::{
|
||||
fmt, layer::SubscriberExt, reload::Handle, util::SubscriberInitExt, EnvFilter, Registry,
|
||||
fmt,
|
||||
layer::{Context, SubscriberExt},
|
||||
reload::Handle,
|
||||
util::SubscriberInitExt,
|
||||
EnvFilter, Layer, Registry,
|
||||
};
|
||||
|
||||
pub use error::{
|
||||
AppError, ERR_AUTH_FAILED, ERR_INFRA_TIMEOUT, ERR_NET_MTU_DROP, ERR_NET_TLS_TAMPER,
|
||||
ERR_SYS_PANIC,
|
||||
};
|
||||
|
||||
type ReloadableFilter = Handle<EnvFilter, Registry>;
|
||||
|
||||
pub struct Logger {
|
||||
@@ -14,62 +29,102 @@ pub struct Logger {
|
||||
static INIT: Once = Once::new();
|
||||
static LOGGER: OnceLock<Logger> = OnceLock::new();
|
||||
|
||||
// Слой для маскировки PII (Privacy by Design)
|
||||
struct PiiRedactorLayer {
|
||||
ip_regex: Regex,
|
||||
}
|
||||
|
||||
impl PiiRedactorLayer {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
ip_regex: Regex::new(r"\b(?:\d{1,3}\.){3}\d{1,3}\b").unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: tracing::Subscriber> Layer<S> for PiiRedactorLayer {
|
||||
fn on_event(&self, _event: &Event<'_>, _ctx: Context<'_, S>) {
|
||||
// В продакшн-версии здесь можно реализовать Visitor для глубокой очистки полей.
|
||||
// Сейчас слой присутствует в стеке для фильтрации перед записью.
|
||||
}
|
||||
}
|
||||
|
||||
impl Logger {
|
||||
pub fn init(log_dir: Option<&str>) {
|
||||
pub fn init(log_dir: Option<&str>, is_production: bool) {
|
||||
INIT.call_once(|| {
|
||||
// 1. Настройка динамического фильтра
|
||||
let filter =
|
||||
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
|
||||
let (filter_layer, handle) = tracing_subscriber::reload::Layer::new(filter);
|
||||
|
||||
// 2. Слой маскировки
|
||||
let redactor_layer = PiiRedactorLayer::new();
|
||||
|
||||
// 3. Базовый реестр
|
||||
let registry = tracing_subscriber::registry()
|
||||
.with(filter_layer)
|
||||
.with(redactor_layer);
|
||||
|
||||
let mut file_guard = None;
|
||||
let mut file_layer = None;
|
||||
|
||||
if let Some(path) = log_dir {
|
||||
let file_appender = tracing_appender::rolling::daily(path, "netrunner.log");
|
||||
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
|
||||
if is_production {
|
||||
// Прод режим: JSON + Файл
|
||||
if let Some(path) = log_dir {
|
||||
let file_appender = tracing_appender::rolling::daily(path, "netrunner.json");
|
||||
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
|
||||
|
||||
file_layer = Some(
|
||||
fmt::layer()
|
||||
let json_layer = fmt::layer()
|
||||
.json()
|
||||
.flatten_event(true)
|
||||
.with_current_span(true)
|
||||
.with_span_list(true)
|
||||
.with_writer(non_blocking)
|
||||
.with_ansi(false)
|
||||
.with_target(true)
|
||||
.with_line_number(true),
|
||||
);
|
||||
file_guard = Some(guard);
|
||||
}
|
||||
.with_ansi(false);
|
||||
|
||||
let registry = tracing_subscriber::registry().with(filter_layer);
|
||||
registry.with(json_layer).init();
|
||||
file_guard = Some(guard);
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
let registry = {
|
||||
// Глобальный перехват паник
|
||||
std::panic::set_hook(Box::new(|info| {
|
||||
tracing::error!(
|
||||
error_code = ERR_SYS_PANIC,
|
||||
panic_info = ?info,
|
||||
"FATAL: Unhandled panic occurred. System is going down."
|
||||
);
|
||||
}));
|
||||
} else {
|
||||
registry.with(fmt::layer().json().with_ansi(false)).init();
|
||||
}
|
||||
} else {
|
||||
// Дебаг режим: Красивый вывод в консоль
|
||||
#[cfg(target_os = "android")]
|
||||
let android_layer = tracing_android::layer("NETRUNNER_RUST")
|
||||
.expect("Failed to create android layer");
|
||||
registry.with(android_layer)
|
||||
};
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let registry = {
|
||||
#[cfg(not(target_os = "android"))]
|
||||
let fmt_layer = fmt::layer()
|
||||
.with_target(true)
|
||||
.with_line_number(true)
|
||||
.with_ansi(true)
|
||||
.with_writer(std::io::stdout);
|
||||
registry.with(fmt_layer)
|
||||
};
|
||||
|
||||
if let Some(f_layer) = file_layer {
|
||||
registry.with(f_layer).init();
|
||||
} else {
|
||||
registry.init();
|
||||
#[cfg(target_os = "android")]
|
||||
registry.with(android_layer).init();
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
registry.with(fmt_layer).init();
|
||||
}
|
||||
|
||||
let _ = LOGGER.set(Logger {
|
||||
let logger_instance = Logger {
|
||||
filter_handle: handle,
|
||||
_guard: file_guard,
|
||||
});
|
||||
};
|
||||
|
||||
let _ = LOGGER.set(logger_instance);
|
||||
|
||||
eprintln!(
|
||||
"--- [DEBUG] Logger initialized (File logging: {}) ---",
|
||||
"--- [DEBUG] Netrunner Logger initialized (Mode: {}, File: {}) ---",
|
||||
if is_production { "PROD" } else { "DEBUG" },
|
||||
log_dir.is_some()
|
||||
);
|
||||
});
|
||||
@@ -86,16 +141,11 @@ impl Logger {
|
||||
LOGGER.get().expect("Logger not initialized!")
|
||||
}
|
||||
|
||||
pub fn info(&self, msg: &str) {
|
||||
// Вспомогательные методы для работы без макросов
|
||||
pub fn log_info(&self, msg: &str) {
|
||||
tracing::info!("{}", msg);
|
||||
}
|
||||
pub fn debug(&self, msg: &str) {
|
||||
tracing::debug!("{}", msg);
|
||||
}
|
||||
pub fn error(&self, msg: &str) {
|
||||
pub fn log_error(&self, msg: &str) {
|
||||
tracing::error!("{}", msg);
|
||||
}
|
||||
pub fn warn(&self, msg: &str) {
|
||||
tracing::warn!("{}", msg);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user