add plugin

This commit is contained in:
zzz
2025-04-09 08:09:03 -04:00
parent f01aeb9294
commit 68d26f7022
23 changed files with 1644 additions and 0 deletions

68
build.xml Normal file
View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<project basedir="." default="all" name="prometheus">
<property file="override.properties"/>
<target name="all" depends="clean,plugin" />
<target name="war" >
<ant dir="src" target="build" />
</target>
<target name="plugin" depends="war">
<!-- get version number -->
<buildnumber file="scripts/build.number" />
<!-- change version in servlet when you change this -->
<property name="release.number" value="0.1" />
<!-- make the update su3 -->
<copy file="LICENSE.txt" todir="plugin/" overwrite="true" />
<copy file="README.txt" todir="plugin/" overwrite="true" />
<copy file="scripts/plugin.config" todir="plugin/" overwrite="true" />
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.config" append="true">
<arg value="update-only=true" />
</exec>
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.config" append="true">
<arg value="version=${release.number}-b${build.number}" />
</exec>
<mkdir dir="plugin/console/webapps" />
<copy file="src/build/prometheus.war" todir="plugin/console/webapps/" overwrite="true" />
<input message="Enter su3 signing key password:" addproperty="release.password.su3" />
<fail message="You must enter a password." >
<condition>
<equals arg1="${release.password.su3}" arg2=""/>
</condition>
</fail>
<!-- this will fail if no su3 keys exist, as it needs the password twice -->
<exec executable="scripts/makeplugin.sh" inputstring="${release.password.su3}" failonerror="true" >
<arg value="plugin" />
</exec>
<move file="prometheus.su3" tofile="prometheus-update.su3" overwrite="true" />
<!-- make the install su3 -->
<copy file="scripts/plugin.config" todir="plugin/" overwrite="true" />
<!-- Files in installer but not update. Be sure to Add to delete fileset above and clean target below -->
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.config" append="true">
<arg value="version=${release.number}-b${build.number}" />
</exec>
<exec executable="scripts/makeplugin.sh" inputstring="${release.password.su3}" failonerror="true" >
<arg value="plugin" />
</exec>
</target>
<target name="distclean" depends="clean" />
<target name="clean" >
<ant dir="src" target="clean" />
<defaultexcludes remove="**/*~"/>
<delete>
<fileset dir="." includes="*/*.~ **/*.*~ */**/*.*~ *.*~" />
</delete>
<delete file="plugin/plugin.config" />
<delete file="plugin/console/webapps/prometheus.war" />
<delete file="plugin/LICENSE.txt" />
<delete file="plugin/README.txt" />
<delete file="prometheus.su3" />
<delete file="prometheus-update.su3" />
<delete file="plugin.zip" />
</target>
</project>

View File

@ -0,0 +1,4 @@
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.10.14
Created-By: 1.8.0_442-8u442-b06~us1-0ubuntu1~24.10-b06 (Private Build)

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>io.prometheus.metrics.exporter.servlet.javax.PrometheusMetricsServlet</servlet-name>
<servlet-class>io.prometheus.metrics.exporter.servlet.javax.PrometheusMetricsServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- precompiled servlets -->
<servlet-mapping>
<servlet-name>io.prometheus.metrics.exporter.servlet.javax.PrometheusMetricsServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>

13
resources/collapse.css Normal file
View File

@ -0,0 +1,13 @@
#configuration {
display: none !important;
}
#expand {
display: inline-block !important;
z-index: 100 !important;
}
#collapse {
display: none !important;
z-index: -1 !important;
}

13
resources/expand.css Normal file
View File

@ -0,0 +1,13 @@
#configuration {
display: table !important;
}
#expand {
display: none !important;
z-index: -1 !important;
}
#collapse {
display: inline-block !important;
z-index: 100 !important;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B

View File

@ -0,0 +1 @@
<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><radialGradient id="a" cx="31.93" cy="32" gradientTransform="matrix(.83 0 0 .81 5.44 5.97)" gradientUnits="userSpaceOnUse" r="17.66"><stop offset="0" stop-color="#ccc"/><stop offset="1" stop-color="#808080"/></radialGradient><path d="m31.93 14.34c-9.71 0-17.66 7.95-17.66 17.66s7.95 17.66 17.66 17.66 17.66-7.95 17.66-17.66-7.95-17.66-17.66-17.66zm0 25.02c-4.12 0-7.36-3.24-7.36-7.36s3.24-7.36 7.36-7.36 7.36 3.24 7.36 7.36-3.24 7.36-7.36 7.36z" fill="url(#a)" stroke="#808080"/><path d="m54.89 35.24c.15-1.03.29-2.06.29-3.24s-.15-2.21-.29-3.24l6.62-4.7c.6-.44.88-1.33.44-2.06l-6.47-10.9c-.44-.74-1.18-1.03-1.91-.6l-7.36 3.39a26.35 26.35 0 0 0 -5.59-3.24l-.74-8.1c-.15-.74-.74-1.33-1.47-1.33h-12.66c-.74 0-1.47.6-1.47 1.33l-.74 8.1c-2.06.88-3.97 1.91-5.59 3.24l-7.36-3.39c-.74-.29-1.62 0-1.91.6l-6.34 10.89c-.44.74-.15 1.62.44 2.06l6.62 4.7c-.15 1.03-.29 2.06-.29 3.24s.15 2.21.29 3.24l-6.92 4.7c-.6.44-.88 1.33-.44 2.06l6.33 10.9c.44.74 1.18 1.03 1.91.6l7.36-3.39a26.35 26.35 0 0 0 5.59 3.24l.74 8.1c.15.74.74 1.33 1.47 1.33h12.67c.74 0 1.47-.6 1.47-1.33l.74-8.1c2.06-.88 3.97-1.91 5.59-3.24l7.36 3.39c.74.29 1.62 0 1.91-.6l6.33-10.9c.44-.74.15-1.62-.44-2.06zm-22.96 11.48c-8.1 0-14.72-6.62-14.72-14.72s6.62-14.72 14.72-14.72 14.71 6.63 14.71 14.72-6.62 14.72-14.71 14.72z" fill="#e6e6e6" stroke="#4d4d4d" stroke-width="2.5"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill="red" d="M9.7 0c-1 0-2.1.4-3 1.2L1.3 7a4.2 4.2 0 000 5.8L20.6 32 1.3 51.3a4.2 4.2 0 000 5.9l5.6 5.6a4.2 4.2 0 005.9 0L32 43.5l19.2 19.3a4.2 4.2 0 005.9 0l5.6-5.6a4.2 4.2 0 000-5.9L43.5 32l19.2-19.3a4.1 4.1 0 000-5.9l-5.6-5.6a4.2 4.2 0 00-5.8 0L32 20.5 12.6 1.2A4.2 4.2 0 009.7 0z"/></svg>

After

Width:  |  Height:  |  Size: 359 B

BIN
resources/images/expand.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 64 64"><defs><linearGradient xlink:href="#a" id="b" x1="26.9" x2="26.9" y1="17.2" y2="50.6" gradientUnits="userSpaceOnUse"/></defs><linearGradient id="a" x1="26.9" x2="26.9" y1="17.2" y2="50.6" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#59f"/><stop offset="1" stop-color="#2a7fff"/></linearGradient><path fill="url(#b)" stroke="#00002b" stroke-linejoin="round" stroke-width="6" d="M60.8 32a28.8 28.8 0 1 1-57.6 0 28.8 28.8 0 1 1 57.6 0z"/><path fill="url(#a)" stroke="#87aade" stroke-linejoin="round" stroke-width="4" d="M60.8 32a28.8 28.8 0 1 1-57.6 0 28.8 28.8 0 1 1 57.6 0z"/><path fill="none" stroke="#fff" stroke-linejoin="round" stroke-width="4" d="M57.2 32a25.2 25.2 0 0 1-50.4 0A25.2 25.2 0 0 1 32 7a25.2 25.2 0 0 1 25.2 25z"/><g fill="#fff"><path d="M35.6 48h-7.2V26.6h7.2z"/><circle cx="32" cy="19.7" r="3.6"/></g></svg>

After

Width:  |  Height:  |  Size: 944 B

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="115.333px"
height="114px"
viewBox="0 0 115.333 114"
enable-background="new 0 0 115.333 114"
xml:space="preserve"
sodipodi:docname="prometheus_logo_orange.svg"
inkscape:version="0.92.1 r15371"><metadata
id="metadata4495"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs4493" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1484"
inkscape:window-height="886"
id="namedview4491"
showgrid="false"
inkscape:zoom="5.2784901"
inkscape:cx="60.603667"
inkscape:cy="60.329656"
inkscape:window-x="54"
inkscape:window-y="7"
inkscape:window-maximized="0"
inkscape:current-layer="Layer_1" /><g
id="Layer_2" /><path
style="fill:#e6522c;fill-opacity:1"
inkscape:connector-curvature="0"
id="path4486"
d="M 56.667,0.667 C 25.372,0.667 0,26.036 0,57.332 c 0,31.295 25.372,56.666 56.667,56.666 31.295,0 56.666,-25.371 56.666,-56.666 0,-31.296 -25.372,-56.665 -56.666,-56.665 z m 0,106.055 c -8.904,0 -16.123,-5.948 -16.123,-13.283 H 72.79 c 0,7.334 -7.219,13.283 -16.123,13.283 z M 83.297,89.04 H 30.034 V 79.382 H 83.298 V 89.04 Z M 83.106,74.411 H 30.186 C 30.01,74.208 29.83,74.008 29.66,73.802 24.208,67.182 22.924,63.726 21.677,60.204 c -0.021,-0.116 6.611,1.355 11.314,2.413 0,0 2.42,0.56 5.958,1.205 -3.397,-3.982 -5.414,-9.044 -5.414,-14.218 0,-11.359 8.712,-21.285 5.569,-29.308 3.059,0.249 6.331,6.456 6.552,16.161 3.252,-4.494 4.613,-12.701 4.613,-17.733 0,-5.21 3.433,-11.262 6.867,-11.469 -3.061,5.045 0.793,9.37 4.219,20.099 1.285,4.03 1.121,10.812 2.113,15.113 C 63.797,33.534 65.333,20.5 71,16 c -2.5,5.667 0.37,12.758 2.333,16.167 3.167,5.5 5.087,9.667 5.087,17.548 0,5.284 -1.951,10.259 -5.242,14.148 3.742,-0.702 6.326,-1.335 6.326,-1.335 l 12.152,-2.371 c 10e-4,-10e-4 -1.765,7.261 -8.55,14.254 z" /></svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill="#141" stroke="#117" stroke-linejoin="round" stroke-width="5" d="M28.2 3.1v4.6A24.1 24.1 0 0 0 7.4 32a24.1 24.1 0 0 0 23.8 24.4v.1h.2l4.4 4.5v-4.6A24.1 24.1 0 0 0 56.6 32 24.1 24.1 0 0 0 32.8 7.7v-.1h-.2zm0 15.4v7l6.8-7a13.8 13.8 0 0 1 .8 27v-7l-6.9 7a13.8 13.8 0 0 1-11.2-13.6c0-6.5 4.5-12 10.5-13.4z"/><path fill="#59f" d="M31.2 7.5h1.5l-1.5 6.9V18a13.8 13.8 0 0 0-13.5 13.8 13.8 13.8 0 0 0 13.5 13.8l1.5-.2-1.5 10.7A24 24 0 0 1 7.4 32 24.1 24.1 0 0 1 31.2 7.5z"/><path fill="none" stroke="#117" stroke-width="5" d="M35.9 38.6 24.7 49.7l11.2 11.2"/><g fill="#0055d4"><path d="M32.7 56.5h-1.4l1.4-6.9v-3.7a13.8 13.8 0 0 0 0-27.6l-1.5.1 1.5-10.7c13.4.2 24 11.1 23.9 24.4a24.1 24.1 0 0 1-23.9 24.4z"/><path d="M35.8 60.8V38.6L24.6 49.7z"/></g><path fill="none" stroke="#117" stroke-width="5" d="m28.1 25.4 11.2-11.1L28 3"/><path fill="#59f" d="M28.2 3.1v22.3l11.2-11.1z"/></svg>

After

Width:  |  Height:  |  Size: 948 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill="#71c837" d="M55.9 8.6a4.3 4.3 0 00-3 1.3l-31 30.8L11.3 30a4.4 4.4 0 00-6 0l-4 4.2a4.4 4.4 0 000 6L19 57.7a4.4 4.4 0 006 0l37.8-37.9a4.4 4.4 0 000-6l-4-4a4.3 4.3 0 00-3-1.3z"/></svg>

After

Width:  |  Height:  |  Size: 253 B

BIN
resources/images/tile2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

557
resources/prometheus.css Normal file
View File

@ -0,0 +1,557 @@
/* I2P Socks Proxy Plugin Theme by dr|z3d 2022 */
:root {
--a: #3b6bbf;
--active: #f30;
--bevel: inset 0 0 0 1px #d0d0da, 0 0 1px 1px #97a2ce;
--bg: #a4a4cb;
--bg_status: var(--bg_table);
--bg_table: #f2f2ff;
--bodybg: var(--bg) url(images/tile2.png) fixed;
--bodyfont: 10.5pt Droid Sans, Open Sans, Noto Sans, Ubuntu, Segoe UI, Verdana, Helvetica Neue, sans-serif;
--border: 1px solid #6c6c93;
--border_inner: 1px solid #9898b3;
--btn_border: 1px solid #97a2ce;
--btn_hover: linear-gradient(180deg, #ddd, #fff);
--btn_active: var(--btn_hover);
--btn: linear-gradient(180deg, #fff, #e8e8ff);
--btn_shadow_active: var(--highlight), inset 4px 4px 4px rgba(0,0,0,.5);
--container: linear-gradient(90deg, #fff, #efefff, #fff);
--filter_shadow: drop-shadow(0 0 1px var(--hover)) drop-shadow(0 0 2px var(--hover));
--highlight: inset 0 0 0 1px #fff;
--highlight_notice: var(--highlight), inset 0 0 1px 1px #900, 0 0 2px 2px rgba(0,0,0,.2);
--hover: #f60;
--ink: #4f4f63;
--ink_light: #5b5b71;
--ink_status_hover: #292d3d;
--input_text: var(--bg_table);
--input_text_focus: #fff;
--monospaced: Droid Sans Mono, Noto Mono, DejaVu Sans Mono, Lucida Console, monospace;
--outline: 0 0 0 1px rgba(255,96,0,.5);
--rowEven: repeating-linear-gradient(135deg, rgba(252,252,255,.5) 2px, rgba(240, 240, 255, .3) 3px, #fafaff 5px) #f0f0ff;
--rowOdd: repeating-linear-gradient(45deg, rgba(255,255,255,.5) 2px, rgba(220, 220, 255, .3) 3px, #fafaff 5px), #fafaff;
--scrollbar_hover: rgba(16,16,48,.4) var(--bg);
--scrollbar: rgba(16,16,48,.3) rgba(0,0,0,0);
--selected: #77f;
--shadow_table: 0 0 1px 0 #bbc;
--shadow_input: var(--highlight), inset 1px 1px 1px 1px rgba(0,0,0,.6);
--th: linear-gradient(180deg, #fcfcff 50%, #f2f2ff 50%, #efefff);
--th_sub: linear-gradient(180deg, #fdfdff, #f0f0ff);
--txtshadow_title: 0 1px 1px #fff, 0 -1px 1px #e2e2ff, 0 2px 1px #ddf;
--visited: #2c4e8f;
--configure: url(images/configure.svg);
--cross: url(images/cross.svg);
--infohelp: url(images/infohelp.svg);
--socks: url(images/prometheus.svg);
--starting: url(images/starting.svg);
--tick: url(images/tick.svg);
}
@media (prefers-color-scheme: light) {
#status .subtitle + tr, #status .subtitle + tr td {
border-bottom: none !important;
}
}
@media (prefers-color-scheme: dark) {
:root {
--a: #3b6bbf;
--active: #f30;
--bevel: inset 0 0 0 1px #000, 0 0 1px 1px #111, 0 0 0 2px #000;
--bodyfont: 10pt Droid Sans, Open Sans, Noto Sans, Ubuntu, Segoe UI, Verdana, Helvetica Neue, sans-serif;
--bg: #111;
--bg_status: #504a4a;
--bg_table: #222;
--bodybg: var(--bg);
--border: 1px solid #444;
--border_inner: 1px solid #181818;
--btn_border: 1px solid #97a2ce;
--btn_active: linear-gradient(180deg, #333, #444);
--btn_hover: linear-gradient(180deg, #444, #333 50%, #111);
--btn: linear-gradient(180deg, #333, #181818);
--container: #222;
--highlight: inset 0 0 0 1px rgba(255,255,255,.1);
--highlight_notice: var(--highlight), inset 0 0 1px 1px #300, 0 0 2px 2px rgba(0,0,0,.5);
--hover: #f60;
--ink: #ddd;
--ink_light: #eee;
--ink_status_hover: #fff;
--input_text_focus: #333;
--rowEven: #555;
--rowOdd: #444;
--scrollbar_hover: rgba(16,16,48,.4) var(--bg);
--scrollbar: rgba(16,16,48,.3) rgba(0,0,0,0);
--selected: #d30;
--shadow_table: 0 0 0 1px #222, 0 0 0 8px #333;
--th: linear-gradient(180deg, #303030 50%, #222 50%, #111);
--th_sub: linear-gradient(180deg, #333, #222);
--txtshadow_title: 0 0 0 1px #000;
--visited: #2c4e8f;
}
#expand img, #collapse img {
filter: invert(1) opacity(.7);
}
#expand:hover img, #collapse:hover img {
filter: invert(1) opacity(.95) var(--filter_shadow) !important;
}
#status {
border-collapse: separate;
border-spacing: 0;
border: var(--border) !important;
}
thead th, #configtitle {
box-shadow: inset 0 0 0 1px #444;
}
#expand, #collapse {
border-left: var(--border) !important;
}
#container {
padding: 0 !important;
}
}
html, body {
background: var(--bodybg);
}
body {
margin: 10px;
padding: 0;
min-width: 600px;
color: var(--ink);
}
body, table {
font: var(--bodyfont);
}
html {
scrollbar-color: var(--scrollbar);
}
html:hover {
scrollbar-color: var(--scrollbar_hover);
}
::selection {
text-shadow: none;
color: #fff;
background: var(--selected);
}
hr, .hidden {
display: none;
}
a:link, .node a:visited {
color: var(--a);
text-decoration: none;
outline: none;
}
a:visited {
color: var(--visited);
text-decoration: none;
}
a:hover, a:focus {
color: var(--hover);
}
a:active {
color: var(--active);
}
#container {
margin: 15px 5px;
padding: 8px;
max-width: 1920px;
border: var(--border);
box-shadow: var(--bevel);
background: var(--container);
}
code {
padding: 0 2px;
font-family: var(--monospaced);
color: #373;
}
table {
width: 100%;
border-collapse: collapse;
border: var(--border_inner);
background: var(--bg_table);
}
tr {
border: var(--border_inner);
}
#configtitle {
font-size: 13pt;
}
.subtitle th {
padding: 6px 8px;
letter-spacing: normal;
text-transform: none;
font-size: 10.5pt;
background: var(--th_sub);
}
#status {
border: var(--border_inner);
}
#status th, #status td {
text-align: center;
}
#status th, #status td {
width: 20%;
}
#status td {
padding: 9px 8px;
border: var(--border_inner);
background: var(--bg_status);
}
#status td:first-child {
text-transform: lowercase;
}
#status td:first-child::first-letter {
text-transform: uppercase;
}
#status td::before {
display: none;
}
#starting, #running, #registered, #notregistered {
vertical-align: middle;
font-size: 0;
}
#starting::before, #running::before, #registered::before, #notregistered::before {
width: 20px;
height: 20px;
display: inline-block;
vertical-align: middle;
background: var(--starting) no-repeat center center / 22px;
content: "";
animation: spin linear 3s forwards infinite;
}
#running::before, #registered::before {
background: var(--tick) no-repeat center center / 16px;
animation: none;
}
#notregistered::before {
background: var(--cross) no-repeat center center / 16px;
animation: none;
}
input.accept {
padding: 5px 9px 2px 26px;
background: var(--tick) no-repeat 9px center / 16px;
}
@keyframes spin {
from {transform: rotate(0)}
to {transform: rotate(360deg)}
}
table table tr:not(.stream):nth-child(odd), #configuration tr:not(.stream):nth-child(odd) {
background: var(--rowOdd);
}
table table tr:not(.stream):nth-child(even), #configuration tr:not(.stream):nth-child(even) {
background: var(--rowEven);
}
td {
padding: 8px;
}
table table td::before {
min-height: 20px;
display: inline-block;
vertical-align: middle;
content: "";
}
table table td, #configuration table td {
padding: 5px 8px;
}
th, #configtitle {
padding: 6px 8px;
text-align: left;
letter-spacing: .08em;
text-transform: uppercase;
font-size: 11pt;
background: var(--th);
}
#title {
padding: 10px 8px 10px 44px;
font-size: 16pt;
letter-spacing: .08em !important;
line-height: 1.1;
text-shadow: var(--txtshadow_title);
background: var(--socks) no-repeat 6px center / 32px, var(--th);
}
#configsection, #configsection td {
border: none;
}
#configsection td {
padding: 0;
}
#configsection table td {
padding: 10px;
vertical-align: text-bottom;
}
#configuration {
position: relative;
border: none;
}
#configuration tr {
border-left: none !important;
border-right: none !important;
}
#configuration td:first-child {
border-left: none !important;
}
#configuration td:last-child {
border-right: none !important;
}
#configuration tr:last-child, #configuration tr:last-child td {
border-bottom: none !important;
}
#configuration th {
width: 25%;
letter-spacing: normal;
text-transform: none;
}
#configuration td:first-child {
border-left: none !important;
}
#configuration td:last-child {
border-right: none !important;
line-height: 1.1;
}
#formaction td {
padding: 12px !important;
background: var(--th_sub);
}
#configtitle {
margin: -1px 0;
padding: 8px 8px 8px 32px;
width: 100%;
display: inline-block;
position: relative;
z-index: 99;
box-sizing: border-box;
text-transform: uppercase;
letter-spacing: .05em;
font-size: 12.5pt;
font-weight: bold;
border: var(--border_inner);
border-left: none;
border-right: none;
background: var(--configure) no-repeat 8px center / 18px, var(--th_sub) !important;
contain: paint;
}
#expand, #collapse {
display: inline-block !important;
position: absolute;
top: calc(50% - 18px);
right: -1px;
font-size: 0;
border-left: var(--border_inner);
}
#collapse {
display: none !important;
}
#expand img, #collapse img {
padding: 15px;
width: 8px;
height: 8px;
display: inline-block;
vertical-align: middle;
transform: scaleX(.6);
}
#collapse img {
transform: scaleY(.6);
}
#expand:hover img, #collapse:hover img {
filter: var(--filter_shadow);
transform: scaleY(1.2) scaleX(.8);
}
#collapse:hover img {
transform: scaleX(1.2) scaleY(.8);
}
#expand::before, #collapse::before {
content: "";
display: inline-block;
height: 100%;
width: 100%;
background: #f00;
}
#configuration td:nth-child(2) code {
line-height: 1.15;
color: #050;
}
#configuration td:nth-child(2) code, #configuration td:nth-child(2) .nowrap {
display: inline-block;
vertical-align: middle;
}
.nowrap {
display: inline-block;
vertical-align: middle;
white-space: nowrap;
}
#configuration .nowrap {
font-style: italic;
color: var(--ink);
}
#configuration i {
margin-right: 1px;
}
#configuration tr {
border-top: var(--border_inner);
border-bottom: var(--border_inner);
}
td, #configuration td {
border: var(--border_inner);
border-left-style: solid;
border-right-style: solid;
box-shadow: var(--highlight) !important;
}
td:first-child {
border-left: var(--border_inner) !important;
}
td:last-child {
border-right: var(--border_inner) !important;
}
#configuration td:first-child {
text-align: right;
}
#formaction {
background: var(--th);
}
input[type=text], input[type=password], input[type=submit] {
font: var(--bodyfont);
border: var(--border_inner);
}
input[type=text], input[type=password] {
padding: 7px 10px;
color: var(--ink_lighter);
background: var(--bg_table);
box-shadow: var(--shadow_input);
}
input[type=text]:focus, input[type=password]:focus {
color: var(--ink);
background: var(--bg_table);
box-shadow: var(--btn_shadow_active), 0 0 0 1px rgba(255,96,0,.7);
outline: none;
}
input[type=submit] {
padding: 7px 14px 7px 30px;
color: var(--ink);
border-radius: 4px;
box-shadow: var(--highlight);
background: var(--tick) no-repeat 12px center / 12px, var(--btn);
cursor: pointer;
}
input[type=submit]:hover {
color: var(--ink_lighter);
background: var(--tick) no-repeat 12px center / 12px, var(--btn_hover);
box-shadow: var(--highlight), var(--outline);
}
input[type=submit]:active {
box-shadow: var(--btn_shadow_active), var(--outline);
background: var(--tick) no-repeat 12px center / 12px, var(--btn_active);
}
#message {
padding: 45px 15px 20px;
width: 400px;
display: inline-block;
position: absolute;
top: 0;
right: -1000px;
text-align: center;
border: var(--border);
box-shadow: var(--highlight_notice);
background: var(--infohelp) no-repeat center 22px / 24px, var(--th_sub);
animation: slideLeft .75s ease-in .1s both, toast 2s ease 4s both;
filter: opacity(0);
}
#message td {
border: none !important;
box-shadow: none !important;
background: none;
}
@keyframes toast{
from {top: 0;}
to {top: -1000px}
}
@keyframes slideLeft {
from {top: 0; right: -800px; filter: opacity(0)}
to {top: 0; right: 0; filter: opacity(1)}
}
@media screen and (max-width: 1000px) {
body {margin: 3px}
body, table {font-size: 9.5pt}
#container {margin: 0}
#title {font-size: 14pt}
#configtitle {font-size: 12pt}
.subtitle th {font-size: 10pt}
#refresh {margin-top: 0}
}

60
resources/toggleConfig.js Normal file
View File

@ -0,0 +1,60 @@
var expandConfig = null;
var collapseConfig = null;
var config = null;
function initToggleConfig()
{
expandConfig = document.getElementById("expandConfig");
collapseConfig = document.getElementById("collapseConfig");
config = document.getElementById("configuration");
hideConfig();
}
function hideConfig() {
if (!collapseConfig)
config.style.display = "none";
}
function showConfig() {
if (collapseConfig)
config.style.display = "block";
}
function clean() {
if (expandConfig) {
expandConfig.remove();
}
if (collapseConfig) {
collapseConfig.remove();
}
}
function expand() {
clean();
var x = document.createElement("link");
x.type="text/css";
x.rel="stylesheet";
x.href="/prometheus/resources/expand.css";
x.setAttribute("id", "expandConfig");
document.head.appendChild(x);
showConfig();
}
function collapse() {
clean();
var c = document.createElement("link");
c.type="text/css";
c.rel="stylesheet";
c.href="/prometheus/resources/collapse.css";
c.setAttribute("id", "collapseConfig");
document.head.appendChild(c);
hideConfig();
}
function copyText() {
document.execCommand("copy");
}
document.addEventListener("DOMContentLoaded", function() {
initToggleConfig();
}, true);

130
scripts/makeplugin.sh Executable file
View File

@ -0,0 +1,130 @@
#!/bin/sh
#
# basic packaging up of a plugin
#
# usage: makeplugin.sh plugindir
#
# zzz 2010-02
# zzz 2014-08 added support for su3 files
#
if [ -z "$I2P" -a -d "$PWD/../i2p.i2p/pkg-temp" ]; then
export I2P=../i2p.i2p/pkg-temp
fi
if [ ! -d "$I2P" ]; then
echo "Can't locate your I2P installation. Please add a environment variable named I2P with the path to the folder as value"
echo "On OSX this solved with running: export I2P=/Applications/i2p if default install directory is used."
exit 1
fi
PUBKEYDIR=$HOME/.i2p-plugin-keys
PUBKEYFILE=$PUBKEYDIR/plugin-public-signing.key
PRIVKEYFILE=$PUBKEYDIR/plugin-private-signing.key
B64KEYFILE=$PUBKEYDIR/plugin-public-signing.txt
PUBKEYSTORE=$PUBKEYDIR/plugin-su3-public-signing.crt
PRIVKEYSTORE=$PUBKEYDIR/plugin-su3-keystore.ks
KEYTYPE=RSA_SHA512_4096
PLUGINDIR=${1:-plugin}
PC=plugin.config
PCT=${PC}.tmp
if [ ! -d $PLUGINDIR ]
then
echo "You must have a $PLUGINDIR directory"
exit 1
fi
if [ ! -f $PLUGINDIR/$PC ]
then
echo "You must have a $PLUGINDIR/$PC file"
exit 1
fi
SIGNER=`grep '^signer=' $PLUGINDIR/$PC`
if [ "$?" -ne "0" ]
then
echo "You must have a plugin name in $PC"
echo 'For example name=foo'
exit 1
fi
SIGNER=`echo $SIGNER | cut -f 2 -d '='`
if [ ! -f $PRIVKEYSTORE ]
then
echo "Creating new SU3 $KEYTYPE keys for $SIGNER"
java -cp $I2P/lib/i2p.jar net.i2p.crypto.SU3File keygen -t $KEYTYPE $PUBKEYSTORE $PRIVKEYSTORE $SIGNER || exit 1
echo '*** Save your password in a safe place!!! ***'
rm -rf logs/
# copy to the router dir so verify will work
CDIR=$I2P/certificates/plugin
mkdir -p $CDIR || exit 1
CFILE=$CDIR/`echo $SIGNER | sed s/@/_at_/`.crt
cp $PUBKEYSTORE $CFILE
chmod 444 $PUBKEYSTORE
chmod 400 $PRIVKEYSTORE
chmod 644 $CFILE
echo "Created new SU3 keys: $PUBKEYSTORE $PRIVKEYSTORE"
echo "Copied public key to $CFILE for testing"
fi
rm -f plugin.zip
OPWD=$PWD
cd $PLUGINDIR
grep -q '^name=' $PC
if [ "$?" -ne "0" ]
then
echo "You must have a plugin name in $PC"
echo 'For example name=foo'
exit 1
fi
grep -q '^version=' $PC
if [ "$?" -ne "0" ]
then
echo "You must have a version in $PC"
echo 'For example version=0.1.2'
exit 1
fi
# update the date
grep -v '^date=' $PC > $PCT
DATE=`date '+%s000'`
echo "date=$DATE" >> $PCT
mv $PCT $PC || exit 1
# add our Base64 key
grep -v '^key=' $PC > $PCT
B64KEY=`cat $B64KEYFILE`
echo "key=$B64KEY" >> $PCT || exit 1
mv $PCT $PC || exit 1
# zip it
zip -r $OPWD/plugin.zip * || exit 1
# get the version and use it for the sud header
VERSION=`grep '^version=' $PC | cut -f 2 -d '='`
# get the name and use it for the file name
NAME=`grep '^name=' $PC | cut -f 2 -d '='`
SU3=${NAME}.su3
cd $OPWD
# sign it
echo 'Signing. ...'
java -cp $I2P/lib/i2p.jar net.i2p.crypto.SU3File sign -c PLUGIN -t $KEYTYPE plugin.zip $SU3 $PRIVKEYSTORE $VERSION $SIGNER || exit 1
rm -f plugin.zip
# verify
echo "Verifying with $PUBKEYSTORE ..."
java -cp $I2P/lib/i2p.jar net.i2p.crypto.SU3File showversion $SU3 || exit 1
java -cp $I2P/lib/i2p.jar net.i2p.crypto.SU3File verifysig -k $PUBKEYSTORE $SU3 || exit 1
rm -rf logs/
echo 'Plugin files created: '
wc -c $SU3
exit 0

10
scripts/plugin.config Normal file
View File

@ -0,0 +1,10 @@
name=prometheus
consoleLinkName=Prometheus Metrics
consoleLinkURL=/prometheus/status.html
console-icon=/resources/images/prometheus.svg
signer=zzz-plugin@mail.i2p
description=Prometheus Metrics Server
author=zzz
updateURL.su3=http://stats.i2p/i2p/plugins/prometheus-update.su3
websiteURL=http://zzz.i2p/forums/16
license=Apache 2.0

77
src/build.xml Normal file
View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<project basedir="." default="all" name="prometheus">
<property name="i2pbase" value="../../i2p.i2p"/>
<property name="i2plib" value="${i2pbase}/build"/>
<property name="jettylib" value="${i2pbase}/apps/jetty/jettylib"/>
<path id="cp">
<pathelement path="${java.class.path}" />
<pathelement location="${i2plib}/i2p.jar" />
<pathelement location="${jettylib}/jasper-runtime.jar" />
<pathelement location="${jettylib}/javax.servlet.jar" />
<pathelement location="${jettylib}/jetty-util.jar" />
</path>
<target name="all" depends="clean, build" />
<target name="build" depends="war" />
<target name="builddep">
</target>
<property name="prom.version" value="1.3.6" />
<property name="javac.compilerargs" value="" />
<property name="javac.version" value="1.8" />
<target name="jar" >
<jar destfile="build/prometheus.jar" >
<zipfileset src="../lib/prometheus-metrics-config-${prom.version}.jar" excludes="META-INF META-INF/**/*" />
<zipfileset src="../lib/prometheus-metrics-core-${prom.version}.jar" excludes="META-INF META-INF/**/*" />
<zipfileset src="../lib/prometheus-metrics-exporter-common-${prom.version}.jar" excludes="META-INF META-INF/**/*" />
<zipfileset src="../lib/prometheus-metrics-exporter-servlet-javax-${prom.version}.jar" excludes="META-INF META-INF/**/*" />
<zipfileset src="../lib/prometheus-metrics-exposition-textformats-${prom.version}.jar" excludes="META-INF META-INF/**/*" />
<zipfileset src="../lib/prometheus-metrics-instrumentation-jvm-${prom.version}.jar" excludes="META-INF META-INF/**/*" />
<zipfileset src="../lib/prometheus-metrics-model-${prom.version}.jar" excludes="META-INF META-INF/**/*" />
<zipfileset src="../lib/prometheus-metrics-tracer-common-${prom.version}.jar" excludes="META-INF META-INF/**/*" />
<zipfileset src="../lib/prometheus-metrics-tracer-initializer-${prom.version}.jar" excludes="META-INF META-INF/**/*" />
</jar>
</target>
<target name="compile" depends="jar" >
<mkdir dir="./build" />
<mkdir dir="./build/obj" />
<javac
srcdir="./java"
debug="true" deprecation="on" source="${javac.version}" target="${javac.version}"
destdir="./build/obj"
includeAntRuntime="false"
classpath="${i2plib}/i2p.jar:${jettylib}/javax.servlet.jar:build/prometheus.jar" >
<compilerarg line="${javac.compilerargs}" />
</javac>
</target>
<target name="precompilejsp" depends="compile" >
<mkdir dir="build" />
<mkdir dir="build/war/WEB-INF/classes" />
<copy file="jsp/WEB-INF/web.xml" tofile="build/web.xml" overwrite="true" />
</target>
<target name="war" depends="precompilejsp">
<!-- add css and resources -->
<copy todir="build/war/resources" overwrite="true" >
<fileset dir="../resources" />
</copy>
<war destfile="build/prometheus.war" webxml="build/web.xml">
<classes dir="./build/obj" />
<fileset dir="./build/war" />
<zipfileset src="build/prometheus.jar" excludes="META-INF META-INF/**/*" prefix="WEB-INF/classes/" />
</war>
</target>
<target name="clean">
<delete dir="./build" />
</target>
<target name="cleandep" depends="clean">
</target>
<target name="distclean" depends="clean">
</target>
</project>

View File

@ -0,0 +1,175 @@
package net.i2p.prometheus;
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import java.net.Socket;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import io.prometheus.metrics.core.metrics.GaugeWithCallback;
import io.prometheus.metrics.instrumentation.jvm.JvmMetrics;
import io.prometheus.metrics.model.registry.PrometheusRegistry;
import net.i2p.I2PAppContext;
import net.i2p.app.*;
import static net.i2p.app.ClientAppState.*;
import net.i2p.data.DataHelper;
import net.i2p.stat.Rate;
import net.i2p.stat.RateStat;
import net.i2p.stat.StatManager;
import net.i2p.util.Log;
/**
*
* @author zzz
*/
public class PromManager implements ClientApp {
private final I2PAppContext _context;
private final Log _log;
private final ClientAppManager _mgr;
private int i2pCount, jvmCount;
private ClientAppState _state = UNINITIALIZED;
public PromManager(I2PAppContext ctx, ClientAppManager mgr, String args[]) {
_context = ctx;
_log = ctx.logManager().getLog(PromManager.class);
_mgr = mgr;
_state = INITIALIZED;
}
public int getJVMCount() { return jvmCount; }
public int getI2PCount() { return i2pCount; }
/**
* Not supported
*/
public synchronized static void main(String args[]) {
throw new UnsupportedOperationException("Must use ClientApp interface");
}
/**
* This adds the stats present at plugin startup.
* TODO: add a monitor to add stats that appear later.
*/
private void addStats() {
StatManager sm = _context.statManager();
Map<String, SortedSet<String>> groups = sm.getStatsByGroup();
int n = 0;
for (Map.Entry<String, SortedSet<String>> e : groups.entrySet()) {
//String pfx = "i2p." + e.getKey() + '.';
String pfx = "i2p.";
for (String s : e.getValue()) {
RateStat rs = sm.getRate(s);
if (rs == null)
continue;
String desc = rs.getDescription();
if (desc == null)
desc = "";
long[] pers = rs.getPeriods();
final long per = pers[0];
final Rate rate = rs.getRate(per);
if (rate == null)
continue;
String name = pfx + s + '.' + DataHelper.formatDuration(per);
name = name.replace(".", "_");
name = name.replace("-", "_");
// https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels
// Prevent IllegalArgumentExceptions
if (name.replaceAll("[a-zA-Z0-9_]", "").length() != 0) {
if (_log.shouldWarn())
_log.warn("skipping stat with illegal chars: " + name);
continue;
}
if (_log.shouldDebug())
_log.debug("adding gauge " + name);
GaugeWithCallback.builder()
.name(name)
.help(desc)
.labelNames("state")
.callback(callback -> {
callback.call(rate.getAvgOrLifetimeAvg(), "average");
})
.register();
n++;
}
}
i2pCount = n;
if (_log.shouldDebug())
_log.info(n + " PromManager I2P metrics registered");
}
/////// ClientApp methods
public synchronized void startup() throws Exception {
if (_state != STOPPED && _state != INITIALIZED && _state != START_FAILED) {
_log.error("Start while state = " + _state);
return;
}
_log.info("PromManager startup");
JvmMetrics.builder().register();
jvmCount = PrometheusRegistry.defaultRegistry.scrape().size();
if (_log.shouldInfo())
_log.info(jvmCount + " PromManager JVM metrics registered");
addStats();
changeState(RUNNING);
_mgr.register(this);
}
public synchronized void shutdown(String[] args) {
_log.warn("PromManager shutdown");
if (_state == STOPPED)
return;
changeState(STOPPING);
// clear() supported as of v1.3.2
PrometheusRegistry.defaultRegistry.clear();
_mgr.unregister(this);
changeState(STOPPED);
}
public ClientAppState getState() {
return _state;
}
public String getName() {
return "prometheus";
}
public String getDisplayName() {
return "PromManager Metrics";
}
/////// end ClientApp methods
private synchronized void changeState(ClientAppState state) {
if (state == _state)
return;
_state = state;
_mgr.notify(this, state, null, null);
}
private synchronized void changeState(ClientAppState state, String msg, Exception e) {
if (state == _state)
return;
_state = state;
_mgr.notify(this, state, msg, e);
}
}

View File

@ -0,0 +1,151 @@
// ========================================================================
// Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
package net.i2p.prometheus.web;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Enumeration;
import java.util.List;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper;
import net.i2p.util.ByteCache;
import net.i2p.util.Log;
import net.i2p.util.SystemVersion;
/* ------------------------------------------------------------ */
/**
* Based on DefaultServlet from Jetty 6.1.26, heavily simplified
* and modified to remove all dependencies on Jetty libs.
*
* Supports HEAD and GET only, for resources from the .war and local files.
* Supports files and resource only.
* Supports MIME types with local overrides and additions.
* Supports Last-Modified.
* Supports single request ranges.
*
* Does not support directories or "welcome files".
* Does not support gzip.
* Does not support multiple request ranges.
* Does not cache.
*
* POST returns 405.
* Directories return 403.
* Jar resources are sent with a long cache directive.
*
* ------------------------------------------------------------
*
* The default servlet.
* This servlet, normally mapped to /, provides the handling for static
* content, OPTION and TRACE methods for the context.
* The following initParameters are supported, these can be set
* on the servlet itself:
* <PRE>
*
* resourceBase Set to replace the context resource base
* warBase Path allowed for resource in war
*
* </PRE>
*
*
* @author Greg Wilkins (gregw)
* @author Nigel Canonizado
*
* @since Jetty 7
*/
class BasicServlet extends HttpServlet
{
protected final I2PAppContext _context;
protected final Log _log;
protected File _resourceBase;
private String _warBase;
/** same as PeerState.PARTSIZE */
private static final int BUFSIZE = 16*1024;
private ByteCache _cache = ByteCache.getInstance(16, BUFSIZE);
private static final int WAR_CACHE_CONTROL_SECS = 24*60*60;
private static final int FILE_CACHE_CONTROL_SECS = 24*60*60;
public BasicServlet() {
super();
_context = I2PAppContext.getGlobalContext();
_log = _context.logManager().getLog(getClass());
}
/* ------------------------------------------------------------ */
public void init(ServletConfig cfg) throws ServletException {
super.init(cfg);
}
/* ------------------------------------------------------------ */
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.sendError(405);
}
/* ------------------------------------------------------------ */
/* (non-Javadoc)
* @see javax.servlet.http.HttpServlet#doTrace(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doTrace(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.sendError(405);
}
protected void doOptions(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.sendError(405);
}
protected void doDelete(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
response.sendError(405);
}
/**
* Simple version of URIUtil.addPaths()
* @param path may be null
*/
protected static String addPaths(String base, String path) {
if (path == null)
return base;
String rv = (new File(base, path)).toString();
if (SystemVersion.isWindows())
rv = rv.replace("\\", "/");
return rv;
}
}

View File

@ -0,0 +1,254 @@
package net.i2p.prometheus.web;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Properties;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.i2p.app.ClientAppManager;
import net.i2p.app.ClientAppState;
import net.i2p.data.DataHelper;
import net.i2p.prometheus.PromManager;
import net.i2p.util.I2PAppThread;
import net.i2p.util.PortMapper;
import net.i2p.util.Translate;
import net.i2p.I2PAppContext;
/**
* From socksoutproxy
*/
public class PrometheusServlet extends BasicServlet {
private String _contextPath;
private String _contextName;
private volatile PromManager _manager;
private volatile boolean _isRunning;
private static long _nonce;
private static final String DEFAULT_NAME = "prometheus";
private static final String DOCTYPE = "<!DOCTYPE HTML>\n";
private static final String FOOTER = "</table>\n</div>\n<span id=\"endOfPage\" data-iframe-height></span>\n</body>\n</html>";
// for now, use console bundle, hope to pick up a few translations for free
private static final String BUNDLE = "net.i2p.router.web.messages";
private static final String RESOURCES = "/prometheus/resources/";
private static final String VERSION = "0.1";
public PrometheusServlet() {
super();
}
@Override
public void init(ServletConfig cfg) throws ServletException {
super.init(cfg);
String cpath = getServletContext().getContextPath();
_contextPath = cpath == "" ? "/" : cpath;
_contextName = cpath == "" ? DEFAULT_NAME : cpath.substring(1).replace("/", "_");
_nonce = _context.random().nextLong();
_isRunning = true;
(new Starter()).start();
}
/**
* Wait for the ClientAppManager
*/
private class Starter extends I2PAppThread {
public Starter() {
super("Prometheus Starter");
}
public void run() {
try {
run2();
} catch (Throwable t) {
// class problems, old router version, ...
_log.error("Unable to start Prometheus", t);
_isRunning = false;
}
}
private void run2() throws Exception {
File f = new File(_context.getConfigDir(), "plugins");
f = new File(f, _contextName);
String[] args = new String[] { f.toString() };
while (_isRunning) {
ClientAppManager cam = _context.clientAppManager();
if (cam != null) {
_manager = new PromManager(_context, cam, args);
_manager.startup();
break;
} else {
try {
Thread.sleep(10*1000);
} catch (InterruptedException ie) {}
}
}
}
}
@Override
public void destroy() {
_isRunning = false;
if (_manager != null)
_manager.shutdown(null);
super.destroy();
}
/**
* Handle what we can here, calling super.doGet() for the rest.
*/
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGetAndPost(request, response);
}
/**
* Handle what we can here, calling super.doPost() for the rest.
*/
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGetAndPost(request, response);
}
/**
* Handle all here
*/
private void doGetAndPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
PromManager c = _manager;
String method = req.getMethod();
String msg = null;
if (c != null) {
if (c.getState() != ClientAppState.RUNNING) {
try {
c.startup();
msg = "Prometheus started";
} catch (Exception e) {
msg = "Prometheus failure: " + e;
}
}
}
// this is the part after /orchid
String path = req.getServletPath();
resp.setHeader("X-Frame-Options", "SAMEORIGIN");
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html; charset=UTF-8");
PrintWriter out = resp.getWriter();
out.write(DOCTYPE + "<html>\n<head>\n<title>");
out.write(_t("Prometheus Metrics Client"));
out.write("</title>\n");
out.write("<script src=\"" + RESOURCES + "toggleConfig.js\" type=\"application/javascript\"></script>\n");
out.write("<meta http-equiv=\"Content-Security-Policy\" content=\"script-src \'self\' \'unsafe-inline\';\">\n");
out.write("<link rel=\"icon\" href=\"" + RESOURCES + "images/prometheus.svg\">\n");
out.write("<link href=\"" + RESOURCES + "prometheus.css?" + VERSION + "\" rel=\"stylesheet\" type=\"text/css\">\n");
out.write("<noscript><style>.script, #expand, #collapse {display: none !important} #configuration {display: table !important} " +
"*::selection {color: #fff; background: #77f}");
out.write("</style></noscript>\n</head>\n");
out.write("<body id=\"prometheus\">\n<div id=\"container\">\n<table id=\"main\" width=\"100%\">\n" +
"<thead><tr><th id=\"title\" align=\"left\">" + _t("Prometheus Metrics Client") + "</th></tr></thead>\n");
out.write("<tbody>\n<tr><td>\n<hr>\n<table id=\"status\" width=\"100%\">\n<tr class=\"subtitle\">" +
"<th width=\"20%\">" + _t("Status") + "</th>" +
"<th width=\"20%\">" + _t("Registered with I2P") + "</th>" +
"<th width=\"20%\">" + _t("Plugin Version") + "</th>" +
"<th width=\"20%\">" + _t("I2P Metrics") + "</th>" +
"<th width=\"20%\">" + _t("Java Metrics") + "</th>");
out.write("</tr>\n<tr><td align=\"center\">");
if (c != null) {
ClientAppState status = c.getState();
if (status == ClientAppState.RUNNING)
out.write("<span id=\"running\">" + _t("Running") + "</span>");
else if (status == ClientAppState.STARTING)
out.write("<span id=\"starting\">" + _t("Starting") + "...</span>");
else if (status == ClientAppState.START_FAILED)
out.write("<span id=\"notregistered\">" + _t("Start failed") + "</span>");
else
out.write(status.toString());
} else {
out.write(_t("Not initialized"));
}
out.write("</td><td align=\"center\">");
ClientAppManager cam = _context.clientAppManager();
if (c != null && cam != null && cam.getRegisteredApp(DEFAULT_NAME) == c) {
out.write("<span id=\"registered\">" + _t("Yes") + "</span>");
} else {
out.write("<span id=\"notregistered\">" + _t("No") + "</span>");
}
out.write("</td><td align=\"center\">" + VERSION + "</td>\n");
if (c != null)
out.write("</td><td align=\"center\">" + c.getI2PCount() + "</td>\n");
else
out.write("</td><td align=\"center\">--</td>\n");
if (c != null)
out.write("</td><td align=\"center\">" + c.getJVMCount() + "</td>\n");
else
out.write("</td><td align=\"center\">--</td></tr>\n");
if (msg != null)
out.write("<tr id=\"message\"><td colspan=\"5\" align=\"center\"><b>" + msg + "</b></td></tr>\n");
out.write("</table>\n");
if (c != null) {
out.write("<tr id=\"configsection\"><td>\n<hr>\n<div id=\"configtitle\"><b>Configuration</b>&nbsp;\n" +
"<a class=\"script\" id=\"expand\" href=\"#\" onclick=\"clean();expand();\">" +
"<img alt=\"Expand\" src=\"/prometheus/resources/images/expand.png\" title=\"Expand\"></a>\n" +
"<a class=\"script\" id=\"collapse\" href=\"#\" onclick=\"clean();collapse();\">" +
"<img alt=\"Collapse\" src=\"/prometheus/resources/images/collapse.png\" title=\"Collapse\"></a></div>\n");
out.write(getHTMLConfig(c));
}
out.write(FOOTER);
}
private String getHTMLConfig(PromManager tc) {
StringBuilder buf = new StringBuilder(1024);
buf.append("<hr>\n<div id=\"configuration\" width=\"100%\">\n");
boolean full = _context.getBooleanProperty("stat.full");
if (full)
buf.append("<p>Full stats are enabled.</p>\n");
else
buf.append("<p>For more metrics, <a href=\"/configstats\">enable full stats</a> and restart.</p>\n");
buf.append("<p>Prometheus server configuration - add to /etc/prometheus/prometheus.yml:</p>\n");
int port = _context.portMapper().getPort(PortMapper.SVC_CONSOLE);
if (port <= 0)
port = 7657;
buf.append("<pre>" +
" - job_name: i2p\n" +
" scrape_interval: 60s\n" +
" metrics_path: /prometheus/metrics\n" +
" static_configs:\n" +
" - targets: ['localhost:").append(port).append("']\n" +
"</pre>\n");
buf.append("</div>\n");
return buf.toString();
}
/** translate */
private String _t(String s) {
return Translate.getString(s, _context, BUNDLE);
}
/** translate */
private String _t(String s, Object o) {
return Translate.getString(s, o, _context, BUNDLE);
}
/** translate */
private String _t(String s, Object o, Object o2) {
return Translate.getString(s, o, o2, _context, BUNDLE);
}
/** translate (ngettext) @since 0.7.14 */
private String ngettext(String s, String p, int n) {
return Translate.getString(n, s, p, _context, BUNDLE);
}
/** dummy for tagging */
private static String ngettext(String s, String p) {
return null;
}
}

60
src/jsp/WEB-INF/web.xml Normal file
View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>PrometheusServlet</servlet-name>
<servlet-class>net.i2p.prometheus.web.PrometheusServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>PrometheusMetricsServlet</servlet-name>
<servlet-class>io.prometheus.metrics.exporter.servlet.javax.PrometheusMetricsServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- precompiled servlets -->
<!--
<servlet-mapping>
<servlet-name>PrometheusServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
-->
<servlet-mapping>
<servlet-name>PrometheusServlet</servlet-name>
<url-pattern>/index.jsp</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>PrometheusServlet</servlet-name>
<url-pattern>/index.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>PrometheusServlet</servlet-name>
<url-pattern>/status</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>PrometheusServlet</servlet-name>
<url-pattern>/status.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>PrometheusServlet</servlet-name>
<url-pattern>/status.jsp</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>PrometheusMetricsServlet</servlet-name>
<url-pattern>/metrics</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>PrometheusMetricsServlet</servlet-name>
<url-pattern>/metrics/</url-pattern>
</servlet-mapping>
</web-app>