Skip to main content

futu_opend/startup/phase4/
summary.rs

1use futu_gateway_core::bridge::GatewayBridge;
2
3use crate::config::RuntimeConfig;
4
5pub(super) fn merge_push_health_snapshots_for_rest(
6    push: serde_json::Value,
7    qot_login: serde_json::Value,
8    backend_connected: bool,
9) -> serde_json::Value {
10    let mut combined = match push {
11        serde_json::Value::Object(map) => map,
12        other => {
13            let mut map = serde_json::Map::new();
14            map.insert(
15                "error".to_string(),
16                serde_json::Value::String(format!(
17                    "push_health snapshot serialized to non-object {}",
18                    json_value_kind(&other)
19                )),
20            );
21            map
22        }
23    };
24    combined.insert("qot_login_health".to_string(), qot_login);
25    combined.insert(
26        "backend_connected".to_string(),
27        serde_json::Value::Bool(backend_connected),
28    );
29    serde_json::Value::Object(combined)
30}
31
32fn json_value_kind(value: &serde_json::Value) -> &'static str {
33    match value {
34        serde_json::Value::Null => "null",
35        serde_json::Value::Bool(_) => "bool",
36        serde_json::Value::Number(_) => "number",
37        serde_json::Value::String(_) => "string",
38        serde_json::Value::Array(_) => "array",
39        serde_json::Value::Object(_) => "object",
40    }
41}
42
43fn optional_surface_addr(bind_ip: &str, port: Option<u16>) -> String {
44    port.map(|port| format!("{bind_ip}:{port}"))
45        .unwrap_or_else(|| "disabled".to_string())
46}
47
48pub(super) fn http_host_for_bind(ip: &str) -> String {
49    match ip {
50        "0.0.0.0" | "::" => "127.0.0.1".to_string(),
51        value if value.contains(':') && !value.starts_with('[') => format!("[{value}]"),
52        value => value.to_string(),
53    }
54}
55
56fn readyz_url(config: &RuntimeConfig) -> String {
57    config
58        .rest_port
59        .map(|port| format!("http://{}:{port}/readyz", http_host_for_bind(&config.ip)))
60        .unwrap_or_else(|| "disabled".to_string())
61}
62
63pub(super) fn log_startup_summary(
64    config: &RuntimeConfig,
65    listen_addr: &str,
66    bridge: &GatewayBridge,
67) {
68    let status = bridge.snapshot_status();
69    let brokers_total = status.brokers.len();
70    let brokers_connected = status
71        .brokers
72        .iter()
73        .filter(|broker| broker.connected)
74        .count();
75    let attribution = status.login.attribution.as_deref().unwrap_or("unknown");
76    let readyz = readyz_url(config);
77    let websocket_addr = optional_surface_addr(&config.ip, config.websocket_port);
78    let rest_addr = optional_surface_addr(&config.ip, config.rest_port);
79    let grpc_addr = optional_surface_addr(&config.ip, config.grpc_port);
80    let telnet_addr = optional_surface_addr(&config.telnet_ip, config.telnet_port);
81
82    tracing::info!(
83        marker = "startup_summary",
84        version = status.version,
85        uid = ?status.login.user_id,
86        attribution,
87        platform_connected = status.platform.connected,
88        brokers_connected,
89        brokers_total,
90        ftapi_addr = %listen_addr,
91        rest_addr = %rest_addr,
92        grpc_addr = %grpc_addr,
93        websocket_addr = %websocket_addr,
94        telnet_addr = %telnet_addr,
95        readyz = %readyz,
96        "startup summary: version={} uid={:?} attribution={} brokers={}/{} ftapi={} rest={} grpc={} ws={} telnet={} readyz={}",
97        status.version,
98        status.login.user_id,
99        attribution,
100        brokers_connected,
101        brokers_total,
102        listen_addr,
103        rest_addr,
104        grpc_addr,
105        websocket_addr,
106        telnet_addr,
107        readyz
108    );
109}