Compare commits
No commits in common. "gh-pages" and "main" have entirely different histories.
16 changed files with 417 additions and 827 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
_build/
|
||||||
|
.merlin
|
||||||
|
*.install
|
||||||
|
.*.swp
|
22
CHANGES.md
Normal file
22
CHANGES.md
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# v0.0.5 (2024-04-10)
|
||||||
|
|
||||||
|
* Update to mirage-runtime 4.5.0 API changes (Arg.log_threshold is now
|
||||||
|
Conv.log_threshold)
|
||||||
|
|
||||||
|
# v0.0.4 (2023-03-05)
|
||||||
|
|
||||||
|
* Remove mirage-solo5 dependency, now that mirage-solo5 and mirage-xen install
|
||||||
|
a memory metrics themselves
|
||||||
|
|
||||||
|
# v0.0.3 (2022-09-26)
|
||||||
|
|
||||||
|
* Upgrade to Mirage 4.3.0+
|
||||||
|
|
||||||
|
# v0.0.2 (2022-08-08)
|
||||||
|
|
||||||
|
* Upgrade to MirageOS4 (mirage-solo5 >= 0.7.0), drop usage of deprecated
|
||||||
|
OS.MM.malloc_metrics
|
||||||
|
|
||||||
|
# v0.0.1 (2022-05-22)
|
||||||
|
|
||||||
|
* Initial public release
|
51
README.md
Normal file
51
README.md
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# Grafana MirageOS Unikernel monitoring
|
||||||
|
|
||||||
|
Using Influx, Telegraf, etc.
|
||||||
|
|
||||||
|
![Monitoring](https://raw.githubusercontent.com/robur-coop/mirage-monitoring/main/one.png)
|
||||||
|
|
||||||
|
Best used on a private network interface for your unikernel.
|
||||||
|
|
||||||
|
# Output metrics to influx / telegraf
|
||||||
|
|
||||||
|
The provided `ip:port` in `create` is used to send every `interval` seconds
|
||||||
|
gathered metrics to, using the Influx wire protocol.
|
||||||
|
|
||||||
|
# Dynamic adjustments of Log level and Metrics reporting
|
||||||
|
|
||||||
|
The `create` function has a *listener_port* argument (defaulting to 2323). On the
|
||||||
|
customizable port TCP connections to the unikernel are possible. Each connection
|
||||||
|
can transmit a command (as text) to adjust log level and enable or disable
|
||||||
|
metrics sources:
|
||||||
|
|
||||||
|
The log level (prefix `L`) is specified, the same as the command-line argument `-l`:
|
||||||
|
- `L*:debug` all log sources are enabled on the *debug* level.
|
||||||
|
- `Lmonitoring-experiments:error` the log source monitoring-experiments is set to the *error* level.
|
||||||
|
- `L*:info,monitoring-experiments:debug` all log sources are enabled on the *info* level, and the log source monitoring-experiments is set to the *debug* level.
|
||||||
|
|
||||||
|
The metrics (prefix `M`) sources can be enabled and disabled based on source name.
|
||||||
|
First, if present, the all command is executed, then the tags, then specific sources:
|
||||||
|
- `M*:disable,memory:enable,net-solo5:enable` disables all metrics sources, and then enables *memory* and *net-solo5*.
|
||||||
|
- `Mnet-solo5:disable` disables the *net-solo5* metrics source.
|
||||||
|
- `Mtag:http:enable` enables all metrics with the *http* tag.
|
||||||
|
|
||||||
|
The log levels for the log sources can be inspected:
|
||||||
|
- `l` reports the default log level and the log level for all log sources with a different log level.
|
||||||
|
- `l*` reports the default log level and the log level for all log sources.
|
||||||
|
- `lmonitoring-experiments,dns` reports the log level for monitoring-experiments and dns respectively.
|
||||||
|
|
||||||
|
Likewise, metrics status can be similarly inspected:
|
||||||
|
- `m` reports the default metrics status and the metrics status for all metrics sources with a different status.
|
||||||
|
- `m*` reports the default metrics status, all enabled tags, and the metrics status of all metrics sources.
|
||||||
|
- `mmemory,net-solo5` reports the metrics status for memory and net-solo5 respectively.
|
||||||
|
|
||||||
|
# Memprof tracing
|
||||||
|
|
||||||
|
The `create` function has a `memtrace_port` argument (defualts to 4242). On this
|
||||||
|
port, when a TCP client connects,
|
||||||
|
[OCaml memory profiling](https://ocaml.org/api/Gc.Memprof.html) is started and
|
||||||
|
dumped to the client. The `sampling_rate` (defaults to 1e-4) is used. This uses
|
||||||
|
the [memprof-mirage](https://github.com/hannesm/memprof-mirage) package.
|
||||||
|
|
||||||
|
The output trace is best being viewed with
|
||||||
|
[memtrace_viewer](https://github.com/janestreet/memtrace_viewer/).
|
2
dune-project
Normal file
2
dune-project
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
(lang dune 1.0)
|
||||||
|
(name mirage-monitoring)
|
File diff suppressed because one or more lines are too long
19
index.html
19
index.html
|
@ -1,19 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<head>
|
|
||||||
<title>index</title>
|
|
||||||
<link rel="stylesheet" href="./odoc.css"/>
|
|
||||||
<meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main class="content">
|
|
||||||
<div class="by-name">
|
|
||||||
<h2>OCaml package documentation</h2>
|
|
||||||
<ol>
|
|
||||||
<li><a href="mirage-monitoring/index.html">mirage-monitoring</a> <span class="version">0.0.2</span></li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
36
mirage-monitoring.opam
Normal file
36
mirage-monitoring.opam
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
opam-version: "2.0"
|
||||||
|
maintainer: "Robur <team@robur.coop>"
|
||||||
|
authors: ["Robur <team@robur.coop>"]
|
||||||
|
homepage: "https://github.com/robur-coop/mirage-monitoring"
|
||||||
|
doc: "https://robur-coop.github.io/mirage-monitoring"
|
||||||
|
dev-repo: "git+https://github.com/robur-coop/mirage-monitoring.git"
|
||||||
|
bug-reports: "https://github.com/robur-coop/mirage-monitoring/issues"
|
||||||
|
license: "AGPL-3.0-only"
|
||||||
|
|
||||||
|
depends: [
|
||||||
|
"ocaml" {>= "4.11.0"}
|
||||||
|
"dune"
|
||||||
|
"logs" {>= "0.6.3"}
|
||||||
|
"metrics" {>= "0.4.0"}
|
||||||
|
"metrics-lwt" {>= "0.2.0"}
|
||||||
|
"metrics-influx" {>= "0.2.0"}
|
||||||
|
"mirage-time" {>= "2.0.0"}
|
||||||
|
"tcpip" {>= "7.0.0"}
|
||||||
|
"mirage-runtime" {>= "4.5.0"}
|
||||||
|
"memtrace-mirage" {>= "0.2.1.2.2"}
|
||||||
|
"mirage-clock" {>= "4.0.0"}
|
||||||
|
]
|
||||||
|
conflicts: [
|
||||||
|
"mirage-solo5" {< "0.9.2"}
|
||||||
|
"mirage-xen" {< "8.0.2"}
|
||||||
|
]
|
||||||
|
build: [
|
||||||
|
["dune" "subst"] {dev}
|
||||||
|
["dune" "build" "-p" name "-j" jobs]
|
||||||
|
["dune" "runtest" "-p" name "-j" jobs] {with-test}
|
||||||
|
]
|
||||||
|
synopsis: "Monitoring of MirageOS unikernels"
|
||||||
|
description: """
|
||||||
|
Reporting metrics to Influx, Telegraf. Dynamic adjusting log level and metrics
|
||||||
|
sources, memprof profiling.
|
||||||
|
"""
|
|
@ -1,12 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Make (mirage-monitoring.Mirage_monitoring.Make)</title><link rel="stylesheet" href="../../../odoc.css"/><meta charset="utf-8"/><meta name="generator" content="odoc 2.1.1"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="../../../highlight.pack.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body class="odoc"><nav class="odoc-nav"><a href="../index.html">Up</a> – <a href="../../index.html">mirage-monitoring</a> » <a href="../index.html">Mirage_monitoring</a> » Make</nav><header class="odoc-preamble"><h1>Module <code><span>Mirage_monitoring.Make</span></code></h1></header><nav class="odoc-toc"><ul><li><a href="#parameters">Parameters</a></li><li><a href="#signature">Signature</a></li></ul></nav><div class="odoc-content"><h2 id="parameters"><a href="#parameters" class="anchor"></a>Parameters</h2><div class="odoc-spec"><div class="spec parameter" id="argument-1-T" class="anchored"><a href="#argument-1-T" class="anchor"></a><code><span><span class="keyword">module</span> </span><span>T</span><span> : <span class="xref-unresolved">Mirage_time</span>.S</span></code></div></div><div class="odoc-spec"><div class="spec parameter" id="argument-2-P" class="anchored"><a href="#argument-2-P" class="anchor"></a><code><span><span class="keyword">module</span> </span><span>P</span><span> : <span class="xref-unresolved">Mirage_clock</span>.PCLOCK</span></code></div></div><div class="odoc-spec"><div class="spec parameter" id="argument-3-S" class="anchored"><a href="#argument-3-S" class="anchor"></a><code><span><span class="keyword">module</span> </span><span>S</span><span> : <span class="xref-unresolved">Tcpip</span>.Stack.V4V6</span></code></div></div><h2 id="signature"><a href="#signature" class="anchor"></a>Signature</h2><div class="odoc-spec"><div class="spec value" id="val-create" class="anchored"><a href="#val-create" class="anchor"></a><code><span><span class="keyword">val</span> create :
|
|
||||||
<span>?interval:int <span class="arrow">-></span></span>
|
|
||||||
<span>?quick:bool <span class="arrow">-></span></span>
|
|
||||||
<span>?hostname:string <span class="arrow">-></span></span>
|
|
||||||
<span><span class="xref-unresolved">Ipaddr</span>.t <span class="arrow">-></span></span>
|
|
||||||
<span>?port:int <span class="arrow">-></span></span>
|
|
||||||
<span>?listen_port:int <span class="arrow">-></span></span>
|
|
||||||
<span>?memtrace_port:int <span class="arrow">-></span></span>
|
|
||||||
<span>?sampling_rate:float <span class="arrow">-></span></span>
|
|
||||||
<span><span class="xref-unresolved">S</span>.t <span class="arrow">-></span></span>
|
|
||||||
unit</span></code></div><div class="spec-doc"><p><code>create ~interval ~quick ~hostname ip ~port ~listen_port ~memtrace_port ~sampling_rate stack</code> initiates monitoring on <code>stack</code> for the unikernel. The metrics are reported every <code>interval</code> (defaults to 10) seconds to <code>ip</code> on <code>port</code> (defaults to 8094) via TCP using the influxd wire protocol. On <code>listen_port</code> (defaults to 2323) a TCP connection can be initiated to adjust the log level and enable and disable metrics sources. On <code>memtrace_port</code> (defaults to 4242) a single TCP client can connect simultaneously to receive a <code>Gc.Memprof</code> trace. The <code>sampling_rate</code> defaults to <code>1e-4</code>. If <code>quick</code> is provided and <code>false</code> (defaults to <code>true</code>), <code>Solo5_os.Memory.stat</code> is used, otherwise <code>Solo5_os.Memory.quick_stat</code> is used.</p></div></div></div></body></html>
|
|
|
@ -1,9 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Mirage_monitoring (mirage-monitoring.Mirage_monitoring)</title><link rel="stylesheet" href="../../odoc.css"/><meta charset="utf-8"/><meta name="generator" content="odoc 2.1.1"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="../../highlight.pack.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body class="odoc"><nav class="odoc-nav"><a href="../index.html">Up</a> – <a href="../index.html">mirage-monitoring</a> » Mirage_monitoring</nav><header class="odoc-preamble"><h1>Module <code><span>Mirage_monitoring</span></code></h1></header><div class="odoc-content"><div class="odoc-spec"><div class="spec value" id="val-counter_metrics" class="anchored"><a href="#val-counter_metrics" class="anchor"></a><code><span><span class="keyword">val</span> counter_metrics :
|
|
||||||
<span>f:<span>( <span><span class="type-var">'a</span> <span class="arrow">-></span></span> string )</span> <span class="arrow">-></span></span>
|
|
||||||
<span>string <span class="arrow">-></span></span>
|
|
||||||
<span><span>( <span><span class="xref-unresolved">Metrics</span>.field list</span>, <span><span class="type-var">'a</span> <span class="arrow">-></span></span> <span class="xref-unresolved">Metrics</span>.data )</span> <span class="xref-unresolved">Metrics</span>.src</span></span></code></div></div><div class="odoc-spec"><div class="spec value" id="val-vmname" class="anchored"><a href="#val-vmname" class="anchor"></a><code><span><span class="keyword">val</span> vmname : <span>string <span class="arrow">-></span></span> <span class="xref-unresolved">Metrics</span>.field</span></code></div><div class="spec-doc"><p><code>vmname name</code> creates a <code>tag</code> with the virtual machine name.</p></div></div><div class="odoc-spec"><div class="spec module" id="module-Make" class="anchored"><a href="#module-Make" class="anchor"></a><code><span><span class="keyword">module</span> <a href="Make/index.html">Make</a></span><span>
|
|
||||||
(<a href="Make/index.html#argument-1-T">T</a> : <span class="xref-unresolved">Mirage_time</span>.S)
|
|
||||||
(<a href="Make/index.html#argument-2-P">P</a> : <span class="xref-unresolved">Mirage_clock</span>.PCLOCK)
|
|
||||||
(<a href="Make/index.html#argument-3-S">S</a> : <span class="xref-unresolved">Tcpip</span>.Stack.V4V6) :
|
|
||||||
<span class="keyword">sig</span> ... <span class="keyword">end</span></span></code></div></div></div></body></html>
|
|
|
@ -1,2 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>index (mirage-monitoring.index)</title><link rel="stylesheet" href="../odoc.css"/><meta charset="utf-8"/><meta name="generator" content="odoc 2.1.1"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><script src="../highlight.pack.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body class="odoc"><nav class="odoc-nav"><a href="../index.html">Up</a> – mirage-monitoring</nav><header class="odoc-preamble"><h1 id="mirage-monitoring-index"><a href="#mirage-monitoring-index" class="anchor"></a>mirage-monitoring index</h1></header><nav class="odoc-toc"><ul><li><a href="#library-mirage-monitoring">Library mirage-monitoring</a></li></ul></nav><div class="odoc-content"><h2 id="library-mirage-monitoring"><a href="#library-mirage-monitoring" class="anchor"></a>Library mirage-monitoring</h2><p>The entry point of this library is the module: <a href="Mirage_monitoring/index.html"><code>Mirage_monitoring</code></a>.</p></div></body></html>
|
|
783
odoc.css
783
odoc.css
|
@ -1,783 +0,0 @@
|
||||||
@charset "UTF-8";
|
|
||||||
/* Copyright (c) 2016 The odoc contributors. All rights reserved.
|
|
||||||
Distributed under the ISC license, see terms at the end of the file.
|
|
||||||
odoc 2.1.1 */
|
|
||||||
|
|
||||||
/* Fonts */
|
|
||||||
@import url('https://fonts.googleapis.com/css?family=Fira+Mono:400,500');
|
|
||||||
@import url('https://fonts.googleapis.com/css?family=Noticia+Text:400,400i,700');
|
|
||||||
@import url('https://fonts.googleapis.com/css?family=Fira+Sans:400,400i,500,500i,600,600i,700,700i');
|
|
||||||
|
|
||||||
:root,
|
|
||||||
.light:root {
|
|
||||||
--main-background: #FFFFFF;
|
|
||||||
|
|
||||||
--color: #333333;
|
|
||||||
--link-color: #2C94BD;
|
|
||||||
--anchor-hover: #555;
|
|
||||||
--anchor-color: #d5d5d5;
|
|
||||||
--xref-shadow: #cc6666;
|
|
||||||
--header-shadow: #ddd;
|
|
||||||
--by-name-version-color: #aaa;
|
|
||||||
--by-name-nav-link-color: #222;
|
|
||||||
--target-background: rgba(187, 239, 253, 0.3);
|
|
||||||
--target-shadow: rgba(187, 239, 253, 0.8);
|
|
||||||
--pre-border-color: #eee;
|
|
||||||
--code-background: #f6f8fa;
|
|
||||||
|
|
||||||
--li-code-background: #f6f8fa;
|
|
||||||
--li-code-color: #0d2b3e;
|
|
||||||
--toc-color: #1F2D3D;
|
|
||||||
--toc-before-color: #777;
|
|
||||||
--toc-background: #f6f8fa;
|
|
||||||
--toc-list-border: #ccc;
|
|
||||||
|
|
||||||
--spec-summary-border-color: #5c9cf5;
|
|
||||||
--spec-summary-background: var(--code-background);
|
|
||||||
--spec-summary-hover-background: #ebeff2;
|
|
||||||
--spec-details-after-background: rgba(0, 4, 15, 0.05);
|
|
||||||
--spec-details-after-shadow: rgba(204, 204, 204, 0.53);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark:root {
|
|
||||||
--main-background: #202020;
|
|
||||||
--code-background: #222;
|
|
||||||
--line-numbers-background: rgba(0, 0, 0, 0.125);
|
|
||||||
--navbar-background: #202020;
|
|
||||||
|
|
||||||
--color: #bebebe;
|
|
||||||
--dirname-color: #666;
|
|
||||||
--underline-color: #444;
|
|
||||||
--visited-color: #002800;
|
|
||||||
--visited-number-color: #252;
|
|
||||||
--unvisited-color: #380000;
|
|
||||||
--unvisited-number-color: #622;
|
|
||||||
--somevisited-color: #303000;
|
|
||||||
--highlight-color: #303e3f;
|
|
||||||
--line-number-color: rgba(230, 230, 230, 0.3);
|
|
||||||
--unvisited-margin-color: #622;
|
|
||||||
--border: #333;
|
|
||||||
--navbar-border: #333;
|
|
||||||
--code-color: #ccc;
|
|
||||||
|
|
||||||
--li-code-background: #373737;
|
|
||||||
--li-code-color: #999;
|
|
||||||
--toc-color: #777;
|
|
||||||
--toc-background: #252525;
|
|
||||||
|
|
||||||
--hljs-link: #999;
|
|
||||||
--hljs-keyword: #cda869;
|
|
||||||
--hljs-regexp: #f9ee98;
|
|
||||||
--hljs-title: #dcdcaa;
|
|
||||||
--hljs-type: #ac885b;
|
|
||||||
--hljs-meta: #82aaff;
|
|
||||||
--hljs-variable: #cf6a4c;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
|
||||||
--main-background: #202020;
|
|
||||||
--code-background: #333;
|
|
||||||
--line-numbers-background: rgba(0, 0, 0, 0.125);
|
|
||||||
--navbar-background: #202020;
|
|
||||||
|
|
||||||
--meter-unvisited-color: #622;
|
|
||||||
--meter-visited-color: #252;
|
|
||||||
--meter-separator-color: black;
|
|
||||||
|
|
||||||
--color: #bebebe;
|
|
||||||
--dirname-color: #666;
|
|
||||||
--underline-color: #444;
|
|
||||||
--visited-color: #002800;
|
|
||||||
--visited-number-color: #252;
|
|
||||||
--unvisited-color: #380000;
|
|
||||||
--unvisited-number-color: #622;
|
|
||||||
--somevisited-color: #303000;
|
|
||||||
--highlight-color: #303e3f;
|
|
||||||
--line-number-color: rgba(230, 230, 230, 0.3);
|
|
||||||
--unvisited-margin-color: #622;
|
|
||||||
--border: #333;
|
|
||||||
--navbar-border: #333;
|
|
||||||
--code-color: #ccc;
|
|
||||||
--by-name-nav-link-color: var(--color);
|
|
||||||
|
|
||||||
--li-code-background: #373737;
|
|
||||||
--li-code-color: #999;
|
|
||||||
--toc-color: #777;
|
|
||||||
--toc-before-color: #777;
|
|
||||||
--toc-background: #252525;
|
|
||||||
--toc-list-border: #ccc;
|
|
||||||
--spec-summary-hover-background: #ebeff2;
|
|
||||||
--spec-details-after-background: rgba(0, 4, 15, 0.05);
|
|
||||||
--spec-details-after-shadow: rgba(204, 204, 204, 0.53);
|
|
||||||
|
|
||||||
--hljs-link: #999;
|
|
||||||
--hljs-keyword: #cda869;
|
|
||||||
--hljs-regexp: #f9ee98;
|
|
||||||
--hljs-title: #dcdcaa;
|
|
||||||
--hljs-type: #ac885b;
|
|
||||||
--hljs-meta: #82aaff;
|
|
||||||
--hljs-variable: #cf6a4c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reset a few things. */
|
|
||||||
|
|
||||||
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
font: inherit;
|
|
||||||
vertical-align: baseline;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
*, *:before, *:after {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
text-align: left;
|
|
||||||
background: #FFFFFF;
|
|
||||||
color: var(--color);
|
|
||||||
background-color: var(--main-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
max-width: 100ex;
|
|
||||||
margin-left: calc(10vw + 20ex);
|
|
||||||
margin-right: 4ex;
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 50px;
|
|
||||||
font-family: "Noticia Text", Georgia, serif;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav {
|
|
||||||
font-family: "Fira Sans", Helvetica, Arial, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Basic markup elements */
|
|
||||||
|
|
||||||
b, strong {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
em, i em.odd{
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
em.odd, i em {
|
|
||||||
font-style: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
sup {
|
|
||||||
vertical-align: super;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub {
|
|
||||||
vertical-align: sub;
|
|
||||||
}
|
|
||||||
|
|
||||||
sup, sub {
|
|
||||||
font-size: 12px;
|
|
||||||
line-height: 0;
|
|
||||||
margin-left: 0.2ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul, ol {
|
|
||||||
list-style-position: outside
|
|
||||||
}
|
|
||||||
|
|
||||||
ul>li {
|
|
||||||
margin-left: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol>li {
|
|
||||||
margin-left: 27.2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
li>*:first-child {
|
|
||||||
margin-top: 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Text alignements, this should be forbidden. */
|
|
||||||
|
|
||||||
.left {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Links and anchors */
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--link-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
box-shadow: 0 1px 0 0 var(--link-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Linked highlight */
|
|
||||||
*:target {
|
|
||||||
background-color: var(--target-background) !important;
|
|
||||||
box-shadow: 0 0px 0 1px var(--target-shadow) !important;
|
|
||||||
border-radius: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
*:hover > a.anchor {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.anchor:before {
|
|
||||||
content: "#";
|
|
||||||
}
|
|
||||||
|
|
||||||
a.anchor:hover {
|
|
||||||
box-shadow: none;
|
|
||||||
text-decoration: none;
|
|
||||||
color: var(--anchor-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
a.anchor {
|
|
||||||
visibility: hidden;
|
|
||||||
position: absolute;
|
|
||||||
/* top: 0px; */
|
|
||||||
/* margin-left: -3ex; */
|
|
||||||
margin-left: -1.3em;
|
|
||||||
font-weight: normal;
|
|
||||||
font-style: normal;
|
|
||||||
padding-right: 0.4em;
|
|
||||||
padding-left: 0.4em;
|
|
||||||
/* To remain selectable */
|
|
||||||
color: var(--anchor-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.spec > a.anchor {
|
|
||||||
margin-left: -2.3em;
|
|
||||||
padding-right: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.xref-unresolved {
|
|
||||||
color: #2C94BD;
|
|
||||||
}
|
|
||||||
.xref-unresolved:hover {
|
|
||||||
box-shadow: 0 1px 0 0 var(--xref-shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Section and document divisions.
|
|
||||||
Until at least 4.03 many of the modules of the stdlib start at .h7,
|
|
||||||
we restart the sequence there like h2 */
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6, .h7, .h8, .h9, .h10 {
|
|
||||||
font-family: "Fira Sans", Helvetica, Arial, sans-serif;
|
|
||||||
font-weight: 400;
|
|
||||||
padding-top: 0.1em;
|
|
||||||
line-height: 1.2;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 2.441em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 1.953em;
|
|
||||||
box-shadow: 0 1px 0 0 var(--header-shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 1.563em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
small, .font_small {
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 code, h1 tt {
|
|
||||||
font-size: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 code, h2 tt {
|
|
||||||
font-size: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 code, h3 tt {
|
|
||||||
font-size: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 code, h3 tt {
|
|
||||||
font-size: inherit;
|
|
||||||
font-weight: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 1.12em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Comment delimiters, hidden but accessible to screen readers and
|
|
||||||
selected for copy/pasting */
|
|
||||||
|
|
||||||
/* Taken from bootstrap */
|
|
||||||
/* See also https://stackoverflow.com/a/27769435/4220738 */
|
|
||||||
.comment-delim {
|
|
||||||
position: absolute;
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
padding: 0;
|
|
||||||
margin: -1px;
|
|
||||||
overflow: hidden;
|
|
||||||
clip: rect(0, 0, 0, 0);
|
|
||||||
white-space: nowrap;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Preformatted and code */
|
|
||||||
|
|
||||||
tt, code, pre {
|
|
||||||
font-family: "Fira Mono", courier;
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
padding: 0.1em;
|
|
||||||
border: 1px solid var(--pre-border-color);
|
|
||||||
border-radius: 5px;
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
p code,
|
|
||||||
li code {
|
|
||||||
background-color: var(--li-code-background);
|
|
||||||
color: var(--li-code-color);
|
|
||||||
border-radius: 3px;
|
|
||||||
padding: 0 0.3ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
p a > code {
|
|
||||||
color: var(--link-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Code blocks (e.g. Examples) */
|
|
||||||
|
|
||||||
pre code {
|
|
||||||
font-size: 0.893rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Code lexemes */
|
|
||||||
|
|
||||||
.keyword {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrow { white-space: nowrap }
|
|
||||||
|
|
||||||
/* Module member specification */
|
|
||||||
|
|
||||||
.spec {
|
|
||||||
background-color: var(--spec-summary-background);
|
|
||||||
border-radius: 3px;
|
|
||||||
border-left: 4px solid var(--spec-summary-border-color);
|
|
||||||
border-right: 5px solid transparent;
|
|
||||||
padding: 0.35em 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.def-doc {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Spacing between items */
|
|
||||||
div.odoc-spec,.odoc-include {
|
|
||||||
margin-bottom: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spec.type .variant {
|
|
||||||
margin-left: 2ch;
|
|
||||||
}
|
|
||||||
.spec.type .variant p {
|
|
||||||
margin: 0;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
.spec.type .record {
|
|
||||||
margin-left: 2ch;
|
|
||||||
}
|
|
||||||
.spec.type .record p {
|
|
||||||
margin: 0;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.def {
|
|
||||||
margin-top: 0;
|
|
||||||
text-indent: -2ex;
|
|
||||||
padding-left: 2ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.def+div.def-doc {
|
|
||||||
margin-left: 1ex;
|
|
||||||
margin-top: 2.5px
|
|
||||||
}
|
|
||||||
|
|
||||||
div.def-doc>*:first-child {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Collapsible inlined include and module */
|
|
||||||
|
|
||||||
.odoc-include details {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.odoc-include.shadowed-include {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.odoc-include details:after {
|
|
||||||
z-index: -100;
|
|
||||||
display: block;
|
|
||||||
content: " ";
|
|
||||||
position: absolute;
|
|
||||||
border-radius: 0 1ex 1ex 0;
|
|
||||||
right: -20px;
|
|
||||||
top: 1px;
|
|
||||||
bottom: 1px;
|
|
||||||
width: 15px;
|
|
||||||
background: var(--spec-details-after-background, rgba(0, 4, 15, 0.05));
|
|
||||||
box-shadow: 0 0px 0 1px var(--spec-details-after-shadow, rgba(204, 204, 204, 0.53));
|
|
||||||
}
|
|
||||||
|
|
||||||
.odoc-include summary {
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
cursor: pointer;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.odoc-include summary:hover {
|
|
||||||
background-color: var(--spec-summary-hover-background);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME: Does not work in Firefox. */
|
|
||||||
.odoc-include summary::-webkit-details-marker {
|
|
||||||
color: #888;
|
|
||||||
transform: scaleX(-1);
|
|
||||||
position: absolute;
|
|
||||||
top: calc(50% - 5px);
|
|
||||||
height: 11px;
|
|
||||||
right: -29px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Records and variants FIXME */
|
|
||||||
|
|
||||||
div.def table {
|
|
||||||
text-indent: 0em;
|
|
||||||
padding: 0;
|
|
||||||
margin-left: -2ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.def {
|
|
||||||
padding-left: 2ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.def-doc *:first-child {
|
|
||||||
margin-top: 0em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Lists of @tags */
|
|
||||||
|
|
||||||
.at-tags { list-style-type: none; margin-left: -3ex; }
|
|
||||||
.at-tags li { padding-left: 3ex; text-indent: -3ex; }
|
|
||||||
.at-tags .at-tag { text-transform: capitalize }
|
|
||||||
|
|
||||||
/* Lists of modules */
|
|
||||||
|
|
||||||
.modules { list-style-type: none; margin-left: -3ex; }
|
|
||||||
.modules li { padding-left: 3ex; text-indent: -3ex; margin-top: 5px }
|
|
||||||
.modules .synopsis { padding-left: 1ch; }
|
|
||||||
|
|
||||||
/* Odig package index */
|
|
||||||
|
|
||||||
.packages { list-style-type: none; margin-left: -3ex; }
|
|
||||||
.packages li { padding-left: 3ex; text-indent: -3ex }
|
|
||||||
.packages li a.anchor { padding-right: 0.5ch; padding-left: 3ch; }
|
|
||||||
.packages .version { font-size: 10px; color: var(--by-name-version-color); }
|
|
||||||
.packages .synopsis { padding-left: 1ch }
|
|
||||||
|
|
||||||
.by-name nav a {
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 18px;
|
|
||||||
margin-right: 1ex;
|
|
||||||
color: var(--by-name-nav-link-color,);
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.by-tag nav a {
|
|
||||||
margin-right: 1ex;
|
|
||||||
color: var(--by-name-nav-link-color);
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.by-tag ol { list-style-type: none; }
|
|
||||||
.by-tag ol.tags li { margin-left: 1ch; display: inline-block }
|
|
||||||
.by-tag td:first-child { text-transform: uppercase; }
|
|
||||||
|
|
||||||
/* Odig package page */
|
|
||||||
|
|
||||||
.package nav {
|
|
||||||
display: inline;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.package .version {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.package.info {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.package.info td:first-child {
|
|
||||||
font-style: italic;
|
|
||||||
padding-right: 2ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.package.info ul {
|
|
||||||
list-style-type: none;
|
|
||||||
display: inline;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.package.info li {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0;
|
|
||||||
margin-right: 1ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
#info-authors li, #info-maintainers li {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sidebar and TOC */
|
|
||||||
|
|
||||||
.odoc-toc:before {
|
|
||||||
display: block;
|
|
||||||
content: "Contents";
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 1em;
|
|
||||||
margin: 1.414em 0 0.5em;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--toc-before-color);
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.odoc-toc {
|
|
||||||
position: fixed;
|
|
||||||
top: 0px;
|
|
||||||
bottom: 0px;
|
|
||||||
left: 0px;
|
|
||||||
max-width: 30ex;
|
|
||||||
min-width: 26ex;
|
|
||||||
width: 20%;
|
|
||||||
background: var(--toc-background);
|
|
||||||
overflow: auto;
|
|
||||||
color: var(--toc-color);
|
|
||||||
padding-left: 2ex;
|
|
||||||
padding-right: 2ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.odoc-toc ul li a {
|
|
||||||
font-family: "Fira Sans", sans-serif;
|
|
||||||
font-size: 0.95em;
|
|
||||||
color: var(--color);
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 1.6em;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.odoc-toc ul li a:hover {
|
|
||||||
box-shadow: none;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* First level titles */
|
|
||||||
|
|
||||||
.odoc-toc>ul>li>a {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.odoc-toc li ul {
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.odoc-toc ul {
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.odoc-toc ul li {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.odoc-toc>ul>li {
|
|
||||||
margin-bottom: 0.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.odoc-toc ul li li {
|
|
||||||
border-left: 1px solid var(--toc-list-border);
|
|
||||||
margin-left: 5px;
|
|
||||||
padding-left: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile adjustements. */
|
|
||||||
|
|
||||||
@media only screen and (max-width: 95ex) {
|
|
||||||
.odoc-content {
|
|
||||||
margin: auto;
|
|
||||||
padding: 2em;
|
|
||||||
}
|
|
||||||
.odoc-toc {
|
|
||||||
position: static;
|
|
||||||
width: auto;
|
|
||||||
min-width: unset;
|
|
||||||
max-width: unset;
|
|
||||||
border: none;
|
|
||||||
padding: 0.2em 1em;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Print adjustements. */
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
body {
|
|
||||||
color: black;
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
body nav:first-child {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Syntax highlighting (based on github-gist) */
|
|
||||||
|
|
||||||
.hljs {
|
|
||||||
display: block;
|
|
||||||
background: var(--code-background);
|
|
||||||
padding: 0.5em;
|
|
||||||
color: var(--color);
|
|
||||||
overflow-x: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-comment,
|
|
||||||
.hljs-meta {
|
|
||||||
color: #969896;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-string,
|
|
||||||
.hljs-variable,
|
|
||||||
.hljs-template-variable,
|
|
||||||
.hljs-strong,
|
|
||||||
.hljs-emphasis,
|
|
||||||
.hljs-quote {
|
|
||||||
color: #df5000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-keyword,
|
|
||||||
.hljs-selector-tag {
|
|
||||||
color: #a71d5d;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-type,
|
|
||||||
.hljs-class .hljs-title {
|
|
||||||
color: #458;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-literal,
|
|
||||||
.hljs-symbol,
|
|
||||||
.hljs-bullet,
|
|
||||||
.hljs-attribute {
|
|
||||||
color: #0086b3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-section,
|
|
||||||
.hljs-name {
|
|
||||||
color: #63a35c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-tag {
|
|
||||||
color: #333333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-attr,
|
|
||||||
.hljs-selector-id,
|
|
||||||
.hljs-selector-class,
|
|
||||||
.hljs-selector-attr,
|
|
||||||
.hljs-selector-pseudo {
|
|
||||||
color: #795da3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-addition {
|
|
||||||
color: #55a532;
|
|
||||||
background-color: #eaffea;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-deletion {
|
|
||||||
color: #bd2c00;
|
|
||||||
background-color: #ffecec;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-link {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------
|
|
||||||
Copyright (c) 2016 The odoc contributors
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
---------------------------------------------------------------------------*/
|
|
BIN
one.png
Normal file
BIN
one.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 223 KiB |
6
src/dune
Normal file
6
src/dune
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
(library
|
||||||
|
(name mirage_monitoring)
|
||||||
|
(public_name mirage-monitoring)
|
||||||
|
(wrapped false)
|
||||||
|
(libraries logs metrics metrics-lwt metrics-influx mirage-time mirage-clock
|
||||||
|
memtrace-mirage tcpip mirage-runtime))
|
275
src/mirage_monitoring.ml
Normal file
275
src/mirage_monitoring.ml
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
open Lwt.Infix
|
||||||
|
|
||||||
|
let src = Logs.Src.create "mirage-monitoring" ~doc:"MirageOS monitoring"
|
||||||
|
module Log = (val Logs.src_log src : Logs.LOG)
|
||||||
|
|
||||||
|
let ( let* ) = Result.bind
|
||||||
|
|
||||||
|
let create ~f =
|
||||||
|
let data : (string, int) Hashtbl.t = Hashtbl.create 7 in
|
||||||
|
(fun x ->
|
||||||
|
let key = f x in
|
||||||
|
let cur = match Hashtbl.find_opt data key with
|
||||||
|
| None -> 0
|
||||||
|
| Some x -> x
|
||||||
|
in
|
||||||
|
Hashtbl.replace data key (succ cur)),
|
||||||
|
(fun () ->
|
||||||
|
let data, total =
|
||||||
|
Hashtbl.fold (fun key value (acc, total) ->
|
||||||
|
(Metrics.uint key value :: acc), value + total)
|
||||||
|
data ([], 0)
|
||||||
|
in
|
||||||
|
Metrics.uint "total" total :: data)
|
||||||
|
|
||||||
|
let counter_metrics ~f name =
|
||||||
|
let open Metrics in
|
||||||
|
let doc = "Counter metrics" in
|
||||||
|
let incr, get = create ~f in
|
||||||
|
let data thing = incr thing; Data.v (get ()) in
|
||||||
|
Src.v ~doc ~tags:Metrics.Tags.[] ~data name
|
||||||
|
|
||||||
|
let vmname = Metrics.field ~doc:"name of the virtual machine" "vm" Metrics.String
|
||||||
|
|
||||||
|
let get_log_levels s =
|
||||||
|
let qs = String.split_on_char ',' s in
|
||||||
|
let srcs = Logs.Src.list () in
|
||||||
|
let srcs = List.map (fun src -> Logs.Src.name src, Logs.Src.level src) srcs in
|
||||||
|
let* srcs =
|
||||||
|
match qs with
|
||||||
|
| [""] ->
|
||||||
|
let all_level = Logs.level () in
|
||||||
|
Ok (("*", all_level) :: List.filter (fun (_,l) -> l <> all_level) srcs)
|
||||||
|
| ["*"] ->
|
||||||
|
let all_level = Logs.level () in
|
||||||
|
Ok (("*", all_level) :: srcs)
|
||||||
|
| qs ->
|
||||||
|
let* () =
|
||||||
|
let src_names = List.map fst srcs in
|
||||||
|
match List.find_opt (fun src -> not (List.mem src src_names)) qs with
|
||||||
|
| Some bad_src -> Error ("unknown source: " ^ bad_src)
|
||||||
|
| None -> Ok ()
|
||||||
|
in
|
||||||
|
Ok (List.filter (fun (name, _) -> List.mem name qs) srcs)
|
||||||
|
in
|
||||||
|
let levels =
|
||||||
|
List.map (fun (name, level) ->
|
||||||
|
name ^ ":" ^ Logs.level_to_string level)
|
||||||
|
srcs
|
||||||
|
in
|
||||||
|
Ok (`String (String.concat "," levels))
|
||||||
|
|
||||||
|
let get_metrics s =
|
||||||
|
let qs = String.split_on_char ',' s in
|
||||||
|
let srcs = Metrics.Src.list () in
|
||||||
|
let srcs =
|
||||||
|
List.map (fun src ->
|
||||||
|
Metrics.Src.name src, Metrics.Src.is_active src)
|
||||||
|
srcs
|
||||||
|
in
|
||||||
|
let* srcs =
|
||||||
|
match qs with
|
||||||
|
| [""] ->
|
||||||
|
let all = Metrics.all_enabled () in
|
||||||
|
Ok (("*", all) :: (List.filter (fun (_, b) -> b <> all) srcs))
|
||||||
|
| ["*"] ->
|
||||||
|
let all = Metrics.all_enabled () in
|
||||||
|
let tags = Metrics.tags_enabled () in
|
||||||
|
Ok (("*", all) :: List.map (fun t -> "tag:" ^ t, true) tags @ srcs)
|
||||||
|
| qs ->
|
||||||
|
let* () =
|
||||||
|
let src_names = List.map fst srcs in
|
||||||
|
match List.find_opt (fun src -> not (List.mem src src_names)) qs with
|
||||||
|
| Some bad_src -> Error ("unknown source: " ^ bad_src)
|
||||||
|
| None -> Ok ()
|
||||||
|
in
|
||||||
|
Ok (List.filter (fun (n, _) -> List.mem n qs) srcs)
|
||||||
|
in
|
||||||
|
let metrics =
|
||||||
|
List.map (fun (name, act) ->
|
||||||
|
name ^ ":" ^ if act then "enabled" else "disabled")
|
||||||
|
srcs
|
||||||
|
in
|
||||||
|
Ok (`String (String.concat "," metrics))
|
||||||
|
|
||||||
|
let adjust_log_level s =
|
||||||
|
let ts =
|
||||||
|
List.map
|
||||||
|
(fun s -> (fst Mirage_runtime.Conv.log_threshold) s)
|
||||||
|
(String.split_on_char ',' s)
|
||||||
|
in
|
||||||
|
let* oks =
|
||||||
|
List.fold_left (fun acc t ->
|
||||||
|
let* acc = acc in
|
||||||
|
match t with
|
||||||
|
| `Ok l -> Ok (l :: acc)
|
||||||
|
| `Error msg -> Error msg)
|
||||||
|
(Ok []) ts
|
||||||
|
in
|
||||||
|
Mirage_runtime.set_level ~default:(Logs.level ()) oks;
|
||||||
|
Ok `Empty
|
||||||
|
|
||||||
|
let enable_of_str s =
|
||||||
|
let s = String.lowercase_ascii s in
|
||||||
|
if s = "enable" || s = "on" then
|
||||||
|
Ok `Enable
|
||||||
|
else if s = "disable" || s = "off" then
|
||||||
|
Ok `Disable
|
||||||
|
else
|
||||||
|
Error ("couldn't decode 'enable' or 'disable': " ^ s)
|
||||||
|
|
||||||
|
let adjust_metrics s =
|
||||||
|
let ts =
|
||||||
|
List.map (fun s ->
|
||||||
|
match String.split_on_char ':' s with
|
||||||
|
| [ en ] | [ "*" ; en ] ->
|
||||||
|
let* en_or_d = enable_of_str en in
|
||||||
|
Ok (`All, en_or_d)
|
||||||
|
| [ src ; en ] ->
|
||||||
|
let* en_or_d = enable_of_str en in
|
||||||
|
Ok (`Src src, en_or_d)
|
||||||
|
| [ "src" ; src ; en ] ->
|
||||||
|
let* en_or_d = enable_of_str en in
|
||||||
|
Ok (`Src src, en_or_d)
|
||||||
|
| [ "tag" ; tag ; en ] ->
|
||||||
|
let* en_or_d = enable_of_str en in
|
||||||
|
Ok (`Tag tag, en_or_d)
|
||||||
|
| _ -> Error ("couldn't decode metrics " ^ s))
|
||||||
|
(String.split_on_char ',' s)
|
||||||
|
in
|
||||||
|
let* (all, srcs, tags) =
|
||||||
|
List.fold_left (fun acc t ->
|
||||||
|
let* (all, srcs, tags) = acc in
|
||||||
|
let* t = t in
|
||||||
|
match t with
|
||||||
|
| `All, en_or_d -> Ok (Some en_or_d, srcs, tags)
|
||||||
|
| `Src s, en_or_d -> Ok (all, (s, en_or_d) :: srcs, tags)
|
||||||
|
| `Tag t, en_or_d -> Ok (all, srcs, (t, en_or_d) :: tags))
|
||||||
|
(Ok (None, [], [])) ts
|
||||||
|
in
|
||||||
|
(match all with
|
||||||
|
| Some `Enable -> Metrics.enable_all ()
|
||||||
|
| Some `Disable -> Metrics.disable_all ()
|
||||||
|
| None -> ());
|
||||||
|
List.iter (fun (tag, e_or_d) ->
|
||||||
|
match e_or_d with
|
||||||
|
| `Enable -> Metrics.enable_tag tag
|
||||||
|
| `Disable -> Metrics.disable_tag tag)
|
||||||
|
tags ;
|
||||||
|
List.iter (fun (src, e_or_d) ->
|
||||||
|
match List.find_opt (fun s -> Metrics.Src.name s = src) (Metrics.Src.list ()), e_or_d with
|
||||||
|
| Some src, `Enable -> Metrics.Src.enable src
|
||||||
|
| Some src, `Disable -> Metrics.Src.disable src
|
||||||
|
| None, _ ->
|
||||||
|
Log.warn (fun m -> m "%s is not a valid metrics source." src))
|
||||||
|
srcs ;
|
||||||
|
Ok `Empty
|
||||||
|
|
||||||
|
module Make (T : Mirage_time.S) (P : Mirage_clock.PCLOCK) (S : Tcpip.Stack.V4V6) = struct
|
||||||
|
module Memtrace = Memtrace.Make(P)(S.TCP)
|
||||||
|
|
||||||
|
let timer conn get host stack dst =
|
||||||
|
let datas =
|
||||||
|
Metrics.SM.fold (fun src (tags, data) acc ->
|
||||||
|
let name = Metrics.Src.name src in
|
||||||
|
Metrics_influx.encode_line_protocol (host@tags) data name :: acc)
|
||||||
|
(get ()) []
|
||||||
|
in
|
||||||
|
let datas = String.concat "" datas in
|
||||||
|
let write flow =
|
||||||
|
Log.debug (fun m -> m "sending measurements");
|
||||||
|
S.TCP.write flow (Cstruct.of_string datas) >|= function
|
||||||
|
| Ok () -> ()
|
||||||
|
| Error e ->
|
||||||
|
Log.err (fun m -> m "error %a writing to metrics" S.TCP.pp_write_error e);
|
||||||
|
conn := None
|
||||||
|
in
|
||||||
|
match !conn with
|
||||||
|
| None ->
|
||||||
|
begin
|
||||||
|
Log.debug (fun m -> m "creating connection");
|
||||||
|
S.TCP.create_connection (S.tcp stack) dst >>= function
|
||||||
|
| Error msg ->
|
||||||
|
Log.err (fun m -> m "couldn't create connection %a"
|
||||||
|
S.TCP.pp_error msg);
|
||||||
|
Lwt.return_unit
|
||||||
|
| Ok flow ->
|
||||||
|
conn := Some flow;
|
||||||
|
write flow
|
||||||
|
end
|
||||||
|
| Some f -> write f
|
||||||
|
|
||||||
|
let timer_loop get host interval stack dst () =
|
||||||
|
let conn = ref None in
|
||||||
|
let rec one () =
|
||||||
|
Lwt.join [
|
||||||
|
timer conn get host stack dst;
|
||||||
|
T.sleep_ns (Duration.of_sec interval)
|
||||||
|
] >>= fun () ->
|
||||||
|
(one[@tailcall]) ()
|
||||||
|
in
|
||||||
|
one ()
|
||||||
|
|
||||||
|
let create_listener stack port =
|
||||||
|
S.TCP.listen (S.tcp stack) ~port (fun f ->
|
||||||
|
(S.TCP.read f >>= function
|
||||||
|
| Ok `Data data ->
|
||||||
|
if Cstruct.length data > 0 then
|
||||||
|
let rest = Cstruct.(to_string (shift data 1)) in
|
||||||
|
let r =
|
||||||
|
match Cstruct.get_char data 0 with
|
||||||
|
| 'L' -> adjust_log_level rest
|
||||||
|
| 'M' -> adjust_metrics rest
|
||||||
|
| 'l' -> get_log_levels rest
|
||||||
|
| 'm' -> get_metrics rest
|
||||||
|
| _ -> Error "unknown command"
|
||||||
|
in
|
||||||
|
let msg =
|
||||||
|
match r with
|
||||||
|
| Ok `Empty -> "ok"
|
||||||
|
| Ok `String reply -> "ok: " ^ reply
|
||||||
|
| Error msg -> "error: " ^ msg
|
||||||
|
in
|
||||||
|
S.TCP.write f (Cstruct.of_string msg) >|= function
|
||||||
|
| Ok () -> ()
|
||||||
|
| Error e ->
|
||||||
|
Log.warn (fun m -> m "write error on log & metrics listener %a"
|
||||||
|
S.TCP.pp_write_error e)
|
||||||
|
else
|
||||||
|
(Log.debug (fun m -> m "received empty data on log & metrics listener");
|
||||||
|
Lwt.return_unit)
|
||||||
|
| Ok `Eof ->
|
||||||
|
Log.debug (fun m -> m "EOF on log & metrics listener");
|
||||||
|
Lwt.return_unit
|
||||||
|
| Error e ->
|
||||||
|
Log.debug (fun m -> m "read error on log & metrics listener %a"
|
||||||
|
S.TCP.pp_error e);
|
||||||
|
Lwt.return_unit) >>= fun () ->
|
||||||
|
S.TCP.close f)
|
||||||
|
|
||||||
|
let create ?(interval = 10) ?hostname dst ?(port = 8094) ?(listen_port = 2323)
|
||||||
|
?(memtrace_port = 4242) ?(sampling_rate = 1e-4) stack =
|
||||||
|
S.TCP.listen (S.tcp stack) ~port:memtrace_port
|
||||||
|
(fun f ->
|
||||||
|
(* only allow a single tracing client *)
|
||||||
|
match Memtrace.Memprof_tracer.active_tracer () with
|
||||||
|
| Some _ ->
|
||||||
|
Log.warn (fun m -> m "memtrace tracing already active");
|
||||||
|
S.TCP.close f
|
||||||
|
| None ->
|
||||||
|
Logs.info (fun m -> m "starting memtrace tracing");
|
||||||
|
let tracer = Memtrace.start_tracing ~context:None ~sampling_rate f in
|
||||||
|
Lwt.async (fun () ->
|
||||||
|
S.TCP.read f >|= fun _ ->
|
||||||
|
Logs.info (fun m -> m "memtrace tracing read returned, closing");
|
||||||
|
Memtrace.stop_tracing tracer);
|
||||||
|
Lwt.return_unit);
|
||||||
|
let get_cache, reporter = Metrics.cache_reporter () in
|
||||||
|
Metrics.set_reporter reporter;
|
||||||
|
Metrics.enable_all ();
|
||||||
|
Metrics_lwt.init_periodic (fun () -> T.sleep_ns (Duration.of_sec interval));
|
||||||
|
let host = match hostname with None -> [] | Some host -> [vmname host] in
|
||||||
|
Lwt.async (timer_loop get_cache host interval stack (dst, port));
|
||||||
|
create_listener stack listen_port
|
||||||
|
end
|
||||||
|
|
21
src/mirage_monitoring.mli
Normal file
21
src/mirage_monitoring.mli
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
val counter_metrics : f:('a -> string) -> string ->
|
||||||
|
(Metrics.field list, 'a -> Metrics.data) Metrics.src
|
||||||
|
|
||||||
|
val vmname : string -> Metrics.field
|
||||||
|
(** [vmname name] creates a [tag] with the virtual machine name. *)
|
||||||
|
|
||||||
|
module Make (T : Mirage_time.S) (P : Mirage_clock.PCLOCK) (S : Tcpip.Stack.V4V6) : sig
|
||||||
|
|
||||||
|
val create : ?interval:int -> ?hostname:string -> Ipaddr.t -> ?port:int ->
|
||||||
|
?listen_port:int -> ?memtrace_port:int -> ?sampling_rate:float -> S.t ->
|
||||||
|
unit
|
||||||
|
(** [create ~interval ~hostname ip ~port ~listen_port ~memtrace_port ~sampling_rate stack]
|
||||||
|
initiates monitoring on [stack] for the unikernel. The metrics are reported
|
||||||
|
every [interval] (defaults to 10) seconds to [ip] on [port] (defaults to
|
||||||
|
8094) via TCP using the influxd wire protocol. On [listen_port] (defaults
|
||||||
|
to 2323) a TCP connection can be initiated to adjust the log level and
|
||||||
|
enable and disable metrics sources. On [memtrace_port] (defaults to 4242)
|
||||||
|
a single TCP client can connect simultaneously to receive a [Gc.Memprof]
|
||||||
|
trace. The [sampling_rate] defaults to [1e-4]. *)
|
||||||
|
end
|
Loading…
Reference in a new issue