From 68d26f702230f0585c9035b996bc5a2ee82ef354 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 9 Apr 2025 08:09:03 -0400 Subject: [PATCH] add plugin --- build.xml | 68 +++ plugin/console/webapps/META-INF/MANIFEST.MF | 4 + plugin/console/webapps/WEB-INF/web.xml | 17 + resources/collapse.css | 13 + resources/expand.css | 13 + resources/images/collapse.png | Bin 0 -> 275 bytes resources/images/configure.svg | 1 + resources/images/cross.svg | 1 + resources/images/expand.png | Bin 0 -> 281 bytes resources/images/infohelp.svg | 1 + resources/images/prometheus.svg | 50 ++ resources/images/starting.svg | 1 + resources/images/tick.svg | 1 + resources/images/tile2.png | Bin 0 -> 1475 bytes resources/prometheus.css | 557 ++++++++++++++++++ resources/toggleConfig.js | 60 ++ scripts/makeplugin.sh | 130 ++++ scripts/plugin.config | 10 + src/build.xml | 77 +++ src/java/net/i2p/prometheus/PromManager.java | 175 ++++++ .../net/i2p/prometheus/web/BasicServlet.java | 151 +++++ .../i2p/prometheus/web/PrometheusServlet.java | 254 ++++++++ src/jsp/WEB-INF/web.xml | 60 ++ 23 files changed, 1644 insertions(+) create mode 100644 build.xml create mode 100644 plugin/console/webapps/META-INF/MANIFEST.MF create mode 100644 plugin/console/webapps/WEB-INF/web.xml create mode 100644 resources/collapse.css create mode 100644 resources/expand.css create mode 100644 resources/images/collapse.png create mode 100644 resources/images/configure.svg create mode 100644 resources/images/cross.svg create mode 100644 resources/images/expand.png create mode 100644 resources/images/infohelp.svg create mode 100644 resources/images/prometheus.svg create mode 100644 resources/images/starting.svg create mode 100644 resources/images/tick.svg create mode 100644 resources/images/tile2.png create mode 100644 resources/prometheus.css create mode 100644 resources/toggleConfig.js create mode 100755 scripts/makeplugin.sh create mode 100644 scripts/plugin.config create mode 100644 src/build.xml create mode 100644 src/java/net/i2p/prometheus/PromManager.java create mode 100644 src/java/net/i2p/prometheus/web/BasicServlet.java create mode 100644 src/java/net/i2p/prometheus/web/PrometheusServlet.java create mode 100644 src/jsp/WEB-INF/web.xml diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..1cd3b95 --- /dev/null +++ b/build.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugin/console/webapps/META-INF/MANIFEST.MF b/plugin/console/webapps/META-INF/MANIFEST.MF new file mode 100644 index 0000000..d8e4d24 --- /dev/null +++ b/plugin/console/webapps/META-INF/MANIFEST.MF @@ -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) + diff --git a/plugin/console/webapps/WEB-INF/web.xml b/plugin/console/webapps/WEB-INF/web.xml new file mode 100644 index 0000000..93a393c --- /dev/null +++ b/plugin/console/webapps/WEB-INF/web.xml @@ -0,0 +1,17 @@ + + + + + io.prometheus.metrics.exporter.servlet.javax.PrometheusMetricsServlet + io.prometheus.metrics.exporter.servlet.javax.PrometheusMetricsServlet + 1 + + + + + + io.prometheus.metrics.exporter.servlet.javax.PrometheusMetricsServlet + / + + + diff --git a/resources/collapse.css b/resources/collapse.css new file mode 100644 index 0000000..bf5c058 --- /dev/null +++ b/resources/collapse.css @@ -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; +} \ No newline at end of file diff --git a/resources/expand.css b/resources/expand.css new file mode 100644 index 0000000..3acf197 --- /dev/null +++ b/resources/expand.css @@ -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; +} diff --git a/resources/images/collapse.png b/resources/images/collapse.png new file mode 100644 index 0000000000000000000000000000000000000000..6ad65cc3f94f1be21ead3f720ff5f86bc763f820 GIT binary patch literal 275 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~i!3-n?Kj!QJQh@Jl;ThbLP+_1S+vKYwUlSCOBSvZ53VZ$_9ZP*H@Zi(?4K_0Y5KVhsuc zEEnC3W*Fc5zo4td{7>xHB(~R=B@~W1lpb)4j&GQMGT~$48nfvg(=MHPs2(DBrgw?= zS{9@ApHH7UHcutsqrflOBL|t>4{s3Wjnvz>%9E?R=fSP#AZK~H`njxgN@xNAKJ-u+ literal 0 HcmV?d00001 diff --git a/resources/images/configure.svg b/resources/images/configure.svg new file mode 100644 index 0000000..6c51046 --- /dev/null +++ b/resources/images/configure.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/cross.svg b/resources/images/cross.svg new file mode 100644 index 0000000..bd6f23d --- /dev/null +++ b/resources/images/cross.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/expand.png b/resources/images/expand.png new file mode 100644 index 0000000000000000000000000000000000000000..ecf1fa84f7ca608e2b850a6d90ee3265b3736ac3 GIT binary patch literal 281 zcmeAS@N?(olHy`uVBq!ia0vp^d_XL~!3-n?z4mbcsi**-5LY0rsHi9}FE1x2Co3x} zBO@azDJdl-B`PWkWCHelajv^T?iyS$zd)ID@CFpUXO@geCw~ CpiEl; literal 0 HcmV?d00001 diff --git a/resources/images/infohelp.svg b/resources/images/infohelp.svg new file mode 100644 index 0000000..397fb77 --- /dev/null +++ b/resources/images/infohelp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/prometheus.svg b/resources/images/prometheus.svg new file mode 100644 index 0000000..5c51f66 --- /dev/null +++ b/resources/images/prometheus.svg @@ -0,0 +1,50 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/resources/images/starting.svg b/resources/images/starting.svg new file mode 100644 index 0000000..fe184c7 --- /dev/null +++ b/resources/images/starting.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/tick.svg b/resources/images/tick.svg new file mode 100644 index 0000000..fca668d --- /dev/null +++ b/resources/images/tick.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/tile2.png b/resources/images/tile2.png new file mode 100644 index 0000000000000000000000000000000000000000..5da7a46c54f5db60ec0e800dcfcb353698579bb7 GIT binary patch literal 1475 zcmZuxX;4#V6n&D$jYUx~M1zS*iwi|%hzf|xuAvz)6j`DmAXrcq6BMzyumo&i90m3%el+c`%_1 z3??`bhP4a4V8S>6S`g;(9zq^6z`7Gb!=Yj^+SUZ>AEG z98Mox3_h3Kz@ERpWgxk_`84r0KNlsfpsOYR;%^R+1rmGs4|MHlaK8q!*)$#MKc+r=9v@{!=+4mJZDSoedXKwa{FF-M?CEt#s8Rj$%QN95ZQ z6EifwoaE+clr|{4ZBB7A_lTj~T6B68nx8Q0(tGU7>C&_){$&FGZCi3+?$Tt}ML&(D z$7~v&VjjT`M<0H33U_(gmWML}NAyi3>BFATBdU~(_Hy~vkLMKX==JxV`*P#R#usm+ zW~SU7sGHbHXVkSd0+!baqbbrPcL}B;oM6#J*V+)WzS3_81-(8l|8V_T4JP{)KjbA! z;(2E`>JxYCmM>vNzaAR$oM}>LvO^Ut2WMd){y)PNRD&=#y2!cwg{S4l%BZ`T>G^&OIRD(t=99jE*G)58W_AG8 zGFeib?z7d!*ZR%|`G=aFsfeObucl(VV}GaI;IB6Q%@YzEKP)p#!HG;UyA_@7KIAjy zE(xw$>TUCyL9`R`R)(w4rWFR~ujfvx${f`u`XX5jj`{D%gUUSBs_b-P0SSlG|5CLk zs5XVXv8w&S;o&M$%o(eko>5zWY8UY!HV~ammX3QzkbdJbTN~mw6@Pp`If+cNXzSko z@p#u-!xQ6khcuxBmO>_LO>6J81#$&nvTco_8@{@EE-Q^h+wOZ4_qU&T3`-hJOj15k zB$%Y#OS~k{$IQLi7xSR;LL0Z{vX>^>U{Ra7N#rdp%O#9ukXxQsKe4{4qW&)a^YPbT z@hV0v?P=^;<;X;GeS`9AkXgN-pIFeJ5P!c_zM+7z?Hz@TF{%z8A-W7$?quxpCfaqn zBzo-*!QoD)vNszWp&e{z$e{t{TDHZDWjsDMa_2Errm_9>32J_$Q_-pEih-Y7ZWCXb zQdf)VV+YXX9P*7(|89-4)?xN+-K>IHM7Ma8LUeJ+BO`5jePhzm9|@UQ+}1{$i1-eF za~b9NIK^L&vo_1RF!`DhMq0U8FT^s+>z{M+N8{)WiMe(~Sls> $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 diff --git a/scripts/plugin.config b/scripts/plugin.config new file mode 100644 index 0000000..dc5ae84 --- /dev/null +++ b/scripts/plugin.config @@ -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 diff --git a/src/build.xml b/src/build.xml new file mode 100644 index 0000000..d3b0048 --- /dev/null +++ b/src/build.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/java/net/i2p/prometheus/PromManager.java b/src/java/net/i2p/prometheus/PromManager.java new file mode 100644 index 0000000..fd65190 --- /dev/null +++ b/src/java/net/i2p/prometheus/PromManager.java @@ -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> groups = sm.getStatsByGroup(); + int n = 0; + for (Map.Entry> 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); + } +} diff --git a/src/java/net/i2p/prometheus/web/BasicServlet.java b/src/java/net/i2p/prometheus/web/BasicServlet.java new file mode 100644 index 0000000..55324d3 --- /dev/null +++ b/src/java/net/i2p/prometheus/web/BasicServlet.java @@ -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: + *
                                                                      
+ *
+ *  resourceBase      Set to replace the context resource base
+
+ *  warBase      Path allowed for resource in war
+ * 
+ * 
+ * + * + * @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; + } +} diff --git a/src/java/net/i2p/prometheus/web/PrometheusServlet.java b/src/java/net/i2p/prometheus/web/PrometheusServlet.java new file mode 100644 index 0000000..4c638aa --- /dev/null +++ b/src/java/net/i2p/prometheus/web/PrometheusServlet.java @@ -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 = "\n"; + private static final String FOOTER = "\n\n\n\n"; + // 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 + "\n\n"); + out.write(_t("Prometheus Metrics Client")); + out.write("\n"); + out.write("\n"); + out.write("\n"); + out.write("\n"); + out.write("\n"); + out.write("\n\n"); + out.write("\n
\n\n" + + "\n"); + out.write("\n
" + _t("Prometheus Metrics Client") + "
\n
\n\n" + + "" + + "" + + "" + + "" + + ""); + out.write("\n\n"); + if (c != null) + out.write("\n"); + else + out.write("\n"); + if (c != null) + out.write("\n"); + else + out.write("\n"); + if (msg != null) + out.write("\n"); + out.write("
" + _t("Status") + "" + _t("Registered with I2P") + "" + _t("Plugin Version") + "" + _t("I2P Metrics") + "" + _t("Java Metrics") + "
"); + if (c != null) { + ClientAppState status = c.getState(); + if (status == ClientAppState.RUNNING) + out.write("" + _t("Running") + ""); + else if (status == ClientAppState.STARTING) + out.write("" + _t("Starting") + "..."); + else if (status == ClientAppState.START_FAILED) + out.write("" + _t("Start failed") + ""); + else + out.write(status.toString()); + } else { + out.write(_t("Not initialized")); + } + out.write(""); + ClientAppManager cam = _context.clientAppManager(); + if (c != null && cam != null && cam.getRegisteredApp(DEFAULT_NAME) == c) { + out.write("" + _t("Yes") + ""); + } else { + out.write("" + _t("No") + ""); + } + out.write("" + VERSION + "" + c.getI2PCount() + "--" + c.getJVMCount() + "--
" + msg + "
\n"); + if (c != null) { + out.write("
\n
\n
Configuration \n" + + "" + + "\"Expand\"\n" + + "" + + "\"Collapse\"
\n"); + out.write(getHTMLConfig(c)); + } + out.write(FOOTER); + } + + private String getHTMLConfig(PromManager tc) { + StringBuilder buf = new StringBuilder(1024); + buf.append("
\n
\n"); + boolean full = _context.getBooleanProperty("stat.full"); + if (full) + buf.append("

Full stats are enabled.

\n"); + else + buf.append("

For more metrics, enable full stats and restart.

\n"); + buf.append("

Prometheus server configuration - add to /etc/prometheus/prometheus.yml:

\n"); + int port = _context.portMapper().getPort(PortMapper.SVC_CONSOLE); + if (port <= 0) + port = 7657; + buf.append("
" +
+                   "  - job_name: i2p\n" +
+                   "    scrape_interval: 60s\n" +
+                   "    metrics_path: /prometheus/metrics\n" +
+                   "    static_configs:\n" +
+                   "      - targets: ['localhost:").append(port).append("']\n" +
+                   "
\n"); + buf.append("
\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; + } + +} diff --git a/src/jsp/WEB-INF/web.xml b/src/jsp/WEB-INF/web.xml new file mode 100644 index 0000000..9f3283c --- /dev/null +++ b/src/jsp/WEB-INF/web.xml @@ -0,0 +1,60 @@ + + + + + PrometheusServlet + net.i2p.prometheus.web.PrometheusServlet + 1 + + + + PrometheusMetricsServlet + io.prometheus.metrics.exporter.servlet.javax.PrometheusMetricsServlet + 1 + + + + + + + + PrometheusServlet + /index.jsp + + + + PrometheusServlet + /index.html + + + + PrometheusServlet + /status + + + + PrometheusServlet + /status.html + + + + PrometheusServlet + /status.jsp + + + + PrometheusMetricsServlet + /metrics + + + + PrometheusMetricsServlet + /metrics/ + + +