Micrometer Plugin
This plugin allows reporting metrics using Micrometer.
Getting Started
The Micrometer plugin is available on Maven Central as a separate artifact, but in order to use the plugin an additional library for reporting to a specific monitoring system is required. In the following description, an example is given to report to Prometheus. Thus, add the following dependencies:
<dependency>
<groupId>io.javalin</groupId>
<artifactId>javalin-micrometer</artifactId>
<version>7.2.0</version>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>${io.micrometer.version}</version>
</dependency>
Create a registry, register the plugin, and provide a route:
public static void main(String[] args) {
PrometheusMeterRegistry prometheusMeterRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
String contentType = "text/plain; version=0.0.4; charset=utf-8";
MicrometerPlugin micrometerPlugin = new MicrometerPlugin(micrometerPluginConfig -> micrometerPluginConfig.registry = prometheusMeterRegistry);
Javalin.create(config -> {
config.registerPlugin(micrometerPlugin);
config.routes.get("/prometheus", ctx -> ctx.contentType(contentType).result(prometheusMeterRegistry.scrape()));
}).start(8080);
}
With this setup, Javalin will report several Jetty-related metrics (for example accessed endpoints and returned status codes) at the specified endpoint, suitable for being ingested by Prometheus.
Provided Metrics
The Micrometer library comes with a number of useful metrics provider, which can be easily added:
import io.micrometer.core.instrument.binder.jvm.*;
import io.micrometer.core.instrument.binder.system.*;
PrometheusMeterRegistry prometheusMeterRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
// add a tag to all reported values to simplify filtering in large installations:
registry.config().commonTags("application", "My-Application");
new ClassLoaderMetrics().bindTo(registry);
new JvmMemoryMetrics().bindTo(registry);
new JvmGcMetrics().bindTo(registry);
new JvmThreadMetrics().bindTo(registry);
new UptimeMetrics().bindTo(registry);
new ProcessorMetrics().bindTo(registry);
new DiskSpaceMetrics(new File(System.getProperty("user.dir"))).bindTo(registry);
MicrometerPlugin micrometerPlugin = new MicrometerPlugin(micrometerPluginConfig -> micrometerPluginConfig.registry = prometheusMeterRegistry);
String contentType = "text/plain; version=0.0.4; charset=utf-8";
Javalin.create(config -> {
config.registerPlugin(micrometerPlugin);
config.routes.get("/prometheus", ctx -> ctx.contentType(contentType).result(prometheusMeterRegistry.scrape()));
}).start(8080);
Custom Meters
It’s also easy to provide custom meters, reporting application-specific values, for example the length of a job-queue, the number of logged in users, or other values. In the following example, just a random number is returned:
import io.micrometer.core.instrument.Gauge;
Gauge
.builder("myapp_random", () -> (int) (Math.random() * 1000))
.description("Random number from My-Application.")
.strongReference(true)
.register(registry);
WebSocket Exception Tagging
To tag exceptions thrown from WebSocket handlers in your Micrometer metrics, delegate to
the static wsExceptionHandler from your own wsException handler. The plugin exposes
it as a companion-object field, and its signature in the source is:
@JvmField
val wsExceptionHandler = WsExceptionHandler<Exception> { e, ctx ->
val simpleName = e.javaClass.simpleName
ctx.attribute(EXCEPTION_HEADER, simpleName.ifBlank { e.javaClass.name })
}
When invoked, it stores the exception’s simple class name (falling back to the fully
qualified name if the simple name is blank) on the WsContext as the
__micrometer_exception_name attribute. By default — i.e. if you never delegate to
this handler — WebSocket exceptions are not tagged in metrics.
- Java
- Kotlin
Javalin.create(config -> {
config.registerPlugin(new MicrometerPlugin(plugin -> plugin.registry = registry));
// Delegate WebSocket exceptions to the Micrometer handler for tagging
config.routes.wsException(Exception.class, (e, ctx) -> {
MicrometerPlugin.wsExceptionHandler.handle(e, ctx);
ctx.closeSession(WsCloseStatus.SERVER_ERROR, e.getMessage());
});
});
Javalin.create { config ->
config.registerPlugin(MicrometerPlugin { it.registry = registry })
// Delegate WebSocket exceptions to the Micrometer handler for tagging
config.routes.wsException(Exception::class.java) { e, ctx ->
MicrometerPlugin.wsExceptionHandler.handle(e, ctx)
ctx.closeSession(WsCloseStatus.SERVER_ERROR, e.message)
}
}
Registering a wsException handler suppresses Javalin’s default behavior of closing the
socket on uncaught exceptions, so close the session yourself as shown above.