opcua_server/
metrics.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2022 Adam Lock
4
5//! Provides debug metric of server state that can be used by anything that wants
6//! to see what is happening in the server. State is updated by the server as sessions are added, removed,
7//! and when subscriptions / monitored items are added, removed.
8
9use opcua_types::DateTime;
10
11use crate::{
12    comms::transport::{Transport, TransportState},
13    config,
14    diagnostics::ServerDiagnostics,
15    server,
16    state::ServerState,
17    subscriptions::subscriptions,
18};
19
20#[derive(Serialize)]
21pub struct ServerMetrics {
22    pub server: Server,
23    pub diagnostics: ServerDiagnostics,
24    pub config: Option<config::ServerConfig>,
25    pub connections: Vec<Connection>,
26    pub runtime_components: Vec<String>,
27}
28
29#[derive(Serialize)]
30pub struct Server {
31    pub start_time: String,
32    pub uptime_ms: i64,
33}
34
35#[derive(Serialize)]
36pub struct Connection {
37    pub sessions: Vec<Session>,
38    // creation time
39    // state
40    pub client_address: String,
41    pub transport_state: String,
42}
43
44#[derive(Serialize)]
45pub struct Session {
46    pub id: String,
47    pub session_activated: bool,
48    pub session_terminated: bool,
49    pub session_terminated_at: String,
50    pub subscriptions: subscriptions::Metrics,
51}
52
53impl Default for ServerMetrics {
54    fn default() -> Self {
55        // Sample metrics
56        Self {
57            server: Server {
58                start_time: String::new(),
59                uptime_ms: 0,
60            },
61            diagnostics: ServerDiagnostics::default(),
62            config: None,
63            connections: Vec::new(),
64            runtime_components: Vec::new(),
65        }
66    }
67}
68
69impl ServerMetrics {
70    pub fn new() -> ServerMetrics {
71        Self::default()
72    }
73
74    pub fn set_server_info(&mut self, server: &server::Server) {
75        let server_state = server.server_state();
76        let config = {
77            let server_state = trace_read_lock!(server_state);
78            server_state.config.clone()
79        };
80        let mut config = {
81            let config = trace_read_lock!(config);
82            config.clone()
83        };
84        // For security, blank out user tokens
85        config.user_tokens.clear();
86        config.user_tokens.insert(
87            String::new(),
88            config::ServerUserToken {
89                user: String::from("User identity tokens have been removed"),
90                pass: None,
91                x509: None,
92                thumbprint: None,
93            },
94        );
95        self.config = Some(config);
96    }
97
98    // Update the server state metrics (uptime etc.)
99    pub fn update_from_server_state(&mut self, server_state: &ServerState) {
100        let start_time = &server_state.start_time;
101        let now = DateTime::now();
102
103        self.server.start_time = start_time.as_chrono().to_rfc3339();
104
105        // Take a snapshot of the diagnostics
106        {
107            let diagnostics = trace_read_lock!(server_state.diagnostics);
108            self.diagnostics = diagnostics.clone();
109        }
110
111        let elapsed = now
112            .as_chrono()
113            .signed_duration_since(start_time.as_chrono());
114        self.server.uptime_ms = elapsed.num_milliseconds();
115    }
116
117    // Update the connection metrics which includes susbcriptions and monitored items
118    pub fn update_from_connections(&mut self, connections: server::Connections) {
119        self.runtime_components = runtime_components!();
120        self.connections = connections
121            .iter()
122            .map(|c| {
123                // Carefully extract info while minimizing chance of deadlock
124                let (client_address, transport_state, session_manager) = {
125                    let connection = trace_read_lock!(c);
126                    let client_address =
127                        if let Some(ref client_address) = connection.client_address() {
128                            format!("{:?}", client_address)
129                        } else {
130                            String::new()
131                        };
132                    let transport_state = match connection.state() {
133                        TransportState::New => "New".to_string(),
134                        TransportState::WaitingHello => "WaitingHello".to_string(),
135                        TransportState::ProcessMessages => "ProcessMessages".to_string(),
136                        TransportState::Finished(status_code) => {
137                            format!("Finished({})", status_code)
138                        }
139                    };
140                    (
141                        client_address,
142                        transport_state,
143                        connection.session_manager(),
144                    )
145                };
146                let session_manager = trace_read_lock!(session_manager);
147                let sessions = session_manager
148                    .sessions
149                    .iter()
150                    .map(|(_, session)| {
151                        let session = trace_read_lock!(session);
152                        let id = session.session_id().to_string();
153                        let session_activated = session.is_activated();
154                        let session_terminated = session.is_terminated();
155                        let session_terminated_at = if session.is_terminated() {
156                            session.terminated_at().to_rfc3339()
157                        } else {
158                            String::new()
159                        };
160                        let subscriptions = session.subscriptions().metrics();
161                        Session {
162                            id,
163                            session_activated,
164                            session_terminated,
165                            session_terminated_at,
166                            subscriptions,
167                        }
168                    })
169                    .collect();
170
171                // session.subscriptions.iterate ...
172                Connection {
173                    client_address,
174                    transport_state,
175                    sessions,
176                }
177            })
178            .collect();
179    }
180}