More metrics, metrics renaming

- Add units using heuristics
- Scale time unit ms to seconds
- Add rate event counters
- Add frequency event counters
- Add version infos
- build.xml cleanups
This commit is contained in:
zzz
2025-04-10 12:10:20 -04:00
parent d9aca70a53
commit c39fc3a87a
5 changed files with 141 additions and 36 deletions

View File

@ -1,2 +1,11 @@
2025-04-xx [0.2]
- Add units using heuristics
- Scale time unit ms to seconds
- Add rate event counters
- Add frequency event counters
- Add version infos
- build.xml cleanups
- CSS updates courtesy drzed
2025-04-09 [0.1]
- Initial release

View File

@ -11,10 +11,11 @@
<target name="plugin" depends="war">
<!-- get version number -->
<buildnumber file="scripts/build.number" />
<!-- change version in servlet when you change this -->
<!-- change version in PromManager when you change this -->
<property name="release.number" value="0.1" />
<!-- make the update su3 -->
<copy file="CHANGES.txt" todir="plugin/" overwrite="true" />
<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" />
@ -59,6 +60,7 @@
</delete>
<delete file="plugin/plugin.config" />
<delete file="plugin/console/webapps/prometheus.war" />
<delete file="plugin/CHANGES.txt" />
<delete file="plugin/LICENSE.txt" />
<delete file="plugin/README.txt" />
<delete file="prometheus.su3" />

View File

@ -3,13 +3,6 @@
<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" />
@ -43,7 +36,7 @@
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" >
classpath="${i2plib}/i2p.jar:${i2plib}/router.jar:${jettylib}/javax.servlet.jar:build/prometheus.jar" >
<compilerarg line="${javac.compilerargs}" />
</javac>
</target>

View File

@ -15,18 +15,25 @@ package net.i2p.prometheus;
*/
import java.net.Socket;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import io.prometheus.metrics.core.metrics.CounterWithCallback;
import io.prometheus.metrics.core.metrics.GaugeWithCallback;
import io.prometheus.metrics.core.metrics.Info;
import io.prometheus.metrics.instrumentation.jvm.JvmMetrics;
import io.prometheus.metrics.model.snapshots.Unit;
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.router.RouterVersion;
import net.i2p.stat.Frequency;
import net.i2p.stat.FrequencyStat;
import net.i2p.stat.Rate;
import net.i2p.stat.RateStat;
import net.i2p.stat.StatManager;
@ -44,6 +51,8 @@ public class PromManager implements ClientApp {
private ClientAppState _state = UNINITIALIZED;
public static final String VERSION = "0.1";
public PromManager(I2PAppContext ctx, ClientAppManager mgr, String args[]) {
_context = ctx;
_log = ctx.logManager().getLog(PromManager.class);
@ -62,7 +71,7 @@ public class PromManager implements ClientApp {
}
/**
* This adds the stats present at plugin startup.
* This adds the rate and frequency stats present at plugin startup.
* TODO: add a monitor to add stats that appear later.
*/
private void addStats() {
@ -74,18 +83,28 @@ public class PromManager implements ClientApp {
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;
FrequencyStat fs = null;
Rate rate = null;
long per = 0;
String desc;
String name = pfx + s + '.' + DataHelper.formatDuration(per);
if (rs != null) {
desc = rs.getDescription();
if (desc == null)
desc = "";
long[] pers = rs.getPeriods();
per = pers[0];
rate = rs.getRate(per);
if (rate == null)
continue;
} else {
fs = _context.statManager().getFrequency(s);
if (fs == null)
continue;
desc = fs.getDescription();
}
String name = pfx + s;
name = name.replace(".", "_");
name = name.replace("-", "_");
// https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels
@ -96,17 +115,16 @@ public class PromManager implements ClientApp {
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();
if (rate != null) {
if (_log.shouldDebug())
_log.debug("adding gauge " + name);
if (addStat(rate, per, name, desc))
n++;
} else {
if (_log.shouldDebug())
_log.debug("adding counter " + name);
addFreq(fs, name, desc);
}
n++;
}
}
@ -115,6 +133,88 @@ public class PromManager implements ClientApp {
_log.info(n + " PromManager I2P metrics registered");
}
/**
* This adds a single stat.
* It adds bytes or seconds units based on some heuristics.
* If the units cannot be determined, it also adds an event counter.
* @return true if we added an event counter also.
*/
private static boolean addStat(final Rate rate, long period, String name, String desc) {
boolean rv = false;
GaugeWithCallback.Builder gwcb = GaugeWithCallback.builder()
.help(desc)
.labelNames("state");
// heuristics
String nlc = name.toLowerCase(Locale.US);
if (nlc.contains("time") || nlc.contains("delay") || nlc.contains("lag")) {
// All our stats are in ms
gwcb.unit(Unit.SECONDS)
.callback(callback -> {
callback.call(rate.getAvgOrLifetimeAvg() / 1000, "average");
});
} else if (nlc.contains("size") || nlc.contains("memory") ||
nlc.contains("bps") || nlc.contains("bandwidth")) {
gwcb.unit(Unit.BYTES)
.callback(callback -> {
callback.call(rate.getAvgOrLifetimeAvg(), "average");
});
} else {
gwcb.callback(callback -> {
callback.call(rate.getAvgOrLifetimeAvg(), "average");
});
// events
// Add a _count unit
// Prometheus adds _total
CounterWithCallback.builder()
.name(name + "_count")
.help(desc)
.callback(callback -> {
callback.call(rate.getLifetimeEventCount());
})
.register();
rv = true;
}
name += '_' + DataHelper.formatDuration(period);
gwcb.name(name)
.register();
return rv;
}
/**
* This adds a single counter.
*/
private static void addFreq(final FrequencyStat fs, String name, String desc) {
// Add a _count unit
// Prometheus adds _total
CounterWithCallback.builder()
.name(name + "_count")
.help(desc)
.callback(callback -> {
callback.call(fs.getEventCount());
})
.register();
}
/**
* Add some constant "Info" metrics
*/
private static void addInfos() {
addInfo("i2p_info", "I2P info", "version", RouterVersion.FULL_VERSION);
addInfo("i2p_plugin_info", "I2P Plugin Info", "version", VERSION);
}
/**
* Add some constant "Info" metrics
*/
private static void addInfo(String name, String help, String label, String value) {
Info.builder()
.name(name)
.help(help)
.labelNames(label)
.register()
.addLabelValues(value);
}
/////// ClientApp methods
@ -130,6 +230,7 @@ public class PromManager implements ClientApp {
_log.info(jvmCount + " PromManager JVM metrics registered");
addStats();
addInfos();
changeState(RUNNING);
_mgr.register(this);
}

View File

@ -36,7 +36,7 @@ public class PrometheusServlet extends BasicServlet {
// 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";
private static final String VERSION = PromManager.VERSION;
public PrometheusServlet() {
super();
@ -212,7 +212,7 @@ public class PrometheusServlet extends BasicServlet {
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");
buf.append("<p>Prometheus server configuration: add to <code>/etc/prometheus/prometheus.yml</code>:</p>\n");
int port = _context.portMapper().getPort(PortMapper.SVC_CONSOLE);
if (port <= 0)
port = 7657;
@ -223,9 +223,9 @@ public class PrometheusServlet extends BasicServlet {
" static_configs:\n" +
" - targets: ['localhost:").append(port).append("']\n" +
"</pre>\n");
buf.append("<p>For password-protected or SSL consoles, see the file <tt>")
buf.append("<p>For password-protected or SSL consoles, see the file <code>")
.append(_context.getConfigDir())
.append("/plugins/prometheus/README.txt</tt> for instructions.</p>\n");
.append("/plugins/prometheus/README.txt</code> for instructions.</p>\n");
buf.append("</div>\n");
return buf.toString();
}