feat: 增加日志模块

This commit is contained in:
2026-04-18 12:52:32 +08:00
parent 7cd464bc6a
commit 2ca70d556b
15 changed files with 1263 additions and 186 deletions

View File

@@ -16,8 +16,8 @@ from .common import (
load_omnisocket_config,
utc_iso_now,
)
from .control import ControlArbiter, NativeUdpControlIngress, OmniSocketControlSender
from .video import FrameTrailerMetadata, OmniSocketVideoReceiver
from .control import ControlAckTracker, ControlArbiter, NativeUdpControlIngress, OmniSocketControlAckReceiver, OmniSocketControlSender
from .video import FrameTrailerMetadata, OmniSocketVideoReceiver, VideoDisplayProbeStore
LOCAL_SAMPLE_INTERVAL_MS = 500
@@ -140,7 +140,9 @@ class KcpTrendTracker:
"conv": _coerce_int(raw.get("conv")),
"rto_ms": _coerce_int(raw.get("rto_ms")),
"srtt_ms": _coerce_int(raw.get("srtt_ms")),
"min_srtt_ms": _coerce_int(raw.get("min_srtt_ms")),
"srttvar_ms": _coerce_int(raw.get("srttvar_ms")),
"last_feedback_age_ms": _coerce_int(raw.get("last_feedback_age_ms")),
"snd_wnd": snd_wnd,
"rmt_wnd": rmt_wnd,
"inflight": inflight,
@@ -419,15 +421,21 @@ class NetworkTelemetryService:
self,
video_receiver: OmniSocketVideoReceiver,
control_sender: OmniSocketControlSender,
control_ack_tracker: ControlAckTracker,
control_ack_receiver: OmniSocketControlAckReceiver,
control_arbiter: ControlArbiter,
native_ingress: NativeUdpControlIngress,
hub_receiver: HubTelemetryReceiver,
video_display_probe_store: VideoDisplayProbeStore,
) -> None:
self._video_receiver = video_receiver
self._control_sender = control_sender
self._control_ack_tracker = control_ack_tracker
self._control_ack_receiver = control_ack_receiver
self._control_arbiter = control_arbiter
self._native_ingress = native_ingress
self._hub_receiver = hub_receiver
self._video_display_probe_store = video_display_probe_store
self._trend_tracker = KcpTrendTracker()
self._rate_lock = threading.Lock()
self._last_rate_sample: tuple[float, int, int] | None = None
@@ -439,6 +447,7 @@ class NetworkTelemetryService:
def _ensure_started(self) -> None:
self._video_receiver.ensure_started()
self._control_arbiter.ensure_started()
self._control_ack_receiver.ensure_started()
self._native_ingress.ensure_started()
self._hub_receiver.ensure_started()
with self._rate_lock:
@@ -629,6 +638,74 @@ class NetworkTelemetryService:
"updated_at": _utc_from_epoch(updated_at_epoch_ms / 1000.0) or utc_iso_now(),
}
def _derive_latency_estimate(
self,
*,
links: dict[str, dict[str, Any]],
video_receiver_status: dict[str, Any],
display_probe_status: dict[str, Any],
) -> dict[str, Any]:
a_to_d_control_raw = links["a_to_d"]["sessions"]["control"]["kcp"].get("srtt_ms")
d_to_b_control_raw = links["d_to_b"]["sessions"]["control"]["kcp"].get("srtt_ms")
a_to_d_control_min_raw = links["a_to_d"]["sessions"]["control"]["kcp"].get("min_srtt_ms")
d_to_b_control_min_raw = links["d_to_b"]["sessions"]["control"]["kcp"].get("min_srtt_ms")
a_to_d_video_raw = links["a_to_d"]["sessions"]["video"]["kcp"].get("srtt_ms")
d_to_b_video_raw = links["d_to_b"]["sessions"]["video"]["kcp"].get("srtt_ms")
a_to_d_control = _coerce_float(a_to_d_control_raw) if a_to_d_control_raw is not None else None
d_to_b_control = _coerce_float(d_to_b_control_raw) if d_to_b_control_raw is not None else None
a_to_d_control_min = _coerce_float(a_to_d_control_min_raw) if a_to_d_control_min_raw is not None else None
d_to_b_control_min = _coerce_float(d_to_b_control_min_raw) if d_to_b_control_min_raw is not None else None
a_to_d_video = _coerce_float(a_to_d_video_raw) if a_to_d_video_raw is not None else None
d_to_b_video = _coerce_float(d_to_b_video_raw) if d_to_b_video_raw is not None else None
ack_estimate = self._control_ack_tracker.get_latest_estimate()
capture_to_send_raw = video_receiver_status.get("latest_capture_to_send_ms")
a_recv_to_paint_raw = display_probe_status.get("a_recv_to_paint_ms")
capture_to_send_ms = _coerce_float(capture_to_send_raw) if capture_to_send_raw is not None else None
a_recv_to_paint_ms = _coerce_float(a_recv_to_paint_raw) if a_recv_to_paint_raw is not None else None
video_network_oneway_est_ms = (
round((a_to_d_video + d_to_b_video) / 2.0, 3)
if a_to_d_video is not None and d_to_b_video is not None
else None
)
video_partial_est_ms = None
if capture_to_send_ms is not None and video_network_oneway_est_ms is not None:
video_partial_est_ms = round(capture_to_send_ms + video_network_oneway_est_ms, 3)
video_e2e_est_ms = None
if video_partial_est_ms is not None and a_recv_to_paint_ms is not None:
video_e2e_est_ms = round(video_partial_est_ms + a_recv_to_paint_ms, 3)
return {
"control_loop_rtt_ms": ack_estimate.get("control_loop_rtt_ms"),
"control_to_persist_est_ms": ack_estimate.get("control_to_persist_est_ms"),
"control_oneway_srtt_est_ms": (
round((a_to_d_control + d_to_b_control) / 2.0, 3)
if a_to_d_control is not None and d_to_b_control is not None
else None
),
"control_oneway_bestcase_est_ms": (
round((a_to_d_control_min + d_to_b_control_min) / 2.0, 3)
if a_to_d_control_min is not None and d_to_b_control_min is not None
else None
),
"video_network_oneway_est_ms": video_network_oneway_est_ms,
"video_partial_est_ms": video_partial_est_ms,
"video_e2e_est_ms": video_e2e_est_ms,
"estimate_method": {
"control": "ack_loop" if ack_estimate.get("ack_available") else "srtt_fallback",
"video": "capture_to_send+srtt/2+recv_to_paint" if video_e2e_est_ms is not None else "capture_to_send+srtt/2",
},
"clock_sync_required": False,
"assumptions": [
"control one-way estimate uses ACK loop when available",
"video one-way estimate uses per-leg SRTT and local paint timing",
],
"confidence": {
"control": "derived_ack" if ack_estimate.get("ack_available") else "fallback_srtt",
"video": "derived_local_probe" if video_e2e_est_ms is not None else "partial_without_probe",
},
}
def get_latest(self) -> dict[str, Any]:
self._ensure_started()
@@ -645,7 +722,10 @@ class NetworkTelemetryService:
arbiter_status = self._control_arbiter.get_status()
ingress_status = self._native_ingress.get_status()
sender_status = self._control_sender.get_status()
ack_receiver_status = self._control_ack_receiver.get_status()
ack_status = self._control_ack_tracker.get_latest_estimate()
telemetry_state = self._hub_receiver.get_snapshot()
display_probe_status = self._video_display_probe_store.get_status()
total_send_bytes = int(video_app.get("send_bytes", 0)) + int(control_app.get("send_bytes", 0))
total_recv_bytes = int(video_app.get("recv_bytes", 0)) + int(control_app.get("recv_bytes", 0))
@@ -697,6 +777,9 @@ class NetworkTelemetryService:
remote_stale,
),
}
self._video_receiver.update_remote_video_srtt(
_coerce_int(remote_sessions["video"]["kcp"].get("srtt_ms")) if remote_sessions["video"]["kcp"].get("srtt_ms") is not None else None
)
links = {
"a_to_d": self._build_link("local-a-side", local_updated_at, False, local_sessions),
@@ -724,6 +807,11 @@ class NetworkTelemetryService:
telemetry_state=telemetry_state,
watchdog_status=watchdog_status,
)
latency_estimate = self._derive_latency_estimate(
links=links,
video_receiver_status=video_receiver_status,
display_probe_status=display_probe_status,
)
if local_control_registered and remote_control_fresh:
peer_status = "online"
@@ -764,6 +852,12 @@ class NetworkTelemetryService:
},
},
"links": links,
"latency_estimate": latency_estimate,
"video_freshness": video_receiver_status.get("freshness", {}),
"control_ack_status": {
**ack_status,
"receiver": ack_receiver_status,
},
"telemetry_receiver": {
"hub_connected": bool(telemetry_state.get("connected")),
"hub_updated_at": telemetry_state.get("updated_at"),
@@ -781,6 +875,7 @@ class NetworkTelemetryService:
"control": {
"arbiter": arbiter_status,
"sender": sender_status,
"ack_receiver": ack_receiver_status,
},
}