torrust_tracker/console/ci/e2e/
logs_parser.rs1use regex::Regex;
3use serde::{Deserialize, Serialize};
4
5use crate::servers::health_check_api::HEALTH_CHECK_API_LOG_TARGET;
6use crate::servers::http::HTTP_TRACKER_LOG_TARGET;
7use crate::servers::logging::STARTED_ON;
8use crate::servers::udp::UDP_TRACKER_LOG_TARGET;
9
10const INFO_THRESHOLD: &str = "INFO";
11
12#[derive(Serialize, Deserialize, Debug, Default)]
13pub struct RunningServices {
14 pub udp_trackers: Vec<String>,
15 pub http_trackers: Vec<String>,
16 pub health_checks: Vec<String>,
17}
18
19impl RunningServices {
20 #[must_use]
64 pub fn parse_from_logs(logs: &str) -> Self {
65 let mut udp_trackers: Vec<String> = Vec::new();
66 let mut http_trackers: Vec<String> = Vec::new();
67 let mut health_checks: Vec<String> = Vec::new();
68
69 let udp_re = Regex::new(&format!("{STARTED_ON}: {}", r"udp://([0-9.]+:[0-9]+)")).unwrap();
70 let http_re = Regex::new(&format!("{STARTED_ON}: {}", r"(https?://[0-9.]+:[0-9]+)")).unwrap(); let health_re = Regex::new(&format!("{STARTED_ON}: {}", r"(https?://[0-9.]+:[0-9]+)")).unwrap(); let ansi_escape_re = Regex::new(r"\x1b\[[0-9;]*m").unwrap();
73
74 for line in logs.lines() {
75 let clean_line = ansi_escape_re.replace_all(line, "");
76
77 if !line.contains(INFO_THRESHOLD) {
78 continue;
79 };
80
81 if line.contains(UDP_TRACKER_LOG_TARGET) {
82 if let Some(captures) = udp_re.captures(&clean_line) {
83 let address = Self::replace_wildcard_ip_with_localhost(&captures[1]);
84 udp_trackers.push(address);
85 }
86 } else if line.contains(HTTP_TRACKER_LOG_TARGET) {
87 if let Some(captures) = http_re.captures(&clean_line) {
88 let address = Self::replace_wildcard_ip_with_localhost(&captures[1]);
89 http_trackers.push(address);
90 }
91 } else if line.contains(HEALTH_CHECK_API_LOG_TARGET) {
92 if let Some(captures) = health_re.captures(&clean_line) {
93 let address = format!("{}/health_check", Self::replace_wildcard_ip_with_localhost(&captures[1]));
94 health_checks.push(address);
95 }
96 }
97 }
98
99 Self {
100 udp_trackers,
101 http_trackers,
102 health_checks,
103 }
104 }
105
106 fn replace_wildcard_ip_with_localhost(address: &str) -> String {
107 address.replace("0.0.0.0", "127.0.0.1")
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn it_should_parse_from_logs_with_valid_logs() {
117 let logs = r"
118 Loading configuration from default configuration file: `./share/default/config/tracker.development.sqlite3.toml` ...
119 2024-06-10T16:07:39.989540Z INFO torrust_tracker::bootstrap::logging: Logging initialized
120 2024-06-10T16:07:39.990244Z INFO UDP TRACKER: Starting on: udp://0.0.0.0:6969
121 2024-06-10T16:07:39.990255Z INFO UDP TRACKER: Started on: udp://0.0.0.0:6969
122 2024-06-10T16:07:39.990261Z INFO torrust_tracker::bootstrap::jobs: TLS not enabled
123 2024-06-10T16:07:39.990303Z INFO HTTP TRACKER: Starting on: http://0.0.0.0:7070
124 2024-06-10T16:07:39.990439Z INFO HTTP TRACKER: Started on: http://0.0.0.0:7070
125 2024-06-10T16:07:39.990448Z INFO torrust_tracker::bootstrap::jobs: TLS not enabled
126 2024-06-10T16:07:39.990563Z INFO API: Starting on http://127.0.0.1:1212
127 2024-06-10T16:07:39.990565Z INFO API: Started on http://127.0.0.1:1212
128 2024-06-10T16:07:39.990577Z INFO HEALTH CHECK API: Starting on: http://127.0.0.1:1313
129 2024-06-10T16:07:39.990638Z INFO HEALTH CHECK API: Started on: http://127.0.0.1:1313
130 ";
131
132 let running_services = RunningServices::parse_from_logs(logs);
133
134 assert_eq!(running_services.udp_trackers, vec!["127.0.0.1:6969"]);
135 assert_eq!(running_services.http_trackers, vec!["http://127.0.0.1:7070"]);
136 assert_eq!(running_services.health_checks, vec!["http://127.0.0.1:1313/health_check"]);
137 }
138
139 #[test]
140 fn it_should_support_colored_output() {
141 let logs = "\x1b[2m2024-06-14T14:40:13.028824Z\x1b[0m \x1b[33mINFO\x1b[0m \x1b[2mUDP TRACKER\x1b[0m: \x1b[37mStarted on: udp://0.0.0.0:6969\x1b[0m";
142
143 let running_services = RunningServices::parse_from_logs(logs);
144
145 assert_eq!(running_services.udp_trackers, vec!["127.0.0.1:6969"]);
146 }
147
148 #[test]
149 fn it_should_ignore_logs_with_no_matching_lines() {
150 let logs = "[Other Service][INFO] Started on: 0.0.0.0:7070";
151
152 let running_services = RunningServices::parse_from_logs(logs);
153
154 assert!(running_services.udp_trackers.is_empty());
155 assert!(running_services.http_trackers.is_empty());
156 assert!(running_services.health_checks.is_empty());
157 }
158
159 #[test]
160 fn it_should_parse_multiple_services() {
161 let logs = "
162 2024-06-10T16:07:39.990205Z INFO UDP TRACKER: Starting on: udp://0.0.0.0:6868
163 2024-06-10T16:07:39.990215Z INFO UDP TRACKER: Started on: udp://0.0.0.0:6868
164
165 2024-06-10T16:07:39.990244Z INFO UDP TRACKER: Starting on: udp://0.0.0.0:6969
166 2024-06-10T16:07:39.990255Z INFO UDP TRACKER: Started on: udp://0.0.0.0:6969
167 ";
168
169 let running_services = RunningServices::parse_from_logs(logs);
170
171 assert_eq!(running_services.udp_trackers, vec!["127.0.0.1:6868", "127.0.0.1:6969"]);
172 }
173
174 #[test]
175 fn it_should_replace_wildcard_ip_with_localhost() {
176 let address = "0.0.0.0:8080";
177 assert_eq!(RunningServices::replace_wildcard_ip_with_localhost(address), "127.0.0.1:8080");
178 }
179}