Skip to content

Commit 231a88c

Browse files
fixes #16
Now showing Latency, CPU, RAM and RabbitMQ metrics in the replica tooltips. It brings quick detailed information about the health of the infrastructe and can avoid SSH’ing to a server to further check a sick or dead node. Signed-off-by: Valerian Saliou <valerian@valeriansaliou.name>
1 parent 07d9c7c commit 231a88c

6 files changed

Lines changed: 170 additions & 52 deletions

File tree

dev/workspaces/reporter.paw

170 Bytes
Binary file not shown.

res/assets/stylesheets/common.css

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,26 +140,54 @@ a:hover {
140140
text-align: center;
141141
text-shadow: none;
142142
text-transform: initial;
143-
padding: 6px 20px 7px;
144143
display: inline-block;
145144
left: 50%;
146145
border-radius: 1px;
147146
}
148147

149-
.has-tooltip .tooltip .tooltip-value .tooltip-main,
150-
.has-tooltip .tooltip .tooltip-value .tooltip-label {
148+
.has-tooltip .tooltip .tooltip-value .tooltip-value-text {
149+
padding: 6px 20px 7px;
150+
display: block;
151+
}
152+
153+
.has-tooltip .tooltip .tooltip-value .tooltip-value-text .tooltip-main,
154+
.has-tooltip .tooltip .tooltip-value .tooltip-value-text .tooltip-label {
151155
display: block;
152156
}
153157

154-
.has-tooltip .tooltip .tooltip-value .tooltip-main {
158+
.has-tooltip .tooltip .tooltip-value .tooltip-value-text .tooltip-main {
155159
color: #ffffff;
156160
}
157161

158-
.has-tooltip .tooltip .tooltip-value .tooltip-label {
162+
.has-tooltip .tooltip .tooltip-value .tooltip-value-text .tooltip-label {
159163
color: rgba(255, 255, 255, 0.75);
160164
margin-top: 1px;
161165
}
162166

167+
.has-tooltip .tooltip .tooltip-value .tooltip-value-details {
168+
border-top: 1px solid rgba(255, 255, 255, 0.2);
169+
padding: 7px 20px 9px;
170+
display: block;
171+
}
172+
173+
.has-tooltip .tooltip .tooltip-value .tooltip-value-details .tooltip-detail:after {
174+
content: "";
175+
background-color: rgba(255, 255, 255, 0.4);
176+
vertical-align: sub;
177+
width: 1px;
178+
height: 13px;
179+
margin: 0 8px;
180+
display: inline-block;
181+
}
182+
183+
.has-tooltip .tooltip .tooltip-value .tooltip-value-details .tooltip-detail:last-child:after {
184+
display: none;
185+
}
186+
187+
.has-tooltip .tooltip .tooltip-value .tooltip-value-details .tooltip-detail-label {
188+
text-decoration: underline;
189+
}
190+
163191
.has-tooltip:hover .tooltip {
164192
display: block;
165193
pointer-events: auto;

res/assets/templates/index.tera

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -132,23 +132,51 @@
132132

133133
<span class="tooltip">
134134
<span class="tooltip-value">
135-
<span class="tooltip-main font-sans-semibold">
136-
{% if replica.status == "dead" %}
137-
This replica reports as dead.
138-
{% elif replica.status == "sick" %}
139-
This replica reports as sick.
140-
{% else %}
141-
This replica reports as healthy.
142-
{% endif %}
135+
<span class="tooltip-value-text">
136+
<span class="tooltip-main font-sans-semibold">
137+
{% if replica.status == "dead" %}
138+
This replica reports as dead.
139+
{% elif replica.status == "sick" %}
140+
This replica reports as sick.
141+
{% else %}
142+
This replica reports as healthy.
143+
{% endif %}
144+
</span>
145+
146+
<span class="tooltip-label font-sans-light">
147+
{% if node.mode == "push" %}
148+
Checked via a push probe (Reporter).
149+
{% else %}
150+
Checked via a poll probe (HTTP or TCP).
151+
{% endif %}
152+
</span>
143153
</span>
144154

145-
<span class="tooltip-label font-sans-light">
146-
{% if node.mode == "push" %}
147-
Checked via a push probe (Reporter).
148-
{% else %}
149-
Checked via a poll probe (HTTP or TCP).
150-
{% endif %}
151-
</span>
155+
{% if replica.metrics.system or replica.metrics.latency or replica.metrics.rabbitmq %}
156+
<span class="tooltip-value-details">
157+
{% if replica.metrics.system %}
158+
<span class="tooltip-detail font-sans-regular">
159+
<span class="tooltip-detail-label font-sans-semibold">CPU</span>: {{ replica.metrics.system.cpu }}%
160+
</span>
161+
162+
<span class="tooltip-detail font-sans-regular">
163+
<span class="tooltip-detail-label font-sans-semibold">RAM</span>: {{ replica.metrics.system.ram }}%
164+
</span>
165+
{% endif %}
166+
167+
{% if replica.metrics.latency %}
168+
<span class="tooltip-detail font-sans-regular">
169+
<span class="tooltip-detail-label font-sans-semibold">Latency</span>: {{ replica.metrics.latency }}ms
170+
</span>
171+
{% endif %}
172+
173+
{% if replica.metrics.rabbitmq %}
174+
<span class="tooltip-detail font-sans-regular">
175+
<span class="tooltip-detail-label font-sans-semibold">Queue</span>: {{ replica.metrics.rabbitmq.queue_ready }}R {{ replica.metrics.rabbitmq.queue_nack }}N
176+
</span>
177+
{% endif %}
178+
</span>
179+
{% endif %}
152180
</span>
153181
</span>
154182
</span>

src/prober/manager.rs

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ use config::regex::Regex;
2020
use prober::manager::STORE as PROBER_STORE;
2121
use prober::mode::Mode;
2222
use super::states::{ServiceStates, ServiceStatesProbe, ServiceStatesProbeNode,
23-
ServiceStatesProbeNodeReplica};
23+
ServiceStatesProbeNodeReplica, ServiceStatesProbeNodeReplicaMetrics,
24+
ServiceStatesProbeNodeReplicaMetricsRabbitMQ};
2425
use super::replica::ReplicaURL;
2526
use super::status::Status;
2627
use APP_CONF;
@@ -105,9 +106,8 @@ fn map_poll_replicas() -> Vec<(String, String, String, ReplicaURL, Option<Regex>
105106
fn proceed_replica_probe_with_retry(
106107
replica_url: &ReplicaURL,
107108
body_match: &Option<Regex>,
108-
) -> Status {
109-
let mut status = Status::Dead;
110-
let mut retry_count = 0;
109+
) -> (Status, Option<Duration>) {
110+
let (mut status, mut latency, mut retry_count) = (Status::Dead, None, 0);
111111

112112
while retry_count < APP_CONF.metrics.poll_retry && status == Status::Dead {
113113
retry_count += 1;
@@ -120,13 +120,19 @@ fn proceed_replica_probe_with_retry(
120120

121121
thread::sleep(Duration::from_millis(PROBE_HOLD_MILLISECONDS));
122122

123-
status = proceed_replica_probe(replica_url, body_match);
123+
let probe_results = proceed_replica_probe(replica_url, body_match);
124+
125+
status = probe_results.0;
126+
latency = Some(probe_results.1);
124127
}
125128

126-
status
129+
(status, latency)
127130
}
128131

129-
fn proceed_replica_probe(replica_url: &ReplicaURL, body_match: &Option<Regex>) -> Status {
132+
fn proceed_replica_probe(
133+
replica_url: &ReplicaURL,
134+
body_match: &Option<Regex>,
135+
) -> (Status, Duration) {
130136
let start_time = SystemTime::now();
131137

132138
let is_up = match replica_url {
@@ -135,17 +141,19 @@ fn proceed_replica_probe(replica_url: &ReplicaURL, body_match: &Option<Regex>) -
135141
&ReplicaURL::HTTPS(ref url) => proceed_replica_probe_http(url, body_match),
136142
};
137143

144+
let duration_latency = SystemTime::now().duration_since(start_time).unwrap_or(
145+
Duration::from_secs(0),
146+
);
147+
138148
if is_up == true {
139149
// Probe reports as sick?
140-
if let Ok(duration_since) = SystemTime::now().duration_since(start_time) {
141-
if duration_since >= Duration::from_secs(APP_CONF.metrics.poll_delay_sick) {
142-
return Status::Sick;
143-
}
150+
if duration_latency >= Duration::from_secs(APP_CONF.metrics.poll_delay_sick) {
151+
return (Status::Sick, duration_latency);
144152
}
145153

146-
Status::Healthy
154+
(Status::Healthy, duration_latency)
147155
} else {
148-
Status::Dead
156+
(Status::Dead, duration_latency)
149157
}
150158
}
151159

@@ -233,7 +241,10 @@ fn proceed_replica_probe_http(url: &str, body_match: &Option<Regex>) -> bool {
233241
false
234242
}
235243

236-
fn proceed_rabbitmq_queue_probe(rabbitmq: &ConfigPluginsRabbitMQ, rabbitmq_queue: &str) -> bool {
244+
fn proceed_rabbitmq_queue_probe(
245+
rabbitmq: &ConfigPluginsRabbitMQ,
246+
rabbitmq_queue: &str,
247+
) -> (bool, Option<(u32, u32)>) {
237248
let url_queue = rabbitmq.api_url.join(&format!(
238249
"/api/queues/{}/{}",
239250
rabbitmq.virtualhost,
@@ -268,6 +279,11 @@ fn proceed_rabbitmq_queue_probe(rabbitmq: &ConfigPluginsRabbitMQ, rabbitmq_queue
268279
// Check JSON result?
269280
if status == StatusCode::OK {
270281
if let Ok(response_json) = response_inner.json::<RabbitMQAPIQueueResponse>() {
282+
let queue_counts = Some((
283+
response_json.messages_ready,
284+
response_json.messages_unacknowledged,
285+
));
286+
271287
// Queue full?
272288
if response_json.messages_ready >= rabbitmq.queue_ready_healthy_below ||
273289
response_json.messages_unacknowledged >= rabbitmq.queue_nack_healthy_below
@@ -279,8 +295,10 @@ fn proceed_rabbitmq_queue_probe(rabbitmq: &ConfigPluginsRabbitMQ, rabbitmq_queue
279295
response_json.messages_unacknowledged
280296
);
281297

282-
return true;
298+
return (true, queue_counts);
283299
}
300+
301+
return (false, queue_counts);
284302
}
285303
} else {
286304
warn!(
@@ -293,13 +311,14 @@ fn proceed_rabbitmq_queue_probe(rabbitmq: &ConfigPluginsRabbitMQ, rabbitmq_queue
293311
}
294312
}
295313

296-
false
314+
(false, None)
297315
}
298316

299317
fn dispatch_polls() {
300318
// Probe hosts
301319
for probe_replica in map_poll_replicas() {
302-
let replica_status = proceed_replica_probe_with_retry(&probe_replica.3, &probe_replica.4);
320+
let (replica_status, replica_latency) =
321+
proceed_replica_probe_with_retry(&probe_replica.3, &probe_replica.4);
303322

304323
debug!(
305324
"replica probe result: {}:{}:{} => {:?}",
@@ -317,6 +336,9 @@ fn dispatch_polls() {
317336
if let Some(ref mut node) = probe.nodes.get_mut(&probe_replica.1) {
318337
if let Some(ref mut replica) = node.replicas.get_mut(&probe_replica.2) {
319338
replica.status = replica_status;
339+
340+
replica.metrics.latency =
341+
replica_latency.map(|duration| duration.as_millis() as u64);
320342
}
321343
}
322344
}
@@ -331,12 +353,12 @@ fn dispatch_plugins_rabbitmq(probe_id: String, node_id: String, queue: Option<St
331353
// Any queue for node?
332354
if let Some(ref queue_value) = queue {
333355
// Check if RabbitMQ queue is loaded
334-
let mut rabbitmq_queue_loaded = proceed_rabbitmq_queue_probe(rabbitmq, queue_value);
356+
let mut rabbitmq_queue_load = proceed_rabbitmq_queue_probe(rabbitmq, queue_value);
335357

336358
// Check once again? (the queue can be seen as loaded from check #1, but if we \
337359
// check again a few milliseconds later, it will actually be empty; so not loaded)
338360
// Notice: this prevents false-positive 'sick' statuses.
339-
if rabbitmq_queue_loaded == true {
361+
if rabbitmq_queue_load.0 == true {
340362
if let Some(retry_delay) = rabbitmq.queue_loaded_retry_delay {
341363
debug!(
342364
"rabbitmq queue is loaded, checking once again in {}ms: {}:{} [{}]",
@@ -349,7 +371,7 @@ fn dispatch_plugins_rabbitmq(probe_id: String, node_id: String, queue: Option<St
349371
thread::sleep(Duration::from_millis(retry_delay));
350372

351373
// Check again if RabbitMQ queue is loaded
352-
rabbitmq_queue_loaded = proceed_rabbitmq_queue_probe(rabbitmq, queue_value);
374+
rabbitmq_queue_load = proceed_rabbitmq_queue_probe(rabbitmq, queue_value);
353375
}
354376
}
355377

@@ -358,7 +380,7 @@ fn dispatch_plugins_rabbitmq(probe_id: String, node_id: String, queue: Option<St
358380
&probe_id,
359381
&node_id,
360382
queue_value,
361-
rabbitmq_queue_loaded
383+
rabbitmq_queue_load.0
362384
);
363385

364386
// Update replica status (write-lock the store)
@@ -368,9 +390,19 @@ fn dispatch_plugins_rabbitmq(probe_id: String, node_id: String, queue: Option<St
368390
if let Some(ref mut probe) = store.states.probes.get_mut(&probe_id) {
369391
if let Some(ref mut node) = probe.nodes.get_mut(&node_id) {
370392
for (_, replica) in node.replicas.iter_mut() {
371-
// Only alter healthy replicas
372393
if let Some(ref mut replica_load) = replica.load {
373-
replica_load.queue = rabbitmq_queue_loaded;
394+
replica_load.queue = rabbitmq_queue_load.0;
395+
}
396+
397+
// Store RabbitMQ metrics
398+
if let Some((queue_ready, queue_nack)) = rabbitmq_queue_load.1 {
399+
replica.metrics.rabbitmq =
400+
Some(ServiceStatesProbeNodeReplicaMetricsRabbitMQ {
401+
queue_ready: queue_ready,
402+
queue_nack: queue_nack,
403+
});
404+
} else {
405+
replica.metrics.rabbitmq = None
374406
}
375407
}
376408
}
@@ -440,6 +472,7 @@ pub fn initialize_store() {
440472
ServiceStatesProbeNodeReplica {
441473
status: Status::Healthy,
442474
url: Some(replica_url),
475+
metrics: ServiceStatesProbeNodeReplicaMetrics::default(),
443476
load: None,
444477
report: None,
445478
},

src/prober/report.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66

77
use std::time::{SystemTime, Duration};
88

9-
use super::states::{ServiceStatesProbeNodeReplica, ServiceStatesProbeNodeReplicaLoad,
9+
use super::states::{ServiceStatesProbeNodeReplica, ServiceStatesProbeNodeReplicaMetrics,
10+
ServiceStatesProbeNodeReplicaMetricsSystem, ServiceStatesProbeNodeReplicaLoad,
1011
ServiceStatesProbeNodeReplicaReport};
1112
use prober::manager::STORE as PROBER_STORE;
1213
use prober::status::Status;
@@ -42,28 +43,36 @@ pub fn handle(
4243
return Err(HandleError::WrongMode);
4344
}
4445

45-
// Acquire previous replica status (follow it up to avoid re-processing glitches)
46-
let status = if let Some(ref replica) = node.replicas.get(replica_id) {
47-
replica.status.to_owned()
48-
} else {
49-
Status::Healthy
50-
};
46+
// Acquire previous replica status + previous queue load status (follow-up values)
47+
let (mut status, mut metrics, mut load_queue);
5148

52-
// Acquire previous queue load status
53-
let mut load_queue = false;
49+
load_queue = false;
5450

5551
if let Some(ref replica) = node.replicas.get(replica_id) {
52+
status = replica.status.to_owned();
53+
metrics = replica.metrics.to_owned();
54+
5655
if let Some(ref replica_load) = replica.load {
5756
load_queue = replica_load.queue;
5857
}
58+
} else {
59+
status = Status::Healthy;
60+
metrics = ServiceStatesProbeNodeReplicaMetrics::default();
5961
}
6062

63+
// Assign new system metrics
64+
metrics.system = Some(ServiceStatesProbeNodeReplicaMetricsSystem {
65+
cpu: (load_cpu * 100.0).round() as u16,
66+
ram: (load_ram * 100.0).round() as u16,
67+
});
68+
6169
// Bump stored replica
6270
node.replicas.insert(
6371
replica_id.to_string(),
6472
ServiceStatesProbeNodeReplica {
6573
status: status,
6674
url: None,
75+
metrics: metrics,
6776
load: Some(ServiceStatesProbeNodeReplicaLoad {
6877
cpu: load_cpu,
6978
ram: load_ram,

0 commit comments

Comments
 (0)