Skip to main content

futu_opend/startup/phase4/
summary.rs

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