mirror of
https://github.com/zaphar/Heracles.git
synced 2025-07-26 22:09:50 -04:00
Compare commits
4 Commits
a7a6c99099
...
e44d2087c8
Author | SHA1 | Date | |
---|---|---|---|
e44d2087c8 | |||
c830d1530d | |||
6a503947d8 | |||
01cd54afa4 |
@ -96,6 +96,10 @@ async fn main() -> anyhow::Result<()> {
|
||||
"/embed/dash/:dash_idx/graph/:graph_idx",
|
||||
get(routes::graph_embed).with_state(State(config.clone())),
|
||||
)
|
||||
.route(
|
||||
"/embed/dash/:dash_idx/log/:graph_idx",
|
||||
get(routes::log_embed).with_state(State(config.clone())),
|
||||
)
|
||||
.route("/dash/:dash_idx", get(routes::dashboard_direct))
|
||||
.route("/", get(routes::index).with_state(State(config.clone())))
|
||||
.layer(TraceLayer::new_for_http())
|
||||
|
@ -163,6 +163,21 @@ pub async fn graph_ui(
|
||||
graph_component(dash_idx, graph_idx, graph)
|
||||
}
|
||||
|
||||
pub async fn log_ui(
|
||||
State(config): State<Config>,
|
||||
Path((dash_idx, log_idx)): Path<(usize, usize)>,
|
||||
) -> Markup {
|
||||
let log = config
|
||||
.get(dash_idx)
|
||||
.expect(&format!("No such dashboard {}", dash_idx))
|
||||
.logs
|
||||
.as_ref()
|
||||
.expect("No graphs in this dashboard")
|
||||
.get(log_idx)
|
||||
.expect("No such graph");
|
||||
log_component(dash_idx, log_idx, log)
|
||||
}
|
||||
|
||||
pub async fn dash_ui(State(config): State<Config>, Path(dash_idx): Path<usize>) -> Markup {
|
||||
// TODO(zaphar): Should do better http error reporting here.
|
||||
dash_elements(config, dash_idx)
|
||||
@ -241,6 +256,23 @@ pub async fn graph_embed(
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn log_embed(
|
||||
State(config): State<Config>,
|
||||
Path((dash_idx, log_idx)): Path<(usize, usize)>,
|
||||
) -> Markup {
|
||||
html! {
|
||||
html {
|
||||
head {
|
||||
title { ("Heracles - Prometheus Unshackled") }
|
||||
}
|
||||
body {
|
||||
(graph_lib_prelude())
|
||||
(log_ui(State(config.clone()), Path((dash_idx, log_idx))).await)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn index_html(config: Config, dash_idx: Option<usize>) -> Markup {
|
||||
html! {
|
||||
html {
|
||||
|
@ -36,6 +36,7 @@
|
||||
* @property {{color: string}=} fill
|
||||
* @property {{width: number, color: string}=} line
|
||||
* @property {{family: string, size: number, color: string }=} font
|
||||
* @property {Array<number>=} columnwidth
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -68,6 +69,41 @@
|
||||
* @type {(TableTrace|GraphTrace)}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Map ansi terminal codes to html color codes.
|
||||
* @param {string} line
|
||||
*/
|
||||
function ansiToHtml(line) {
|
||||
const ansiToHtmlMap = {
|
||||
// Map ANSI color codes to HTML color names or hex values
|
||||
// We don't necessarily handle all the colors but this is enough to start.
|
||||
"30": "black",
|
||||
"31": "red",
|
||||
"32": "green",
|
||||
"33": "yellow",
|
||||
"34": "blue",
|
||||
"35": "magenta",
|
||||
"36": "cyan",
|
||||
"37": "white",
|
||||
"39": "initial"
|
||||
};
|
||||
|
||||
// NOTE(zaphar): Yes this is gross and I should really do a better parser but I'm lazy.
|
||||
// Replace ANSI codes with HTML span elements styled with the corresponding color
|
||||
return line.replace(/\x1b\[([0-9;]*)m/g, (match, p1) => {
|
||||
const parts = p1.split(';'); // ANSI codes can be compounded, e.g., "1;31" for bold red
|
||||
let styles = '';
|
||||
for (let part of parts) {
|
||||
if (ansiToHtmlMap[part]) {
|
||||
// If the code is a color, map it to a CSS color
|
||||
styles += `color: ${ansiToHtmlMap[part]};`;
|
||||
}
|
||||
// TODO(zaphar): Add more conditions here to handle other styles like bold or underline?
|
||||
}
|
||||
return styles ? `<span style="${styles}">` : '</span>';
|
||||
}) + '</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get's a css variable's value from the document.
|
||||
* @param {string} variableName - Name of the variable to get `--var-name`
|
||||
@ -395,7 +431,7 @@ export class GraphPlot extends HTMLElement {
|
||||
var layout = {
|
||||
displayModeBar: false,
|
||||
responsive: true,
|
||||
plot_bgcolor: getCssVariableValue('--paper-background-color').trim(),
|
||||
plot_bgcolor: getCssVariableValue('--plot-background-color').trim(),
|
||||
paper_bgcolor: getCssVariableValue('--paper-background-color').trim(),
|
||||
font: {
|
||||
color: getCssVariableValue('--text-color').trim()
|
||||
@ -480,41 +516,50 @@ export class GraphPlot extends HTMLElement {
|
||||
traces.push(trace);
|
||||
}
|
||||
} else if (subplot.Stream) {
|
||||
// TODO(zaphar): subplot.Stream // log lines!!!
|
||||
// TODO(jwall): It's possible that this should actually be a separate custom
|
||||
// element.
|
||||
const trace = /** @type TableTrace */({
|
||||
type: "table",
|
||||
// TODO(zaphar): Column width?
|
||||
columnwidth: [15, 20, 70],
|
||||
headers: {
|
||||
align: "left",
|
||||
values: ["Timestamp", "Log"],
|
||||
values: ["Timestamp","Label", "Log"],
|
||||
fill: { color: layout.xaxis.gridColor }
|
||||
},
|
||||
cells: {
|
||||
align: "left",
|
||||
values: [],
|
||||
fill: { color: layout.paper_bgcolor }
|
||||
fill: { color: layout.plot_bgcolor }
|
||||
},
|
||||
});
|
||||
const dateColumn = [];
|
||||
const metaColumn = [];
|
||||
const logColumn = [];
|
||||
|
||||
loopStream: for (const pair of subplot.Stream) {
|
||||
const labels = pair[0];
|
||||
var labelList = [];
|
||||
for (var label in labels) {
|
||||
var show = this.#filteredLabelSets[label];
|
||||
if (show && !show.includes(labels[label])) {
|
||||
continue loopStream;
|
||||
}
|
||||
labelList.push(`${label}:${labels[label]}`);
|
||||
}
|
||||
const labelsName = labelList.join("<br>");
|
||||
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);
|
||||
// TODO(zaphar): We should improve the timstamp formatting a bit
|
||||
let timestamp = new Date(line.timestamp / 1000000);
|
||||
dateColumn.push(timestamp.toISOString());
|
||||
metaColumn.push(labelsName);
|
||||
logColumn.push(ansiToHtml(line.line));
|
||||
}
|
||||
}
|
||||
trace.cells.values.push(dateColumn);
|
||||
trace.cells.values.push(metaColumn);
|
||||
trace.cells.values.push(logColumn);
|
||||
traces.push(trace);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
--background-color: #FFFFFF; /* Light background */
|
||||
--text-color: #333333; /* Dark text for contrast */
|
||||
--paper-background-color: #F0F0F0;
|
||||
--plot-background-color: #F0F0F0;
|
||||
--accent-color: #6200EE; /* For buttons and interactive elements */
|
||||
|
||||
/* Graph colors */
|
||||
@ -27,6 +28,7 @@
|
||||
/* Solarized Dark Base Colors */
|
||||
--background-color: #002b36; /* base03 */
|
||||
--paper-background-color: #003c4a;
|
||||
--plot-background-color: rgb(24, 34, 21);
|
||||
--text-color: #839496; /* base0 */
|
||||
--accent-color: #268bd2; /* blue */
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user