client log sends to proxy
This commit is contained in:
@@ -181,6 +181,27 @@ impl StreamHandler {
|
||||
debug!(stream_id, "🏁 [Tunnel] Peer closed stream");
|
||||
self.muxer.remove_stream(stream_id);
|
||||
}
|
||||
|
||||
FrameType::Diag => {
|
||||
// Диагностика клиента, доставленная по туннелю. Осмысленна только
|
||||
// на сервере: пересылаем в сток вместе с id сессии (берём из
|
||||
// muxer'а — на сервере это сессия этой ноги). На клиенте сток не
|
||||
// поднят, поэтому отчёт просто отбрасывается. Никогда не идёт в
|
||||
// локальные сокеты и не маршрутизируется как данные.
|
||||
let session_id = self.muxer.session_id().to_string();
|
||||
let json_line = String::from_utf8_lossy(&frame.payload).into_owned();
|
||||
trace!(
|
||||
session_id = %session_id,
|
||||
bytes = json_line.len(),
|
||||
"🩺 [Tunnel] Client diagnostics report received"
|
||||
);
|
||||
crate::net::diagnostics::report_client_diag(
|
||||
crate::net::diagnostics::ClientDiagReport {
|
||||
session_id,
|
||||
json_line,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -529,6 +529,28 @@ impl Muxer {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Идентификатор сессии этого мультиплексора (для логов/топологии и для
|
||||
/// именования пер-сессионных файлов диагностики на сервере).
|
||||
pub fn session_id(&self) -> &str {
|
||||
&self.session_id
|
||||
}
|
||||
|
||||
/// Отправляет на сервер одну строку клиентской диагностики `Diag`-кадром.
|
||||
///
|
||||
/// Холодный путь: едет по контрольному каналу с best-effort семантикой (как и
|
||||
/// прочий не-критичный контроль — при переполнении канала кадр дропается, не
|
||||
/// блокируя). Возвращает `false`, только если живых ног нет вовсе — тогда
|
||||
/// вызывающая сторона может оставить снапшот в очереди и повторить позже.
|
||||
pub async fn send_diag_report(&self, payload: Bytes) -> bool {
|
||||
self.send_to_network(MuxMessage {
|
||||
stream_id: 0,
|
||||
frame_type: FrameType::Diag,
|
||||
data: payload,
|
||||
})
|
||||
.await
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
/// Удобная обёртка для отправки управляющего кадра заданного типа.
|
||||
pub(crate) async fn send_control(
|
||||
&self,
|
||||
|
||||
@@ -54,6 +54,42 @@ pub fn send_diag_event(event: DiagnosticsEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Client-diagnostics sink (server side) ─────────────────────────────────────
|
||||
|
||||
/// Отчёт диагностики клиента, доставленный по туннелю на сервер.
|
||||
///
|
||||
/// Клиент по триггеру строит снапшот, сериализует его в одну JSON-строку и шлёт
|
||||
/// `Diag`-кадром. Сервер принимает кадр, оборачивает payload в этот тип (вместе с
|
||||
/// `session_id` ноги, по которой он пришёл) и кладёт в сток
|
||||
/// [`report_client_diag`], откуда серверный логгер пишет его в пер-сессионный файл.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ClientDiagReport {
|
||||
/// Идентификатор сессии (источник имени файла на сервере).
|
||||
pub session_id: String,
|
||||
/// Одна готовая JSON-строка снапшота (как прислал клиент).
|
||||
pub json_line: String,
|
||||
}
|
||||
|
||||
/// Sender end of the client-diagnostics sink. Set once by the server via
|
||||
/// [`init_client_diag_sink`]; stays `None` on the client, where reports are dropped.
|
||||
static GLOBAL_CLIENT_DIAG_TX: OnceLock<mpsc::UnboundedSender<ClientDiagReport>> = OnceLock::new();
|
||||
|
||||
/// Серверная сторона: вызвать один раз при старте. Возвращает приёмник
|
||||
/// клиентских отчётов, который логгер держит и сливает в пер-сессионные файлы.
|
||||
pub fn init_client_diag_sink() -> mpsc::UnboundedReceiver<ClientDiagReport> {
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
let _ = GLOBAL_CLIENT_DIAG_TX.set(tx);
|
||||
rx
|
||||
}
|
||||
|
||||
/// Fire-and-forget: переслать клиентский отчёт в сток. No-op, если сток не поднят
|
||||
/// (т.е. на клиенте) — ровно как [`send_diag_event`] относительно своего канала.
|
||||
pub fn report_client_diag(report: ClientDiagReport) {
|
||||
if let Some(tx) = GLOBAL_CLIENT_DIAG_TX.get() {
|
||||
let _ = tx.send(report);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Event types ───────────────────────────────────────────────────────────────
|
||||
|
||||
/// Событие, которое стало триггером снапшота (и единица потока событий).
|
||||
@@ -184,6 +220,15 @@ pub struct DiagnosticsSnapshot {
|
||||
pub error_totals: ErrorCounters,
|
||||
}
|
||||
|
||||
impl DiagnosticsSnapshot {
|
||||
/// Сериализует снапшот в одну компактную JSON-строку (без отступов и переводов
|
||||
/// строк). Это формат, в котором клиент шлёт снапшот `Diag`-кадром, а сервер
|
||||
/// дописывает его строкой в пер-сессионный JSONL-файл.
|
||||
pub fn to_json_line(&self) -> String {
|
||||
serde_json::to_string(self).unwrap_or_else(|e| format!("{{\"error\":\"{e}\"}}"))
|
||||
}
|
||||
}
|
||||
|
||||
// ── Atomic error counters (global, updated at event sites) ───────────────────
|
||||
|
||||
/// Глобальные атомарные счётчики событий, инкрементируемые в местах их
|
||||
|
||||
Reference in New Issue
Block a user