Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(server): add basic Prometheus console #1537

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/build/
node_modules/
/src/server_manager/messages/
/src/shadowbox/prometheus/consoles/
1 change: 1 addition & 0 deletions package-lock.json

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

2 changes: 2 additions & 0 deletions src/shadowbox/Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ tasks:
- cp '{{joinPath .TASKFILE_DIR "package.json"}}' '{{.TARGET_DIR}}'
# Build Node.js app
- npx webpack --config='{{joinPath .TASKFILE_DIR "webpack.config.js"}}' --output-path='{{.NODE_DIR}}' ${BUILD_ENV:+--mode="${BUILD_ENV}"}
# Copy Prometheus Console files.
- cp -r '{{joinPath .TASKFILE_DIR "prometheus"}}' '{{.TARGET_DIR}}'
# Copy third_party dependencies
- task: ':third_party:prometheus:copy-{{.TARGET_OS}}-{{.GOARCH}}'
vars: {TARGET_DIR: '{{.BIN_DIR}}'}
Expand Down
53 changes: 53 additions & 0 deletions src/shadowbox/prometheus/console_libraries/menu.lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{{/* vim: set ft=html: */}}

{{/* Navbar, should be passed . */}}
{{ define "navbar" }}
<nav class="navbar fixed-top navbar-expand-sm navbar-dark bg-dark">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="{{ pathPrefix }}/">Prometheus</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="nav-item"><a class="nav-link" href="{{ pathPrefix }}/consoles/index.html">Consoles</a></li>
<li class="nav-item"><a class="nav-link" href="{{ pathPrefix }}/alerts">Alerts</a></li>
<li class="nav-item"><a class="nav-link" href="{{ pathPrefix }}/graph">Graph</a></li>
</ul>
</div>
</div>
</nav>
{{ end }}

{{/* LHS menu, should be passed . */}}
{{ define "menu" }}
<div class="prom_lhs_menu row">
<nav class="col-md-2 md-block bg-dark sidebar prom_lhs_menu_nav">
<div class="sidebar-sticky">
<ul class="nav flex-column">

{{ template "_menuItem" (args . "index.html" "Overview") }}

{{ if query "up{job='outline-server-ss'}" }}
{{ template "_menuItem" (args . "outline.html" "Outline") }}
{{ if match "^outline" .Path }}
{{ if .Params.instance }}
<ul>
<li {{ if eq .Path "outline-overview.html" }}class="prom_lhs_menu_selected nav-item"{{ end }}>
<a class="nav-link" href="outline-overview.html?instance={{ .Params.instance }}">{{.Params.instance }}</a>
</li>
</ul>
{{ end }}
{{ end }}
{{ end }}

</ul>
</div>
</nav>
</div>
{{ end }}

{{/* Helper, pass (args . path name) */}}
{{ define "_menuItem" }}
<li {{ if eq .arg0.Path .arg1 }} class="prom_lhs_menu_selected nav-item" {{ end }}><a class="nav-link" href="{{ .arg1 }}">{{ .arg2 }}</a></li>
{{ end }}

138 changes: 138 additions & 0 deletions src/shadowbox/prometheus/console_libraries/prom.lib
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
{{/* vim: set ft=html: */}}
{{/* Load Prometheus console library JS/CSS. Should go in <head> */}}
{{ define "prom_console_head" }}
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/vendor/rickshaw/rickshaw.min.css">
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/vendor/bootstrap-4.5.2/css/bootstrap.min.css">
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/css/prom_console.css">
<link type="text/css" rel="stylesheet" href="{{ pathPrefix }}/classic/static/vendor/bootstrap4-glyphicons/css/bootstrap-glyphicons.min.css">
<script src="{{ pathPrefix }}/classic/static/vendor/rickshaw/vendor/d3.v3.js"></script>
<script src="{{ pathPrefix }}/classic/static/vendor/rickshaw/vendor/d3.layout.min.js"></script>
<script src="{{ pathPrefix }}/classic/static/vendor/rickshaw/rickshaw.min.js"></script>
<script src="{{ pathPrefix }}/classic/static/vendor/js/jquery-3.5.1.min.js"></script>
<script src="{{ pathPrefix }}/classic/static/vendor/js/popper.min.js"></script>
<script src="{{ pathPrefix }}/classic/static/vendor/bootstrap-4.5.2/js/bootstrap.min.js"></script>

<script>
var PATH_PREFIX = "{{ pathPrefix }}";
</script>
<script src="{{ pathPrefix }}/classic/static/js/prom_console.js"></script>
{{ end }}

{{/* Top of all pages. */}}
{{ define "head" -}}
<!doctype html>
<html lang="en">
<head>
{{ template "prom_console_head" }}
</head>
<body>
{{ template "navbar" . }}

{{ template "menu" . }}
{{ end }}

{{ define "__prom_query_drilldown_noop" }}{{ . }}{{ end }}
{{ define "humanize" }}{{ humanize . }}{{ end }}
{{ define "humanizeNoSmallPrefix" }}{{ if and (lt . 1.0) (gt . -1.0) }}{{ printf "%.3g" . }}{{ else }}{{ humanize . }}{{ end }}{{ end }}
{{ define "humanize1024" }}{{ humanize1024 . }}{{ end }}
{{ define "humanizeDuration" }}{{ humanizeDuration . }}{{ end }}
{{ define "humanizePercentage" }}{{ humanizePercentage . }}{{ end }}
{{ define "humanizeTimestamp" }}{{ humanizeTimestamp . }}{{ end }}
{{ define "printf.1f" }}{{ printf "%.1f" . }}{{ end }}
{{ define "printf.3g" }}{{ printf "%.3g" . }}{{ end }}

{{/* prom_query_drilldown (args expr suffix? renderTemplate?)
Displays the result of the expression, with a link to /graph for it.

renderTemplate is the name of the template to use to render the value.
*/}}
{{ define "prom_query_drilldown" }}
{{ $expr := .arg0 }}{{ $suffix := (or .arg1 "") }}{{ $renderTemplate := (or .arg2 "__prom_query_drilldown_noop") }}
<a class="prom_query_drilldown" href="{{ pathPrefix }}{{ graphLink $expr }}">{{ with query $expr }}{{tmpl $renderTemplate ( . | first | value )}}{{ $suffix }}{{ else }}-{{ end }}</a>
{{ end }}

{{ define "prom_path" }}/consoles/{{ .Path }}?{{ range $param, $value := .Params }}{{ $param }}={{ $value }}&amp;{{ end }}{{ end }}"

{{ define "prom_right_table_head" }}
<div class="prom_console_rhs">
<table class="table table-bordered table-hover table-sm">
{{ end }}
{{ define "prom_right_table_tail" }}
</table>
</div>
{{ end }}

{{/* RHS table head, pass job name. Should be used after prom_right_table_head. */}}
{{ define "prom_right_table_job_head" }}
<tr>
<th>{{ . }}</th>
<th>{{ template "prom_query_drilldown" (args (printf "sum(up{job='%s'})" .)) }} / {{ template "prom_query_drilldown" (args (printf "count(up{job='%s'})" .)) }}</th>
</tr>
<tr>
<td>CPU</td>
<td>{{ template "prom_query_drilldown" (args (printf "avg by(job)(irate(process_cpu_seconds_total{job='%s'}[5m]))" .) "s/s" "humanizeNoSmallPrefix") }}</td>
</tr>
<tr>
<td>Memory</td>
<td>{{ template "prom_query_drilldown" (args (printf "avg by(job)(process_resident_memory_bytes{job='%s'})" .) "B" "humanize1024") }}</td>
</tr>
{{ end }}


{{ define "prom_content_head" }}
<div class="prom_console_content">
<div class="container-fluid">
{{ template "prom_graph_timecontrol" . }}
{{ end }}
{{ define "prom_content_tail" }}
</div>
</div>
{{ end }}

{{ define "prom_graph_timecontrol" }}
<div class="prom_graph_timecontrol">
<div class="prom_graph_timecontrol_inner">
<div class="prom_graph_timecontrol_group ">
<button class="btn btn-light pull-left" type="button" id="prom_graph_duration_shrink" title="Shrink the time range.">
<i class="glyphicon glyphicon-minus"></i>
</button><!-- Comments between elements to remove spaces
--><input class="input pull-left align-middle" size="3" title="Time range of graph" type="text" id="prom_graph_duration"><!--
--><button class="btn btn-light pull-left" type="button" id="prom_graph_duration_grow" title="Grow the time range.">
<i class="glyphicon glyphicon-plus"></i>
</button>
</div>
<div class="prom_graph_timecontrol_group ">
<button class="btn btn-light pull-left" type="button" id="prom_graph_time_back" title="Rewind the end time.">
<i class="glyphicon glyphicon-backward"></i>
</button><!--
--><input class="input pull-left align-middle" title="End time of graph" placeholder="Until" type="text" id="prom_graph_time_end" size="16" value=""><!--
--><button class="btn btn-light pull-left" type="button" id="prom_graph_time_forward" title="Advance the end time.">
<i class="glyphicon glyphicon-forward"></i>
</button>
</div>
<div class="prom_graph_timecontrol_group ">
<div class="btn-group dropup prom_graph_timecontrol_refresh pull-left">
<button type="button" class="btn btn-light pull-left" id="prom_graph_refresh_button" title="Refresh.">
<i class="glyphicon glyphicon-repeat"></i>
<span class="icon-repeat"></span>
(<span id="prom_graph_refresh_button_value">Off</span>)
</button>
<button type="button" class="btn btn-light pull-left dropdown-toggle" data-toggle="dropdown" title="Set autorefresh."aria-haspopup="true" aria-expanded="false">
<span class="caret"></span>&nbsp;
</button>
<ul class="dropdown-menu" id="prom_graph_refresh_intervals" role="menu">
</ul>
</div>
</div>
</div>
<script>
new PromConsole.TimeControl();
</script>
</div>
{{ end }}

{{/* Bottom of all pages. */}}
{{ define "tail" }}
</body>
</html>
{{ end }}
5 changes: 5 additions & 0 deletions src/shadowbox/prometheus/consoles/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{{ template "head" . }} {{ template "prom_right_table_head" }} {{ template "prom_right_table_tail"
}} {{ template "prom_content_head" . }}
<h1>Overview</h1>
<p>These are standard consoles for Outline metrics.</p>
{{ template "prom_content_tail" . }} {{ template "tail" }}
91 changes: 91 additions & 0 deletions src/shadowbox/prometheus/consoles/outline-overview.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
{{ template "head" . }}

{{ template "prom_right_table_head" }}
<tr>
<th colspan="2">Overview</th>
</tr>
<tr>
<td>CPU</td>
<td>{{ template "prom_query_drilldown" (args (printf "irate(process_cpu_seconds_total{job='outline-server-ss',instance='%s'}[5m])" .Params.instance) "s/s" "humanizeNoSmallPrefix") }}</td>
</tr>
<tr>
<td>Memory</td>
<td>{{ template "prom_query_drilldown" (args (printf "process_resident_memory_bytes{job='outline-server-ss',instance='%s'}" .Params.instance) "B" "humanize1024") }}</td>
</tr>
<tr>
<td>Version</td>
<td>
<a href='https://github.com/Jigsaw-Code/outline-ss-server/releases/tag/v{{ with query (printf "shadowsocks_build_info{job='outline-server-ss',instance='%s'}" .Params.instance) }}{{. | first | label "version"}}{{end}}' target="_blank">
{{ with query (printf "shadowsocks_build_info{job='outline-server-ss',instance='%s'}" .Params.instance) }}{{. | first | label "version"}}{{end}}
</a>
</td>
</tr>

<tr>
<th colspan="2">Shadowsocks</th>
</tr>
<tr>
<td>Keys</td>
<td>{{ template "prom_query_drilldown" (args (printf "shadowsocks_keys{job='outline-server-ss',instance='%s'}" .Params.instance) "" "humanize") }}</td>
</tr>
<tr>
<td>Ports</td>
<td>{{ template "prom_query_drilldown" (args (printf "shadowsocks_ports{job='outline-server-ss',instance='%s'}" .Params.instance) "" "humanize") }}</td>
</tr>
<tr>
<td>Bytes transferred</td>
<td>{{ template "prom_query_drilldown" (args (printf "sum by (instance) (shadowsocks_data_bytes{job='outline-server-ss',instance='%s'})" .Params.instance) "B" "humanize1024") }}</td>
</tr>
{{ template "prom_right_table_tail" }}

{{ template "prom_content_head" . }}
<div class="prom_content_div">
<h1>Outline Overview - {{ .Params.instance }}</h1>

<h3>Data Bytes per Access Key</h3>
<div id="dataBytes"></div>
<script>
new PromConsole.Graph({
node: document.querySelector("#dataBytes"),
expr: "sum(increase(shadowsocks_data_bytes{job='outline-server-ss',instance='{{ .Params.instance }}'}[1d])) by (access_key)",
name: '[[access_key]]',
yAxisFormatter: PromConsole.NumberFormatter.humanize1024,
yHoverFormatter: PromConsole.NumberFormatter.humanize1024,
yTitle: "Bytes",
yUnits: "B",
})
</script>

<h3>TCP Connections by Location</h3>
<div id="tcpConnections"></div>
<script>
new PromConsole.Graph({
node: document.querySelector("#tcpConnections"),
expr: "increase(shadowsocks_tcp_connections_closed{job='outline-server-ss',instance='{{ .Params.instance }}'}[1d])",
name: '[[location]]',
yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
yTitle: "Connections",
yUnits: "",
colorScheme: "classic9",
})
</script>

<h3>UDP Packets by Location</h3>
<div id="tcpConnections"></div>
<script>
new PromConsole.Graph({
node: document.querySelector("#tcpConnections"),
expr: "increase(shadowsocks_udp_packets_from_client_per_location{job='outline-server-ss',instance='{{ .Params.instance }}'}[1d])",
name: '[[location]]',
yAxisFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
yHoverFormatter: PromConsole.NumberFormatter.humanizeNoSmallPrefix,
yTitle: "Connections",
yUnits: "",
colorScheme: "cool",
})
</script>
</div>
{{ template "prom_content_tail" . }}

{{ template "tail" }}
36 changes: 36 additions & 0 deletions src/shadowbox/prometheus/consoles/outline.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{{ template "head" . }}

{{ template "prom_right_table_head" }}
<tr>
<th>Outline Shadowsocks</th>
<th>{{ template "prom_query_drilldown" (args "sum(up{job='outline-server-ss'})") }} / {{ template "prom_query_drilldown" (args "count(up{job='outline-server-ss'})") }}</th>
</tr>
{{ template "prom_right_table_tail" }}

{{ template "prom_content_head" . }}
<h1>Outline</h1>

<table class="table table-sm table-striped table-bordered" style="width: 0%">
<tr>
<th>Shadowsocks</th>
<th>Up</th>
<th>Uptime</th>
<th>Memory</th>
</tr>
{{ range query "up{job='outline-server-ss'}" | sortByLabel "instance" }}
<tr>
<td><a href="outline-overview.html?instance={{ .Labels.instance }}">{{ .Labels.instance }}</a></td>
<td {{ if eq (. | value) 1.0 }}>Yes{{ else }} class="alert-danger">No{{ end }}</td>
<td class="text-right">
{{ template "prom_query_drilldown" (args (printf "round((time() - process_start_time_seconds{job='outline-server-ss',instance='%s'})/(60*60*24), 1)" .Labels.instance) " days" "humanizeNoSmallPrefix") }}
</td>
<td class="text-right">{{ template "prom_query_drilldown" (args (printf "process_resident_memory_bytes{job='outline-server-ss',instance='%s'}" .Labels.instance) "B" "humanize1024")}}</td>
</tr>
{{ else }}
<tr><td colspan=4>No jobs found.</td></tr>
{{ end }}
</table>

{{ template "prom_content_tail" . }}

{{ template "tail" }}
4 changes: 4 additions & 0 deletions src/shadowbox/server/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ async function main() {
prometheusTsdbFilename,
'--web.listen-address',
prometheusLocation,
'--web.console.libraries',
path.join(APP_BASE_DIR, 'prometheus', 'console_libraries'),
'--web.console.templates',
path.join(APP_BASE_DIR, 'prometheus', 'consoles'),
'--log.level',
verbose ? 'debug' : 'info',
];
Expand Down
Loading