Skip to content

Commit 523016a

Browse files
committed
add scraping capabilities
1 parent 5134f65 commit 523016a

11 files changed

Lines changed: 2173 additions & 62 deletions

File tree

Cargo.lock

Lines changed: 406 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,24 @@ edition = "2024"
1212

1313
[workspace.dependencies]
1414
async-trait = "0.1"
15+
axum = "0.7"
1516
bytes = "1.0"
1617
chrono = "0.4"
18+
clap = { version = "4", features = ["derive", "env"] }
19+
config = "0.14"
1720
dashmap = "6.1.0"
1821
fail = "0.5"
1922
moka = { version = "0.12", features = ["future"] }
2023
opendata-common = { path = "./opendata-common" }
24+
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] }
2125
rstest = "0.19"
2226
serde = { version = "1.0", features = ["derive"] }
2327
serde_json = "1.0"
28+
serde_yaml = "0.9"
2429
slatedb = "0.9.1"
2530
thiserror = "2.0"
2631
tokio = { version = "1.0", features = ["full"] }
32+
tower-http = { version = "0.5", features = ["cors"] }
2733
tracing = "0.1"
2834
tracing-subscriber = "0.3"
29-
uuid = "1.14.0"
30-
axum = "0.7"
31-
tower-http = { version = "0.5", features = ["cors"] }
35+
uuid = "1.14.0"

open-tsdb/Cargo.toml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,25 @@ edition.workspace = true
55

66
[dependencies]
77
async-trait.workspace = true
8+
axum.workspace = true
89
bytes.workspace = true
910
chrono.workspace = true
11+
clap.workspace = true
12+
config.workspace = true
1013
dashmap.workspace = true
1114
moka.workspace = true
1215
opendata-common.workspace = true
16+
reqwest.workspace = true
17+
serde.workspace = true
18+
serde_json.workspace = true
19+
serde_yaml.workspace = true
1320
slatedb.workspace = true
21+
thiserror.workspace = true
1422
tokio.workspace = true
23+
tower-http.workspace = true
1524
tracing.workspace = true
1625
tracing-subscriber.workspace = true
17-
serde.workspace = true
18-
serde_json.workspace = true
19-
thiserror.workspace = true
2026
uuid.workspace = true
21-
axum.workspace = true
22-
tower-http.workspace = true
2327

2428
blake3 = "1.8.2"
2529
promql-parser = "0.6"
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Mock metrics server that exposes Prometheus-format metrics for testing.
4+
5+
Usage:
6+
python mock_metrics_server.py [--port PORT]
7+
8+
The server exposes metrics at /metrics endpoint.
9+
"""
10+
11+
import argparse
12+
import random
13+
import time
14+
from http.server import HTTPServer, BaseHTTPRequestHandler
15+
from threading import Thread
16+
17+
# Simulated metric values
18+
start_time = time.time()
19+
request_count = 0
20+
error_count = 0
21+
22+
23+
class MetricsHandler(BaseHTTPRequestHandler):
24+
def do_GET(self):
25+
global request_count, error_count
26+
27+
if self.path == "/metrics":
28+
request_count += 1
29+
# Randomly increment error count
30+
if random.random() < 0.1:
31+
error_count += 1
32+
33+
metrics = self._generate_metrics()
34+
self.send_response(200)
35+
self.send_header("Content-Type", "text/plain; version=0.0.4")
36+
self.end_headers()
37+
self.wfile.write(metrics.encode("utf-8"))
38+
elif self.path == "/health":
39+
self.send_response(200)
40+
self.send_header("Content-Type", "text/plain")
41+
self.end_headers()
42+
self.wfile.write(b"OK")
43+
else:
44+
self.send_response(404)
45+
self.end_headers()
46+
47+
def _generate_metrics(self):
48+
uptime = time.time() - start_time
49+
50+
# Generate some realistic-looking metrics
51+
metrics = []
52+
53+
# Counter: total requests
54+
metrics.append("# HELP mock_requests_total Total number of requests received")
55+
metrics.append("# TYPE mock_requests_total counter")
56+
metrics.append(f"mock_requests_total {request_count}")
57+
58+
# Counter: errors
59+
metrics.append("# HELP mock_errors_total Total number of errors")
60+
metrics.append("# TYPE mock_errors_total counter")
61+
metrics.append(f"mock_errors_total {error_count}")
62+
63+
# Gauge: uptime
64+
metrics.append("# HELP mock_uptime_seconds Time since server started")
65+
metrics.append("# TYPE mock_uptime_seconds gauge")
66+
metrics.append(f"mock_uptime_seconds {uptime:.2f}")
67+
68+
# Gauge: random CPU usage simulation
69+
metrics.append("# HELP mock_cpu_usage_percent Simulated CPU usage")
70+
metrics.append("# TYPE mock_cpu_usage_percent gauge")
71+
metrics.append(f"mock_cpu_usage_percent {random.uniform(10, 90):.2f}")
72+
73+
# Gauge: random memory usage simulation
74+
metrics.append("# HELP mock_memory_usage_bytes Simulated memory usage")
75+
metrics.append("# TYPE mock_memory_usage_bytes gauge")
76+
metrics.append(f"mock_memory_usage_bytes {random.randint(100000000, 500000000)}")
77+
78+
# Histogram: simulated request duration
79+
metrics.append("# HELP mock_request_duration_seconds Request duration histogram")
80+
metrics.append("# TYPE mock_request_duration_seconds histogram")
81+
# Simulate bucket counts
82+
le_buckets = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]
83+
cumulative = 0
84+
for le in le_buckets:
85+
cumulative += random.randint(0, 10)
86+
metrics.append(f'mock_request_duration_seconds_bucket{{le="{le}"}} {cumulative}')
87+
metrics.append(f'mock_request_duration_seconds_bucket{{le="+Inf"}} {cumulative + random.randint(0, 5)}')
88+
metrics.append(f"mock_request_duration_seconds_sum {random.uniform(0.1, 10.0):.4f}")
89+
metrics.append(f"mock_request_duration_seconds_count {cumulative}")
90+
91+
# Metrics with labels
92+
metrics.append("# HELP mock_http_requests_by_status HTTP requests by status code")
93+
metrics.append("# TYPE mock_http_requests_by_status counter")
94+
metrics.append(f'mock_http_requests_by_status{{status="200"}} {request_count - error_count}')
95+
metrics.append(f'mock_http_requests_by_status{{status="500"}} {error_count}')
96+
97+
# Multi-label metric
98+
metrics.append("# HELP mock_api_calls API calls by endpoint and method")
99+
metrics.append("# TYPE mock_api_calls counter")
100+
metrics.append(f'mock_api_calls{{endpoint="/users",method="GET"}} {random.randint(100, 1000)}')
101+
metrics.append(f'mock_api_calls{{endpoint="/users",method="POST"}} {random.randint(10, 100)}')
102+
metrics.append(f'mock_api_calls{{endpoint="/orders",method="GET"}} {random.randint(50, 500)}')
103+
metrics.append(f'mock_api_calls{{endpoint="/orders",method="POST"}} {random.randint(20, 200)}')
104+
105+
return "\n".join(metrics) + "\n"
106+
107+
def log_message(self, format, *args):
108+
# Suppress default logging, or customize it
109+
print(f"[{self.log_date_time_string()}] {args[0]}")
110+
111+
112+
def main():
113+
parser = argparse.ArgumentParser(description="Mock Prometheus metrics server")
114+
parser.add_argument("--port", type=int, default=8080, help="Port to listen on (default: 8080)")
115+
args = parser.parse_args()
116+
117+
server = HTTPServer(("0.0.0.0", args.port), MetricsHandler)
118+
print(f"Mock metrics server running on http://0.0.0.0:{args.port}")
119+
print(f"Metrics available at http://0.0.0.0:{args.port}/metrics")
120+
print("Press Ctrl+C to stop")
121+
122+
try:
123+
server.serve_forever()
124+
except KeyboardInterrupt:
125+
print("\nShutting down...")
126+
server.shutdown()
127+
128+
129+
if __name__ == "__main__":
130+
main()

open-tsdb/etc/prometheus.yaml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Prometheus configuration for testing with mock metrics server
2+
#
3+
# Usage:
4+
# 1. Start the mock server: python mock_metrics_server.py --port 8080
5+
# 2. Start Prometheus: prometheus --config.file=prometheus.yaml
6+
7+
global:
8+
scrape_interval: 15s
9+
evaluation_interval: 15s
10+
11+
scrape_configs:
12+
# Scrape the mock metrics server
13+
- job_name: "mock-metrics"
14+
static_configs:
15+
- targets: ["localhost:8080"]
16+
labels:
17+
env: "test"
18+
service: "mock"
19+
20+
# Optionally scrape Prometheus itself
21+
- job_name: "prometheus"
22+
static_configs:
23+
- targets: ["localhost:9090"]

open-tsdb/src/main.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ mod util;
1515

1616
use std::sync::Arc;
1717

18+
use clap::Parser;
1819
use opendata_common::storage::in_memory::InMemoryStorage;
1920

21+
use promql::config::{CliArgs, PrometheusConfig, load_config};
2022
use promql::server::{PromqlServer, ServerConfig};
2123
use storage::merge_operator::OpenTsdbMergeOperator;
2224
use tsdb::Tsdb;
@@ -26,6 +28,26 @@ async fn main() {
2628
// Initialize tracing
2729
tracing_subscriber::fmt::init();
2830

31+
// Parse CLI arguments
32+
let args = CliArgs::parse();
33+
34+
// Load Prometheus configuration if provided
35+
let prometheus_config = if let Some(config_path) = &args.config {
36+
match load_config(config_path) {
37+
Ok(config) => {
38+
tracing::info!("Loaded configuration from {}", config_path);
39+
config
40+
}
41+
Err(e) => {
42+
tracing::error!("Failed to load configuration: {}", e);
43+
std::process::exit(1);
44+
}
45+
}
46+
} else {
47+
tracing::info!("No configuration file provided, using defaults");
48+
PrometheusConfig::default()
49+
};
50+
2951
// Create in-memory storage with merge operator
3052
let storage = Arc::new(InMemoryStorage::with_merge_operator(Arc::new(
3153
OpenTsdbMergeOperator,
@@ -34,10 +56,18 @@ async fn main() {
3456
// Create Tsdb
3557
let tsdb = Arc::new(Tsdb::new(storage));
3658

37-
// Create and run server on default port (9090)
38-
let config = ServerConfig::default();
59+
// Create server configuration
60+
let config = ServerConfig {
61+
port: args.port,
62+
prometheus_config,
63+
};
64+
65+
// Create and run server
3966
let server = PromqlServer::new(tsdb, config);
4067

41-
println!("Starting open-tsdb Prometheus-compatible server on port 9090...");
68+
tracing::info!(
69+
"Starting open-tsdb Prometheus-compatible server on port {}...",
70+
args.port
71+
);
4272
server.run().await;
4373
}

0 commit comments

Comments
 (0)