Which code actually ran when this API was called?
Runtime execution attribution for Spring MVC and WebFlux —
recorded per request, answerable in reverse, and checkable in CI.
Quickstart · Why · In CI · How it works · Integration · Demo video · Docs · 한국어
▶ Watch the 2-minute demo
Recorded on v0.2.0: request separation, reverse lookup, WebFlux thread hops and PR comments. For the current dashboard, use the quickstart below.
Important
Reqover 0.4.2 is an early development release. The libraries are on Maven Central as io.github.reqover-labs, and the agent and CLI jars are on GitHub Releases. Reqover is designed for development, QA, and staging — not for running permanently in production.
A test coverage tool (JaCoCo, for example) tells you this:
OrderService.find()— executed ✅
One thing it does not tell you: who executed it. Was it GET /orders/{id}? An admin batch job? Both? Coverage numbers alone cannot say, so you usually end up tracing through the code by hand.
Reqover records the methods observed during each request's adapter scope, kept separate per request. In MVC that scope begins after handler mapping; it is not the entire network round trip or an ordered call trace. That makes the following possible:
| Ordinary coverage tools | Reqover | |
|---|---|---|
| Which code ran | ✅ | ✅ |
See only the code POST /payments ran |
Trace it yourself | ✅ Straight from the report |
List the APIs that reach SharedValidator |
Trace it yourself | ✅ Reverse lookup |
| Name the APIs a pull request's diff affects | Trace it yourself | ✅ reqover impact in CI |
| How many lines/branches of a method ran | ✅ Precise | ❌ Not supported |
Reqover does not replace JaCoCo. JaCoCo answers "how thoroughly is this tested?"; Reqover answers "who executed this code?" They are meant to be used together.
- Change impact — you touched one shared utility and don't know how many APIs go through it
- Choosing QA scope — you see the changed files in a code review and want to narrow down which APIs to re-run, and you would rather have that posted on the pull request than work it out by hand
- Reading unfamiliar code — you joined an undocumented service and want to see how deep one API actually reaches
- Debugging WebFlux — request handling is scattered across threads and the flow is hard to follow
Since 0.4.0 the report is an offline dashboard. Open it from the report endpoint,
from the file the application exports on shutdown, or with reqover render.
- Request diagnostics — HTTP status and the adapter's recorded interval for each retained request, with average, p95 and maximum. Filter to failures or to requests over a threshold, then expand one to see the methods it ran. Details
- Recording comparison — import the summary of an earlier recording and see which endpoints got slower or started failing. It shows the deltas and leaves the verdict to you. Details
- Test drafts — turn an observed request into a reviewed JSON draft or a
disabled GET/HEAD JUnit test. Review it, set an explicit local/QA base URL and
remove
@Disabledyourself before running it in your existing test suite. Details - In CI — the Action analyses an existing recording and keeps one marked
comment per analysis. Set
upload-artifact: "true"to retain the dashboard; upload is off by default. Details
These are what the adapter observed: no method timings, call order, client-side latency or whole-service TPS. Timing statistics cover the retained requests, while the default store's admitted endpoint aggregates preserve recording-wide counts/code within the aggregate limits.
Call GET /orders/{id} and POST /payments against the same application, and the controllers and services each request executed are shown separated by API. SharedValidator, which both requests passed through, appears under both — and methods reached by two or more APIs are highlighted separately. (A signal that changing it affects several places.)
WebFlux switches threads several times while handling a single request. That normally loses the answer to "which request caused this code to run" — Reqover keeps recording it under the same request even after the thread changes.
Code to Endpoint Index is the same data flipped around: for each method, the APIs that executed it are listed. Use it to decide where to look first after changing code. Method names are shown in a readable form like find(long): OrderResponse rather than JVM descriptors.
Use the filter for endpoint, class or method text.
/focuses search when you are not editing a text field, andEscclears the report filter. In 0.4.2, entering/in a draft path or another input no longer steals focus. Descriptors match either spelling, so(J)andlongfind the same method. Without scripting, the static tables remain readable and browser find still works.
Current dashboard screenshots use light mode and actual 0.4.2 sample requests. The comparison baseline alone uses explicitly labeled synthetic timings; see current capture provenance. The application still supports dark mode. Original capture notes describe the archived old table screenshots.
Before wiring Reqover into your own project, we recommend running the demo application first.
You need
- JDK 17 or 21 (check with
java -version) - Git
- One free port (the examples below use 8080)
Warning
The demo report page has no authentication. The scripts below bind to 127.0.0.1 (reachable only from your own machine). Do not expose this port to a network.
git clone https://github.com/reqover-labs/reqover.git
cd reqover
git checkout v0.4.2
./gradlew test
./scripts/run-agent-demo.sh mvc 8080git clone https://github.com/reqover-labs/reqover.git
Set-Location .\reqover
git checkout v0.4.2
# JAVA_HOME must point at JDK 17 or 21
$env:Path = "$env:JAVA_HOME\bin;$env:Path"
.\gradlew.bat test
.\scripts\run-agent-demo.ps1 -App mvc -Port 8080When the script prints an address and waits, open this in your browser:
http://127.0.0.1:8080/reqover/report.html
Open Requests to inspect the recorded request, or API to code to see its endpoint's executed code. With the default accessor-skipping policy:
GET /auto/orders/{id} 2 classes · 3 methods · 1 thread
AutoOrderController io.reqover.example.mvc.auto
AutoOrderService io.reqover.example.mvc.auto
Press Enter in the terminal running the script to shut it down. To print the
JSON and stop without waiting, pass a third argument. This does not save an HTML
file; use the report export settings or the HTML download button to keep one.
./scripts/run-agent-demo.sh mvc 8080 --stop-after-reportFor failure/slow-request examples while the MVC demo is running, use a second terminal. These read-only demo endpoints have a maximum delay of 2000 ms:
curl -s http://127.0.0.1:8080/auto/diagnostics/delay/1200
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/auto/diagnostics/failureRefresh the dashboard: the second request deliberately returns 503. On
PowerShell, use curl.exe. Request guide.
./scripts/run-agent-demo.sh webflux 8081.\scripts\run-agent-demo.ps1 -App webflux -Port 8081This time you should see GET /auto/reactive/orders/{id} together with two or more distinct thread names. That is the evidence that tracking survived the thread hop.
Open http://127.0.0.1:8081/reqover/report.html for this sample.
./scripts/run-impact-demo.sh 8080This records traffic, exports the report to a file when the application shuts down, and then asks which endpoints a change to one demo class would affect. It is the same sequence the CI section describes, in one command.
One dependency brings the adapters, the report, and the Spring wiring:
implementation("io.github.reqover-labs:reqover-spring-boot-starter:0.4.2")Then attach the agent and name the packages to record:
java -javaagent:reqover-agent-0.4.2.jar=include=com.example.orders -jar your-app.jarSee the Spring integration guide for the full property list. If it doesn't work, opening an issue genuinely helps — where people get stuck is the information this project needs most right now.
A report you look at once is worth less than a report that answers a question every time someone opens a pull request. That question is:
I changed these files. Which APIs should be retested?
Reqover answers it because it already knows which endpoints executed which methods. Point it at a diff and the reverse lookup becomes a checklist.
The starter can write the report to a file when the application shuts down, so an integration test run leaves one behind:
reqover.report.export.json-path=build/reqover-report.jsonRun your integration tests with the agent attached, let the application stop
normally, and the file is there. (A process killed with SIGKILL writes
nothing.) Commit that file as a baseline, or keep it as a CI artifact.
git diff --name-only origin/main... \
| java -jar reqover-cli-0.4.2.jar impact --report build/reqover-report.json --changed-files - --format markdown### Reqover — endpoints to retest
**2 endpoints** were observed executing code this change touches.
| Endpoint | Changed code it ran |
| --- | --- |
| `GET /orders/{id}` | `OrderService#find(long): OrderResponse` |
| `POST /payments` | `SharedValidator#validate(String)` |
The example assumes the downloaded CLI JAR is in the current directory;
otherwise use its full path. In a source build, use
reqover-cli/build/libs/reqover-cli-0.4.2.jar. The CLI
also has render (report JSON to a standalone page) and diff (what changed
between two recordings). --fail-on-impact turns the analysis into a gate:
exit code 0 when nothing is affected, 1 when something is, 2 on bad input.
- uses: reqover-labs/reqover/.github/actions/impact@v0.4.2
with:
report: build/reqover-report.json
upload-artifact: "true"
artifact-name: reqover-${{ github.job }}-${{ strategy.job-index || 'single' }}
analysis-name: ${{ github.job }}-${{ strategy.job-index || 'single' }}This belongs after recording and Java setup in a pull_request job. Checkout
needs fetch-depth: 0; the runner needs Java 17+, Bash, Git, curl and Python 3.
Same-repository comments require pull-requests: write; set comment: "false"
without that permission. Fork comments are skipped. Use distinct artifact and
analysis names for multiple invocations; the Action does not launch tests.
Note
Impact analysis can only speak about code it observed running. A file it reports as having no observed coverage may simply not have been exercised by the traffic that produced the report. Treat the output as where to start looking, not as proof that anything else is safe.
Full walkthrough, including a complete workflow file: Impact analysis in CI.
In one sentence: when the application starts, Reqover inserts code that reports "execution passed here", then groups those reports per request.
flowchart LR
A["Spring application"] --> B["At startup, insert reporting<br/>code at method entry"]
B --> C["On execution, emit<br/>a 'passed here' signal"]
C --> D["Find the request<br/>currently being handled"]
D --> E["Store in that request's bucket"]
E --> F["API → code report"]
E --> G["Code → API reverse lookup"]
In a little more detail:
- Inserting the code — Java has an official mechanism (a Java agent) for adjusting classes as an application loads them. Reqover uses it to add a recording call at the entry of methods in the packages you name. Your source code is never modified.
- Linking to the request — in MVC it uses the storage bound to each request; in WebFlux it uses the context Reactor carries along with the request, to answer "which request is this?"
- Building the report — once a request finishes, the records are grouped by API and rendered as JSON and HTML. The HTML opens on its own with no other files.
Design documents: System architecture · Agent E2E Demo
Written plainly. Using a tool with the wrong expectations wastes everyone's time.
- Per-request execution records for Spring MVC and WebFlux
- Offline dashboard, animated observed associations, status/slow filters and request details
- Retained HTTP timing/status summary comparison, separate from the CLI's code diff
- Reviewed JSON drafts and disabled GET/HEAD JUnit exports, without original-input replay
- Automatic recording at method entry (no source changes)
- API → code report, and the code → API reverse lookup
- Reports written to and read back from JSON, so they outlive the JVM
- Changed files → endpoints to retest, as a CLI command and a GitHub Action
- Diffing two recordings
- Spring Boot auto-configuration, and a starter that wires it in one dependency
- An opt-in report endpoint and a shutdown export to a file
- Attribution for units of work that are not HTTP requests, through
UnitScope - A replaceable storage SPI (
CoverageStore) - E2E tests that attach the agent in a separate JVM
- Dependency inventory (SBOM, CycloneDX 1.6)
- It does not know which lines ran. Method granularity only. If you need line and branch precision, use JaCoCo.
- Compiler-generated methods and runtime proxies (Spring CGLIB, Hibernate, Byte Buddy, Mockito) are excluded. Trivial getters, setters, builders and record accessors are skipped by default;
accessors=recordkeeps them for impact recordings.references=recordadditionally observes included interface calls/static-field reads, not the referenced implementation's execution. Requests served by the unmapped catch-all resource handler are excluded. - Records live in memory only. The default snapshot cap is 10,000 (
reqover.mvc.max-snapshots/reqover.webflux.max-snapshots). Separate aggregates preserve counts/code for admitted names across detail eviction, with a 2,000-distinct-name admission limit and 64 thread names per aggregate. A new name beyond that limit can disappear when its snapshots are evicted; raising the snapshot cap does not raise aggregate limits. Restart clears everything.CoverageStoresupports custom retention, but no persistent implementation ships; export a file instead. - Exported details have their own bound. JSON defaults to the newest 100 unit details and reports
omittedRequestDetails; it does not truncate the report's available endpoint aggregates. Live HTML timing summaries use retained HTTP snapshots, while HTML rendered from exported JSON can use only that file's details. For a complete retained timing baseline, export the live dashboard's summary. - Impact analysis is bounded by what was recorded. It matches changed files against code the report observed running. A file it cannot match is reported as unmatched, which means "not seen", not "not affected".
- MVC async sections are not linked automatically. Work handed to a separate thread is not recorded; attribution resumes when request handling returns.
- The WebFlux adapter turns on one JVM-wide setting. (Reactor's automatic context propagation — needed to carry request information across threads.) If you don't want that, disable the adapter entirely with
reqover.webflux.enabled=falsebefore the application starts. - The agent records nothing unless you pass
include=. This default exists to prevent accidentally instrumenting everything. JDK internals and Reqover's own classes cannot be instrumented even with an include. - The report only shows what was actually observed. Absence from the report does not prove a relationship doesn't exist — you may simply not have called that API yet.
- The reverse lookup is a "start looking here" hint. It is not a complete change-impact analysis.
- The demo report page has no authentication. Keep it on
127.0.0.1.
The published method-entry benchmark · 한국어판 measured about 24 ns per entry under its stated setup. This is a dated, narrow measurement, not a full 0.4.2 dashboard/export/reference-probe or production-overhead guarantee. Check its raw samples and excluded costs before applying it to your application.
| Item | Current |
|---|---|
| Version | 0.4.2 |
| JDK required to build | 17 or 21 |
| Bytecode target | Java 17 |
| CI | Ubuntu + Temurin 17 / 21 |
| Spring Boot in samples | 3.5.16 |
| MVC | Implemented + integration tests |
| WebFlux | Implemented + thread-hop integration tests |
| Report formats | JSON, self-contained HTML, Markdown (impact and diff) |
| CI integration | CLI with exit-code gates, GitHub Action |
| Distribution | Maven Central (libraries) and GitHub Release (agent, CLI) |
Knowing what each directory does makes the code much faster to read.
| Directory | What it does |
|---|---|
| reqover-core | Buckets, bounded snapshots and recording-wide aggregates |
| reqover-instrumentation | ASM method-entry and optional reference probes |
| reqover-agent | Standalone -javaagent JAR and recording options |
| reqover-spring-mvc | MVC request lifecycle and attribution |
| reqover-spring-webflux | Reactive request attribution across thread hops |
| reqover-spring-boot-starter | Application wiring, opt-in report endpoint and exports |
| reqover-report | Dashboard, diagnostics, drafts, summaries, code diff and impact |
| reqover-cli | Offline render, diff, impact, version and help |
examples/mvc-sample |
MVC demo application |
examples/webflux-sample |
WebFlux demo application |
docs |
Design, measurement, and decision records |
scripts |
Demo runners, the impact demo, and the SBOM check script |
./gradlew clean test # tests
./gradlew cyclonedxBom # generate the dependency inventoryOn Windows use .\gradlew.bat. The inventory is written to build/reports/bom/reqover.cdx.json, and the copy pinned to the release is at sbom/reqover.cdx.json. Reproduce the known-vulnerability check with:
./scripts/check-sbom-osv.py sbom/reqover.cdx.jsonThis is a small project, so anything helps. The most valuable contribution right now is a report saying "I ran the demo and it didn't work."
Good first steps
- Run the demo and open an issue about whatever broke — include your OS and JDK version, the exact command, and what actually happened
- Point out sentences in the README or
docs/that don't make sense; if it isn't understandable, that is a bug - Try
reqover impacton a real repository and tell us where the file matching got it wrong — that heuristic needs contact with projects we didn't write - Translate a document still marked (Korean)
- Tell us what happened when you wired it into your own Spring project
Fork, branch, confirm ./gradlew clean test passes, and open a pull request against main. For anything large, open an issue first — work thrown away because the direction didn't match is the worst outcome for everyone. Full rules and the PR checklist: Contributing Guide · Code of Conduct
Issues, pull requests, and commit messages are written in English so contributors anywhere can follow the history. Questions in Korean are welcome — just add an English summary.
Caution
Do not report security vulnerabilities in public issues. Use the private reporting process in the Security Policy.
Terms that keep appearing in this project's docs and code
| Term | Meaning |
|---|---|
| Endpoint | One API address, such as GET /orders/{id} |
| Instrument | Inserting recording calls into code so execution can be observed |
| Java agent | The official Java mechanism for adjusting classes as they load |
| ASM | A library for reading and modifying Java class files; used here for instrumentation |
| WebFlux | Spring's reactive web stack; one request may cross several threads |
| Bucket | The record holder for a single request — "the methods this request passed through" |
| SBOM | The inventory of third-party libraries this project uses; used for vulnerability checks |
-
Competition preparation documents (Korean)
Original plans, Phase 0 notes, old video scripts and review records remain as historical material in the index; they are not the current installation guide.
Documents marked (Korean) have not been translated yet. Translations are welcome contributions.
Project files — Getting help · Roadmap · Governance · Contributing · Code of Conduct · Security policy · Changelog
Reqover Lab — building Reqover, initially as an entry for the 2026 Korea Open Source Developer Competition.
Reqover started as a competition entry, but we intend to keep maintaining it past the contest. Issues and pull requests are welcome regardless of the competition timeline.
| Name | GitHub | Area | |
|---|---|---|---|
| TaeHui Kim | @TaeHuiKKIM | TaeHui Kim | Design and MVP implementation: core, instrumentation, agent, report, demos |
| Sangmin Lee | @lsmin3388 | Sangmin Lee | Design and public repository work: build, CI, core hardening, Spring adapters, docs |
Code written for Reqover is licensed under the Apache License 2.0. Third-party licenses are listed in THIRD_PARTY_NOTICES.md.




