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