A lightweight, framework-agnostic runtime profiling library for JVM applications.
Instrument your code with Metrics.start() / Metrics.end(), Metrics.track(), or Metrics.measure() and get a hierarchical Markdown report that pinpoints exactly where your execution time goes — database, parser, mapper, or anything else.
- Zero external dependencies — JDK only, no Spring, Micronaut, or Quarkus required
- Framework-agnostic — works with any JVM backend
- Three instrumentation styles — manual
start/end, try-with-resourcestrack(), and functionalmeasure() - Hierarchical execution tree — nested calls produce an indented breakdown with percentages
- Hotspot detection — flags nodes above a configurable threshold automatically
- Threshold filtering — ignore fast nodes below a configurable duration floor
- Tag support — attach key-value metadata to any node (
table=users,operation=SELECT) - Thread-safe — each thread has its own independent context via
ThreadLocal - Extensible — implement
MetricsReporterto add JSON, HTML, or any custom format
- Java 21+
- Maven 3.8+ (included via Maven Wrapper — no installation needed)
Add metrics-core to your pom.xml:
<dependency>
<groupId>org.aocdev</groupId>
<artifactId>metrics-core</artifactId>
<version>1.0.0</version>
</dependency>Metrics.init(MetricsConfig.builder()
.hotspotThresholdPercent(40.0)
.includeThreadName(true)
.build());Metrics.startRun("import-job");
try (MetricScope ignored = Metrics.track("processUsers")) {
Metrics.measure("dbQuery", Map.of("table", "users"), () -> fetchFromDatabase());
Metrics.measure("parse", () -> parseResponse());
Metrics.measure("mapper", () -> mapToDomain());
}
Metrics.endRun();Metrics.report().writeToFile("report.md");# Execution Report — import-job
## Summary
- **Total time:** 218 ms
## Breakdown
- **processUsers** (193 ms) → 88.4%
- **dbQuery** (120 ms) → 55.0% `table=users`
- **parse** (40 ms) → 18.4%
- **mapper** (31 ms) → 14.3%
## Hotspots
- **processUsers** — 193 ms (88.4% of total) ⚠️
- **dbQuery** — 120 ms (55.0% of total) ⚠️| Method | Description |
|---|---|
Metrics.init(MetricsConfig) |
Replace the global config. Call once at startup. |
Metrics.startRun(String name) |
Begin a named measurement run on the current thread. |
Metrics.endRun() |
End the current run and finalise the root node. |
Metrics.report() |
Return a MetricReport for the current thread. Call after endRun(). |
| Method | Style | Description |
|---|---|---|
Metrics.start(String name) |
Manual | Begin a block. Pair with end(). |
Metrics.start(String name, Map<String,String> tags) |
Manual | Same, with metadata tags. |
Metrics.end() |
Manual | End the most recently started block. |
Metrics.track(String name) |
try-with-resources | Returns MetricScope (AutoCloseable). Calls end() on close. |
Metrics.track(String name, Map<String,String> tags) |
try-with-resources | Same, with tags. |
Metrics.measure(String name, Runnable) |
Functional | Wraps a Runnable. |
Metrics.measure(String name, Map<String,String> tags, Runnable) |
Functional | Same, with tags. |
Metrics.measure(String name, Supplier<T>) |
Functional | Wraps a Supplier<T>, returns the value. |
Metrics.measure(String name, Map<String,String> tags, Supplier<T>) |
Functional | Same, with tags. |
| Method | Description |
|---|---|
report.toMarkdown() |
Renders the report as a Markdown string. |
report.writeToFile(String path) |
Writes the report to a file (UTF-8). Parent directories are created automatically. |
MetricsConfig config = MetricsConfig.builder()
.enabled(true) // false = all methods become no-ops
.thresholdMs(10) // skip nodes faster than 10 ms
.includeThreadName(true) // include thread name in Summary
.hotspotThresholdPercent(50.0) // flag nodes that exceed 50% of total time
.build();| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
true |
Master switch. false makes all calls no-ops with zero overhead. |
thresholdMs |
long |
0 |
Minimum duration (ms) to include a node in the report. 0 = show everything. |
includeThreadName |
boolean |
false |
Prints the thread name in the Summary section. |
hotspotThresholdPercent |
double |
50.0 |
Percentage of total run time above which a node is flagged as a hotspot. |
Implement MetricsReporter to produce any output format:
public class JsonReporter implements MetricsReporter {
@Override
public String generate(MetricNode root, MetricsConfig config) {
// traverse root.getChildren() recursively and build JSON
}
}Pass it to MetricReport directly or contribute it back via a pull request.
jruntime-inspector/
pom.xml # Parent POM (multi-module)
metrics-core/ # Library — published to Maven Central
src/main/java/org/aocdev/
Metrics.java # Main public API
MetricNode.java # Execution tree node
MetricContext.java # Per-thread state (ThreadLocal)
MetricScope.java # AutoCloseable handle for try-with-resources
MetricsConfig.java # Configuration builder
MetricReport.java # toMarkdown() / writeToFile()
reporter/
MetricsReporter.java # Extension interface
MarkdownReporter.java # Built-in Markdown implementation
metrics-example/ # Demo application (not published)
src/main/java/org/aocdev/example/
Main.java # Simulated import-job demo
./mvnw verifyThe demo generates report.md in the working directory:
./mvnw install && ./mvnw -pl metrics-example exec:javaCopyright 2026 aocdev (Albert Ortells)
See NOTICE for attribution details.
See CONTRIBUTING.md for guidelines on how to contribute to this project.