mirror of
https://github.com/zaphar/Heracles.git
synced 2025-07-23 04:29:48 -04:00
ui: Show log results in our dashboards
This commit is contained in:
parent
17e6ae81a0
commit
f69ea6d6fa
@ -22,7 +22,7 @@ use tracing::{debug, error};
|
|||||||
use super::{LogLine, QueryResult, QueryType, TimeSpan};
|
use super::{LogLine, QueryResult, QueryType, TimeSpan};
|
||||||
|
|
||||||
// TODO(jwall): Should I allow non stream returns?
|
// TODO(jwall): Should I allow non stream returns?
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||||
pub enum ResultType {
|
pub enum ResultType {
|
||||||
/// Returned by query endpoints
|
/// Returned by query endpoints
|
||||||
#[serde(rename = "vector")]
|
#[serde(rename = "vector")]
|
||||||
@ -85,8 +85,10 @@ pub fn loki_to_sample(data: LokiData) -> QueryResult {
|
|||||||
}
|
}
|
||||||
QueryResult::StreamInstant(values)
|
QueryResult::StreamInstant(values)
|
||||||
}
|
}
|
||||||
|
// Stream types are nanoseconds. // Matrix types are seconds
|
||||||
ResultType::Matrix | ResultType::Streams => {
|
ResultType::Matrix | ResultType::Streams => {
|
||||||
let mut values = Vec::with_capacity(data.result.len());
|
let mut values = Vec::with_capacity(data.result.len());
|
||||||
|
let multiple = (if data.result_type == ResultType::Matrix { 1000000 } else { 1 }) as f64;
|
||||||
for result in data.result {
|
for result in data.result {
|
||||||
if let Some(value) = result.values {
|
if let Some(value) = result.values {
|
||||||
values.push((
|
values.push((
|
||||||
@ -94,7 +96,7 @@ pub fn loki_to_sample(data: LokiData) -> QueryResult {
|
|||||||
value
|
value
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(timestamp, line)| LogLine {
|
.map(|(timestamp, line)| LogLine {
|
||||||
timestamp: timestamp.parse::<f64>().expect("Invalid f64 type"),
|
timestamp: multiple * timestamp.parse::<f64>().expect("Invalid f64 type"),
|
||||||
line,
|
line,
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
@ -26,7 +26,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::dashboard::{
|
use crate::dashboard::{
|
||||||
loki_query_data, prom_query_data, AxisDefinition, Dashboard, Graph, GraphSpan, Orientation,
|
loki_query_data, prom_query_data, AxisDefinition, Dashboard, Graph, GraphSpan, Orientation, LogStream,
|
||||||
};
|
};
|
||||||
use crate::query::QueryResult;
|
use crate::query::QueryResult;
|
||||||
|
|
||||||
@ -120,6 +120,18 @@ pub fn mk_api_routes(config: Arc<Vec<Dashboard>>) -> Router<Config> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn log_component(dash_idx: usize, log_idx: usize, log: &LogStream) -> Markup {
|
||||||
|
let log_id = format!("log-{}-{}", dash_idx, log_idx);
|
||||||
|
let log_data_uri = format!("/api/dash/{}/log/{}", dash_idx, log_idx);
|
||||||
|
let log_embed_uri = format!("/embed/dash/{}/log/{}", dash_idx, log_idx);
|
||||||
|
html! {
|
||||||
|
div {
|
||||||
|
h2 { (log.title) " - " a href=(log_embed_uri) { "embed url" } }
|
||||||
|
graph-plot uri=(log_data_uri) id=(log_id) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn graph_component(dash_idx: usize, graph_idx: usize, graph: &Graph) -> Markup {
|
pub fn graph_component(dash_idx: usize, graph_idx: usize, graph: &Graph) -> Markup {
|
||||||
let graph_id = format!("graph-{}-{}", dash_idx, graph_idx);
|
let graph_id = format!("graph-{}-{}", dash_idx, graph_idx);
|
||||||
let graph_data_uri = format!("/api/dash/{}/graph/{}", dash_idx, graph_idx);
|
let graph_data_uri = format!("/api/dash/{}/graph/{}", dash_idx, graph_idx);
|
||||||
@ -160,19 +172,35 @@ fn dash_elements(config: State<Arc<Vec<Dashboard>>>, dash_idx: usize) -> maud::P
|
|||||||
let dash = config
|
let dash = config
|
||||||
.get(dash_idx)
|
.get(dash_idx)
|
||||||
.expect(&format!("No such dashboard {}", dash_idx));
|
.expect(&format!("No such dashboard {}", dash_idx));
|
||||||
let graph_iter = dash
|
let graph_components = if let Some(graphs) = dash
|
||||||
.graphs
|
.graphs
|
||||||
.as_ref()
|
.as_ref() {
|
||||||
.expect("No graphs in this dashboard")
|
let graph_iter = graphs.iter()
|
||||||
.iter()
|
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.collect::<Vec<(usize, &Graph)>>();
|
.collect::<Vec<(usize, &Graph)>>();
|
||||||
|
Some(html! {
|
||||||
|
@for (idx, graph) in &graph_iter {
|
||||||
|
(graph_component(dash_idx, *idx, *graph))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let log_components = if let Some(logs) = dash.logs.as_ref() {
|
||||||
|
let log_iter = logs.iter().enumerate().collect::<Vec<(usize, &LogStream)>>();
|
||||||
|
Some(html! {
|
||||||
|
@for (idx, log) in &log_iter {
|
||||||
|
(log_component(dash_idx, *idx, *log))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
html!(
|
html!(
|
||||||
h1 { (dash.title) }
|
h1 { (dash.title) }
|
||||||
span-selector class="row-flex" {}
|
span-selector class="row-flex" {}
|
||||||
@for (idx, graph) in &graph_iter {
|
@if graph_components.is_some() { (graph_components.unwrap()) }
|
||||||
(graph_component(dash_idx, *idx, *graph))
|
@if log_components.is_some() { (log_components.unwrap()) }
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
/**
|
/**
|
||||||
* @typedef PlotList
|
* @typedef PlotList
|
||||||
* @type {object}
|
* @type {object}
|
||||||
* @property {?Array} Series
|
* @property {Array=} Series
|
||||||
* @property {?Array} Scalar
|
* @property {Array=} Scalar
|
||||||
* @property {?Array} StreamInstant
|
* @property {Array<{timestamp: string, line: string}>=} StreamInstant - Timestamps are in seconds
|
||||||
* @property {?Array} Stream
|
* @property {Array<{timestamp: string, line: string}>=} Stream - Timestamps are in nanoseconds
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,16 +29,43 @@
|
|||||||
* @property {Array<PlotList>} plots
|
* @property {Array<PlotList>} plots
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef HeaderOrCell
|
||||||
|
* @type {object}
|
||||||
|
* @property {array} values
|
||||||
|
* @property {string=} fill
|
||||||
|
* @property {{width: number, color: string}=} line
|
||||||
|
* @property {{family: string, size: number, color: string }=} font
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef PlotTrace
|
* @typedef TableTrace
|
||||||
* @type {object}
|
* @type {object}
|
||||||
* @property {string=} name
|
* @property {string=} name
|
||||||
* @property type {string}
|
* @property type {string}
|
||||||
* @property {string=} mode
|
* @property {string=} mode
|
||||||
|
* @property {HeaderOrCell} headers
|
||||||
|
* @property {HeaderOrCell} cells - An Array of columns for the table.
|
||||||
|
* @property {string=} xaxis
|
||||||
|
* @property {string=} yaxis
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef GraphTrace
|
||||||
|
* @type {object}
|
||||||
|
* @property {string=} name
|
||||||
|
* @property {string=} fill
|
||||||
|
* @property type {string}
|
||||||
|
* @property {string=} mode
|
||||||
* @property {Array} x
|
* @property {Array} x
|
||||||
* @property {Array} y
|
* @property {Array} y
|
||||||
* @peroperty {string=} xaxis
|
* @property {string=} xaxis
|
||||||
* @peroperty {string=} yaxis
|
* @property {string=} yaxis
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef PlotTrace
|
||||||
|
* @type {(TableTrace|GraphTrace)}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -401,7 +428,7 @@ export class GraphPlot extends HTMLElement {
|
|||||||
var yaxis = meta.yaxis || "y";
|
var yaxis = meta.yaxis || "y";
|
||||||
// https://plotly.com/javascript/reference/layout/yaxis/
|
// https://plotly.com/javascript/reference/layout/yaxis/
|
||||||
const series = triple[2];
|
const series = triple[2];
|
||||||
const trace = {
|
const trace = /** @type GraphTrace */({
|
||||||
type: "scatter",
|
type: "scatter",
|
||||||
mode: "lines+text",
|
mode: "lines+text",
|
||||||
x: [],
|
x: [],
|
||||||
@ -410,7 +437,7 @@ export class GraphPlot extends HTMLElement {
|
|||||||
xaxis: "x",
|
xaxis: "x",
|
||||||
yaxis: yaxis,
|
yaxis: yaxis,
|
||||||
//yhoverformat: yaxis.tickformat,
|
//yhoverformat: yaxis.tickformat,
|
||||||
};
|
});
|
||||||
if (meta.fill) {
|
if (meta.fill) {
|
||||||
trace.fill = meta.fill;
|
trace.fill = meta.fill;
|
||||||
}
|
}
|
||||||
@ -434,7 +461,7 @@ export class GraphPlot extends HTMLElement {
|
|||||||
}
|
}
|
||||||
const meta = triple[1];
|
const meta = triple[1];
|
||||||
const series = triple[2];
|
const series = triple[2];
|
||||||
const trace = /** @type PlotTrace */({
|
const trace = /** @type GraphTrace */({
|
||||||
type: "bar",
|
type: "bar",
|
||||||
x: [],
|
x: [],
|
||||||
y: [],
|
y: [],
|
||||||
@ -446,7 +473,42 @@ export class GraphPlot extends HTMLElement {
|
|||||||
trace.x.push(trace.name);
|
trace.x.push(trace.name);
|
||||||
traces.push(trace);
|
traces.push(trace);
|
||||||
}
|
}
|
||||||
} // TODO(zaphar): subplot.Stream // log lines!!!
|
} else if (subplot.Stream) {
|
||||||
|
// TODO(zaphar): subplot.Stream // log lines!!!
|
||||||
|
const trace = /** @type TableTrace */({
|
||||||
|
type: "table",
|
||||||
|
headers: {
|
||||||
|
align: "left",
|
||||||
|
values: ["Timestamp", "Log"]
|
||||||
|
},
|
||||||
|
cells: {
|
||||||
|
align: "left",
|
||||||
|
values: []
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const dateColumn = [];
|
||||||
|
const logColumn = [];
|
||||||
|
|
||||||
|
loopStream: for (const pair of subplot.Stream) {
|
||||||
|
const labels = pair[0];
|
||||||
|
for (var label in labels) {
|
||||||
|
var show = this.#filteredLabelSets[label];
|
||||||
|
if (show && !show.includes(labels[label])) {
|
||||||
|
continue loopStream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const lines = pair[1];
|
||||||
|
// TODO(jwall): Headers
|
||||||
|
for (const line of lines) {
|
||||||
|
// For streams the timestamps are in nanoseconds
|
||||||
|
dateColumn.push(new Date(line.timestamp / 1000000));
|
||||||
|
logColumn.push(line.line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace.cells.values.push(dateColumn);
|
||||||
|
trace.cells.values.push(logColumn);
|
||||||
|
traces.push(trace);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// https://plotly.com/javascript/plotlyjs-function-reference/#plotlyreact
|
// https://plotly.com/javascript/plotlyjs-function-reference/#plotlyreact
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
Loading…
x
Reference in New Issue
Block a user