mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
feat(proxy): surface node resource diagnostics
This commit is contained in:
@@ -650,6 +650,10 @@ impl ProxyNodeWriteRepository for InMemoryProxyNodeRepository {
|
||||
"source": "heartbeat",
|
||||
"category": error.category,
|
||||
"message": error.message,
|
||||
"severity": error.severity.as_deref(),
|
||||
"component": error.component.as_deref(),
|
||||
"summary": error.summary.as_deref(),
|
||||
"operator_action": error.operator_action.as_deref(),
|
||||
"timestamp_unix_secs": error.timestamp_unix_secs,
|
||||
})),
|
||||
created_at_unix_ms: Some(if error.timestamp_unix_secs == 0 {
|
||||
|
||||
@@ -836,6 +836,10 @@ WHERE is_manual = 0
|
||||
"source": "heartbeat",
|
||||
"category": error.category,
|
||||
"message": error.message,
|
||||
"severity": error.severity.as_deref(),
|
||||
"component": error.component.as_deref(),
|
||||
"summary": error.summary.as_deref(),
|
||||
"operator_action": error.operator_action.as_deref(),
|
||||
"timestamp_unix_secs": error.timestamp_unix_secs,
|
||||
});
|
||||
self.insert_event(
|
||||
|
||||
@@ -1224,6 +1224,10 @@ impl ProxyNodeWriteRepository for SqlxProxyNodeRepository {
|
||||
"source": "heartbeat",
|
||||
"category": error.category,
|
||||
"message": error.message,
|
||||
"severity": error.severity.as_deref(),
|
||||
"component": error.component.as_deref(),
|
||||
"summary": error.summary.as_deref(),
|
||||
"operator_action": error.operator_action.as_deref(),
|
||||
"timestamp_unix_secs": error.timestamp_unix_secs,
|
||||
});
|
||||
self.insert_event(
|
||||
|
||||
@@ -838,6 +838,10 @@ WHERE is_manual = 0
|
||||
"source": "heartbeat",
|
||||
"category": error.category,
|
||||
"message": error.message,
|
||||
"severity": error.severity.as_deref(),
|
||||
"component": error.component.as_deref(),
|
||||
"summary": error.summary.as_deref(),
|
||||
"operator_action": error.operator_action.as_deref(),
|
||||
"timestamp_unix_secs": error.timestamp_unix_secs,
|
||||
});
|
||||
self.insert_event(
|
||||
|
||||
@@ -324,6 +324,10 @@ pub struct TunnelErrorEventRecord {
|
||||
pub timestamp_unix_secs: u64,
|
||||
pub category: String,
|
||||
pub message: String,
|
||||
pub severity: Option<String>,
|
||||
pub component: Option<String>,
|
||||
pub summary: Option<String>,
|
||||
pub operator_action: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
@@ -417,7 +421,11 @@ pub fn build_tunnel_metrics_sample(
|
||||
}
|
||||
|
||||
pub fn build_tunnel_error_event_detail(event: &TunnelErrorEventRecord) -> String {
|
||||
format!("[{}] {}", event.category, event.message)
|
||||
format!(
|
||||
"[{}] {}",
|
||||
event.category,
|
||||
event.summary.as_deref().unwrap_or(event.message.as_str())
|
||||
)
|
||||
}
|
||||
|
||||
pub fn normalize_proxy_metadata(
|
||||
@@ -497,6 +505,10 @@ fn extract_recent_tunnel_errors(proxy_metadata: Option<&Value>) -> Vec<TunnelErr
|
||||
.and_then(Value::as_str)
|
||||
.unwrap_or("n/a")
|
||||
.to_string(),
|
||||
severity: json_string(item.get("severity")),
|
||||
component: json_string(item.get("component")),
|
||||
summary: json_string(item.get("summary")),
|
||||
operator_action: json_string(item.get("operator_action")),
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
@@ -512,6 +524,14 @@ fn json_u64(value: Option<&Value>) -> Option<u64> {
|
||||
})
|
||||
}
|
||||
|
||||
fn json_string(value: Option<&Value>) -> Option<String> {
|
||||
value
|
||||
.and_then(Value::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(ToString::to_string)
|
||||
}
|
||||
|
||||
fn counter_delta_u64(previous: Option<u64>, current: u64) -> u64 {
|
||||
match previous {
|
||||
Some(previous) if current >= previous => current - previous,
|
||||
@@ -726,10 +746,11 @@ mod tests {
|
||||
use serde_json::json;
|
||||
|
||||
use super::{
|
||||
bucket_start_unix_secs, build_tunnel_metrics_sample, normalize_proxy_node_scheduling_state,
|
||||
proxy_node_accepts_new_tunnels, proxy_reported_version,
|
||||
reconcile_remote_config_after_heartbeat, remote_config_scheduling_state,
|
||||
remote_config_upgrade_target, ProxyNodeMetricsStep, StoredProxyNode,
|
||||
bucket_start_unix_secs, build_tunnel_error_event_detail, build_tunnel_metrics_sample,
|
||||
normalize_proxy_node_scheduling_state, proxy_node_accepts_new_tunnels,
|
||||
proxy_reported_version, reconcile_remote_config_after_heartbeat,
|
||||
remote_config_scheduling_state, remote_config_upgrade_target, ProxyNodeMetricsStep,
|
||||
StoredProxyNode,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -842,7 +863,15 @@ mod tests {
|
||||
},
|
||||
"recent_tunnel_errors": [
|
||||
{"timestamp_unix_secs": 100, "category": "older", "message": "old"},
|
||||
{"timestamp_unix_secs": 101, "category": "newer", "message": "new"}
|
||||
{
|
||||
"timestamp_unix_secs": 101,
|
||||
"category": "newer",
|
||||
"message": "new",
|
||||
"severity": "error",
|
||||
"component": "tunnel_write",
|
||||
"summary": "WebSocket write failed because the peer closed or reset the connection",
|
||||
"operator_action": "Check gateway restarts and network resets."
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
@@ -860,6 +889,19 @@ mod tests {
|
||||
assert_eq!(sample.ws_out_frames_delta, 3);
|
||||
assert_eq!(sample.recent_error_events.len(), 2);
|
||||
assert_eq!(sample.recent_error_events[0].category, "older");
|
||||
assert_eq!(sample.recent_error_events[0].summary, None);
|
||||
assert_eq!(
|
||||
sample.recent_error_events[1].severity.as_deref(),
|
||||
Some("error")
|
||||
);
|
||||
assert_eq!(
|
||||
sample.recent_error_events[1].component.as_deref(),
|
||||
Some("tunnel_write")
|
||||
);
|
||||
assert_eq!(
|
||||
build_tunnel_error_event_detail(&sample.recent_error_events[1]),
|
||||
"[newer] WebSocket write failed because the peer closed or reset the connection"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user