Skip to content

Commit

Permalink
server: update metrics and traffic page
Browse files Browse the repository at this point in the history
  • Loading branch information
fuxiaohei committed Mar 29, 2024
1 parent 64d5916 commit ea5d3c8
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 30 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ rust-embed = { version = "8.3.0", features = [
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.115"
time = { version = "0.3.34", features = ["local-offset"] }
tokio = { version = "1.36.0", features = ["full"] }
tokio = { version = "1.37.0", features = ["full"] }
toml = "0.8.12"
tower-http = { version = "0.5.2", features = [
"timeout",
Expand Down
26 changes: 26 additions & 0 deletions crates/kernel/src/prom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,32 @@ pub struct QueryResponseDataItem {
pub values: Vec<(i64, String)>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct LineSeries {
pub total: i64,
pub values: Vec<(i64, i64)>,
}

impl LineSeries {
pub fn from(res: &QueryResponse, sequence: Vec<i64>) -> Self {
let mut times_data = HashMap::<i64, i64>::new();
for item in res.data.result.iter() {
for (t, v) in &item.values {
let value = v.parse::<f64>().unwrap_or(0.0) as i64;
times_data.insert(*t, value);
}
}
let mut values = vec![];
let mut total: i64 = 0;
for t in sequence.iter() {
let value = times_data.get(t).unwrap_or(&0);
values.push((*t * 1000, *value)); // js use milliseconds, t*1000
total += *value;
}
LineSeries { total, values }
}
}

/// query_range queries range from Prometheus
pub async fn query_range(params: QueryRangeParams) -> Result<QueryResponse> {
let prom_env = PROM_ENV
Expand Down
24 changes: 17 additions & 7 deletions crates/worker-server/src/middleware.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::METRICS_ENABLED;

use super::{DEFAULT_WASM, ENDPOINT_NAME};
use crate::METRICS_ENABLED;
use axum::extract::Request;
use axum::http::StatusCode;
use axum::middleware::Next;
Expand Down Expand Up @@ -64,12 +63,23 @@ pub async fn middleware(mut request: Request, next: Next) -> Result<Response, St
("project_id", project_id.clone()),
("endpoint", endpoint.clone()),
];
let mut all_req_labels = labels.clone();
all_req_labels.push(("status", "all".to_string()));
let mut notfound_labels = labels.clone();
notfound_labels.push(("status", "notfound".to_string()));
let mut error_req_labels = labels.clone();
error_req_labels.push(("status", "error".to_string()));
let mut flow_in_labels = labels.clone();
flow_in_labels.push(("flowtype", "in".to_string()));
let mut flow_out_labels = labels.clone();
flow_out_labels.push(("flowtype", "out".to_string()));

(
counter!("req_fn_total", &labels),
counter!("req_fn_notfound_total", &labels),
counter!("req_fn_error_total", &labels),
counter!("req_fn_in_bytes_total", &labels),
counter!("req_fn_out_bytes_total", &labels),
counter!("req_fn_total", &all_req_labels),
counter!("req_fn_total", &notfound_labels),
counter!("req_fn_total", &error_req_labels),
counter!("req_fn_bytes_total", &flow_in_labels),
counter!("req_fn_bytes_total", &flow_out_labels),
)
} else {
(
Expand Down
69 changes: 55 additions & 14 deletions land-server/src/server/dash/traffic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::auth::SessionUser;
use crate::server::ServerError;
use axum::{extract::Query, response::IntoResponse, Extension, Json};
use serde::{Deserialize, Serialize};
use tracing::debug;
use tracing::{debug, info};

#[derive(Debug, Serialize, Deserialize)]
pub struct RequestsQuery {
Expand All @@ -11,18 +11,41 @@ pub struct RequestsQuery {
pub period: Option<String>, //only for daily or weekly
}

#[derive(Debug)]
struct PeriodParams {
start: i64,
end: i64,
step: i64,
step_word: String,
sequence: Vec<i64>, // unix timestamp from start to end with step
}

impl RequestsQuery {
pub fn get_period(&self) -> (i64, i64, i64, String) {
fn get_period(&self) -> PeriodParams {
if let Some(p) = self.period.as_ref() {
if p.eq("weekly") {
let end = chrono::Utc::now().timestamp() / 3600 * 3600;
let start = end - 604800; // 86400 * 7
return (start, end, 3600, "1h".to_string());
let sequence = (0..169).map(|i| start + i * 3600).collect();
return PeriodParams {
start,
end,
step: 3600,
step_word: "1h".to_string(),
sequence,
};
}
}
let end = chrono::Utc::now().timestamp() / 600 * 600;
let start = end - 86400;
(start, end, 600, "10m".to_string())
let start = end - 86400; // oneday 1440/10+1
let sequence = (0..145).map(|i| start + i * 600).collect();
PeriodParams {
start,
end,
step: 600,
step_word: "10m".to_string(),
sequence,
}
}
}

Expand All @@ -33,38 +56,56 @@ pub async fn flows(
Ok("flows".to_string())
}

#[derive(Debug, Serialize, Deserialize)]
pub struct RequestsValues {
pub total: i64,
pub time_series: Vec<i64>,
pub values: Vec<i64>,
}

/// requests is a handler for GET /traffic/requests
pub async fn requests(
Extension(user): Extension<SessionUser>,
Query(q): Query<RequestsQuery>,
) -> Result<impl IntoResponse, ServerError> {
let (start, end, step, step_word) = q.get_period();
let period = q.get_period();
let acc = q.account.unwrap_or_default();
if acc != user.id.to_string() {
return Err(ServerError::forbidden("user id does not match"));
}
let query = if let Some(pid) = q.project {
format!(
"increase(req_fn_total{{project_id=\"{}\"}}[{}])",
pid, step_word
pid, period.step_word
)
} else {
format!(
"sum(increase(req_fn_total{{user_id=\"{}\"}}[{}]))",
acc, step_word
acc, period.step_word
)
};
let now = tokio::time::Instant::now();
// end time is now ts with latest 10 decade
debug!(
"query: {}, start:{}, end:{}, step:{}",
query, start, end, step
query, period.start, period.end, period.step
);
let params = land_kernel::prom::QueryRangeParams {
query,
step,
start,
end,
query: query.clone(),
step: period.step,
start: period.start,
end: period.end,
};
let res = land_kernel::prom::query_range(params).await?;
Ok(Json(res).into_response())
let values = land_kernel::prom::LineSeries::from(&res, period.sequence);
info!(
"query: {}, start:{}, end:{}, step:{}, values:{}, cost:{}",
query,
period.start,
period.end,
period.step,
values.values.len(),
now.elapsed().as_millis(),
);
Ok(Json(values))
}
7 changes: 1 addition & 6 deletions land-server/tpls/overview.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,7 @@
</div>
</div>
</div>
<div class="container project-container py-4" id="traffic">
<div>
<h6 class="mb-0">Global Traffic</h6>
<p class="fs-6 text-body-tertiary mb-0">Aggregated all projects traffic in recently.</p>
</div>
</div>
{{> partials/traffic.hbs}}
{{> partials/footer.hbs}}
</body>

Expand Down
24 changes: 24 additions & 0 deletions land-server/tpls/partials/traffic.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<div class="container project-container py-4" id="traffic" data-x-account="{{user.id}}">
<div>
<h6 class="mb-0">Global Traffic</h6>
<p class="fs-6 text-body-tertiary mb-0">Aggregated all projects traffic in recently.</p>
</div>
<div class="row g-4 py-4">
<div class="col-6">
<div class="border rounded p-4">
<div>
<p class="text-body-tertiary text-uppercase mb-2">Request counts</p>
<p class="fw-bold mb-1" id="requests-total"></p>
<div id="requests-loading-spinner" class="spinner-grow spinner-grow-sm" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</div>
<div id="requests-chart" style="height: 200px;"></div>
</div>
</div>
<div class="col-6">222</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"
integrity="sha256-QvgynZibb2U53SsVu98NggJXYqwRL7tg3FeyfXvPOUY=" crossorigin="anonymous"></script>
<script src="/static/js/traffic.js"></script>
96 changes: 96 additions & 0 deletions land-server/tpls/static/js/traffic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@

(async () => {
let d = document.getElementById("requests-loading-spinner");
let acc = document.getElementById("traffic").getAttribute("data-x-account");
let resp = await fetch("/traffic/requests?account=" + acc)
if (!resp.ok) {
// TODO: show error message
return
}
// hide loading spinner
d.classList.add("d-none");
let series = await resp.json();
document.getElementById("requests-total").innerText = friendly_bytesize(series.total, false);
let chart = echarts.init(document.getElementById('requests-chart'));
let option = {
title: {
show: false,
},
tooltip: {
trigger: "axis",
formatter: function (params) {
let t = params[0].axisValueLabel;
let v = params[0].value[1];
return `<span>${t}<br/><span style="color:#AC3B2A">REQUESTS</span> <strong>${v}</strong></span>`
}
},
xAxis: {
type: 'time',
axisLabel: {
formatter: function (value, index) {
return unix2hour(value) // js use milliseconds
},
color: "#AAA",
},
splitNumber: 3,
},
yAxis: {
show: false
},
grid: {
top: 0,
left: 0,
right: 0,
bottom: 20,
},
series: [
{
type: 'line',
data: series.values || [],
smooth: true,
symbol: "none",
lineStyle: {
normal: {
color: "#AC3B2A",
width: 2,
}
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: '#AC3B2A'
},
{
offset: 1,
color: '#FFF'
}
])
},
}
]
};
chart.setOption(option);
})()

/// convert unixtimestamp to hour and minute, HH:MM
function unix2hour(v) {
const dateObj = new Date(v)
const hours = dateObj.getHours() >= 10 ? dateObj.getHours() : '0' + dateObj.getHours()
const minutes = dateObj.getMinutes() < 10 ? dateObj.getMinutes() + '0' : dateObj.getMinutes()
const UnixTimeToDate = hours + ':' + minutes
return UnixTimeToDate
}

function friendly_bytesize(v, is_bytes) {
let bytes_units = ['iB', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
let units = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
let i = 0;
while (v > 1000) {
v /= 1000;
i++;
}
v = v.toFixed(2);
let u = is_bytes ? bytes_units[i] : units[i];
return `${v}${u}`
}

0 comments on commit ea5d3c8

Please sign in to comment.