Skip to content

Commit f410a67

Browse files
author
Thomas Luijken
committed
Added additional labels for targets
1 parent 0991e22 commit f410a67

5 files changed

Lines changed: 45 additions & 8 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ organisationX:
101101
polling_interval_seconds: 20
102102
targets:
103103
- url: http://www.example.com
104+
labels:
105+
environment: production
106+
region: west-eu
104107
```
105108
106109
Each top-level key (e.g., `demo`, `organisationX`) represents a distinct probe group. The configuration allows you to define:
@@ -110,6 +113,7 @@ Each top-level key (e.g., `demo`, `organisationX`) represents a distinct probe g
110113
* `targets`: List of endpoints to monitor.
111114
* `url`: The target URL.
112115
* `accepted_status_codes` (optional): A list of HTTP status codes considered successful.
116+
* `labels` (optional): A list of additional labels to send with the timeseries metrics.
113117

114118
---
115119

oxybox/example-config.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@ demo:
55
- url: https://www.google.com
66
- url: https://www.github.com
77
accepted_status_codes: [200, 301]
8-
- url: https://grafana.com/
8+
- url: https://expired.badssl.com
9+
accepted_status_codes: [200, 495, 526]
910

1011
organisationX:
11-
organisation_id: another-org
12+
organisation_id: 1
1213
polling_interval_seconds: 20
1314
targets:
1415
- url: http://www.example.com
16+
labels:
17+
environment: production
18+
hello: world

oxybox/src/config/probe_config.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
use serde::Deserialize;
1+
use serde::{Deserialize, Deserializer};
2+
use std::collections::HashMap;
3+
4+
fn deserialize_labels<'de, D>(deserializer: D) -> Result<Option<Vec<(String, String)>>, D::Error>
5+
where
6+
D: Deserializer<'de>,
7+
{
8+
let map = Option::<HashMap<String, String>>::deserialize(deserializer)?;
9+
Ok(map.map(|m| m.into_iter().collect()))
10+
}
211

312
/// An organisation configuration for the OxyBox service.
413
/// Contains the organisation ID, the polling interval in seconds, and a list of target configurations.
@@ -26,6 +35,12 @@ pub struct TargetConfig {
2635
/// Defaults to 200 if not specified.
2736
#[serde(default = "default_status_codes")]
2837
pub accepted_status_codes: Vec<u16>,
38+
39+
/// Additional labels to be included in the Mimir metrics for this target.
40+
/// This is an optional field that can contain a list of key-value pairs representing the labels.
41+
/// For example, you could include labels like `env: production` or `region: us-east-1` to provide more context about the target service in the metrics.
42+
#[serde(default, deserialize_with = "deserialize_labels")]
43+
pub labels: Option<Vec<(String, String)>>,
2944
}
3045

3146
fn default_status_codes() -> Vec<u16> {

oxybox/src/http_probe/probe.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,17 +261,16 @@ pub async fn run_probe_loop(
261261
let target = target.clone();
262262
let tenant_name = tenant_name.clone();
263263
let org_id = org_config.organisation_id.clone();
264+
let labels = target.labels.clone();
264265
let mimir_endpoint = mimir_endpoint.clone();
265266
let client = client.clone();
266267

267268
handles.push(tokio::spawn(async move {
268269
// ✅ Permit is held until the task returns
269270
let _permit = semaphore.acquire_owned().await.expect("Semaphore closed");
270271

271-
// ✅ Jitter per task
272272
sleep(jitter(250)).await;
273273

274-
// ✅ Timeout wraps the whole probe work
275274
let result = tokio::time::timeout(probe_timeout, async {
276275
handle_target_probe(
277276
tenant_name,
@@ -282,6 +281,7 @@ pub async fn run_probe_loop(
282281
&resolver,
283282
&mimir_endpoint,
284283
max_org_width,
284+
labels,
285285
)
286286
.await
287287
})
@@ -332,6 +332,7 @@ fn to_fixed_width(input: &str, width: usize) -> String {
332332
/// * `resolver` - The DNS resolver for resolving hostnames.
333333
/// * `mimir_target` - The Mimir endpoint to send metrics to.
334334
/// * `max_width` - The maximum width for tenant name formatting in logs.
335+
/// * `labels` - Optional additional labels to include in the metrics.
335336
async fn handle_target_probe(
336337
tenant: String,
337338
org_id: &str,
@@ -341,6 +342,7 @@ async fn handle_target_probe(
341342
resolver: &TokioAsyncResolver,
342343
mimir_target: &str,
343344
max_width: usize,
345+
labels: Option<Vec<(String, String)>>,
344346
) {
345347
let url = &target.url;
346348
let result = probe_url(client.clone(), tls_connector, resolver, url).await;
@@ -351,6 +353,12 @@ async fn handle_target_probe(
351353
.as_secs_f64();
352354
let padded_tenant = to_fixed_width(&tenant, max_width);
353355

356+
let labels = labels.as_ref().map(|l| {
357+
l.iter()
358+
.map(|(k, v)| (k.as_str(), v.as_str()))
359+
.collect::<Vec<(&str, &str)>>()
360+
});
361+
354362
match result {
355363
Ok(probe) => {
356364
let accepted = probe
@@ -377,7 +385,12 @@ async fn handle_target_probe(
377385
);
378386
}
379387

380-
let metrics = create_probe_metrics(&probe, accepted);
388+
let metrics = create_probe_metrics(
389+
&probe,
390+
accepted,
391+
labels
392+
);
393+
381394
if let Err(e) = send_to_mimir(mimir_target, Some(org_id), metrics).await {
382395
log::error!("[{padded_tenant}] Failed to send metrics for {url}: {e}");
383396
}
@@ -397,7 +410,7 @@ async fn handle_target_probe(
397410
transfer_time: None,
398411
total_probe_time: 0.0,
399412
};
400-
let metrics = create_probe_metrics(&probe, false);
413+
let metrics = create_probe_metrics(&probe, false, labels);
401414
if let Err(e) = send_to_mimir(mimir_target, Some(org_id), metrics).await {
402415
log::error!("[{padded_tenant}] Failed to send error metrics for {url}: {e}");
403416
}

oxybox/src/mimir/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ fn create_time_series(
5858
pub fn create_probe_metrics(
5959
probe_result: &ProbeResult,
6060
probe_success: bool,
61+
labels: Option<Vec<(&str, &str)>>,
6162
) -> Vec<prompb::TimeSeries> {
6263
let mut metrics = Vec::new();
6364
let probe_successful = match probe_success {
@@ -69,7 +70,7 @@ pub fn create_probe_metrics(
6970
PROBE_SUCCESS_METRIC,
7071
&probe_result.url,
7172
probe_successful,
72-
None,
73+
labels,
7374
));
7475

7576
let phases = [

0 commit comments

Comments
 (0)