Cartographer is a JVM agent + Maven plugin that instruments every non-synthetic method (including constructors) in classes under a configured package with OpenTelemetry span decorations during test runs. Each JUnit/TestNG test method produces its own trace, letting you visualize the full call graph and relative timing of every method call in a tool like Jaeger.
Primary use case: identify code paths executed during a test run to find areas being revisited unnecessarily — cache misses, redundant computation, unexpected re-initialization, etc.
This tool is intended for development and testing only, not production use.
The Maven plugin injects the Cartographer JVM agent via -javaagent into the test JVM (the same mechanism JaCoCo uses). The agent instruments all matching classes at class-load time using Byte Buddy and records OTel spans. Trace output is written per-test to target/cartographer/ and, optionally, exported live to any OTLP-compatible backend (Jaeger, Grafana, Datadog, etc.).
| Module | Description |
|---|---|
cartographer-agent |
The JVM agent that does the instrumentation and trace export. |
cartographer-maven-plugin |
Maven plugin that wires the agent into your test JVM; includes an automatic setup goal. |
cartographer-intellij-plugin |
IntelliJ IDEA tool window for browsing trace output as an in-IDE waterfall view. |
Run the setup goal directly against your project. It adds a cartographer Maven profile to your pom.xml automatically:
mvn com.antwerkz:cartographer-maven-plugin:setupThen enable the profile when running tests:
mvn test -DcartographerSee Automatic setup for full details and options.
Wrap the plugin in a profile so you can enable it on demand without slowing down every build:
<profiles>
<profile>
<id>cartographer</id>
<activation>
<property><name>cartographer</name></property>
</activation>
<build>
<plugins>
<plugin>
<groupId>com.antwerkz</groupId>
<artifactId>cartographer-maven-plugin</artifactId>
<version>1.0.2</version>
<configuration>
<packages>
<package>com.example</package> <!-- root package(s) to instrument -->
</packages>
</configuration>
<executions>
<execution>
<goals><goal>instrument</goal></goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>@{argLine}</argLine> <!-- @{} is required for late binding -->
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>mvn test -DcartographerTrace files are written to target/cartographer/ — one JSON file per test method (e.g. com.example.MyTest.testFoo.json).
Start Jaeger locally:
docker run -d --name jaeger \
-p 4317:4317 -p 4318:4318 -p 16686:16686 \
jaegertracing/jaeger:latestOpen the UI at http://localhost:16686. Then add endpoint to the plugin configuration to stream traces live:
<configuration>
<packages>
<package>com.example</package>
</packages>
<endpoint>http://localhost:4318</endpoint>
</configuration>Open the Jaeger UI at http://localhost:16686.
The setup goal adds a ready-to-use Cartographer profile to your pom.xml so you don't have to write the XML by hand.
mvn com.antwerkz:cartographer-maven-plugin:setupThe goal:
- Parses your
pom.xmlto detect the packaging type and root source package. - Inserts a
cartographerprofile with theinstrumentgoal and a surefireargLinewired up for late binding. - Warns you to review the inserted values — auto-detection may not be 100% accurate.
The profile is activated by setting the cartographer system property, so normal builds are unaffected.
If you don't supply packages, the goal walks src/main/{java or kotlin}and descends while there is exactly one subdirectory at each level. The deepest such path becomes the default package. For example, a tree with src/main/kotlin/com/example/myapp/ containing service/ and model/ produces com.example.myapp.
If the packaging is pom, or no source tree is found, a comment placeholder is inserted instead — edit it before running the profile.
| Parameter | Default | Description |
|---|---|---|
packages |
(auto-detected) | Comma-separated root package(s) to instrument. Auto-detected from the source tree when omitted. |
pomFile |
${basedir}/pom.xml |
Path to the pom.xml to modify. |
profileId |
cartographer |
ID of the profile to insert (also used as the activation property name). |
endpoint |
(none) | OTLP HTTP endpoint to include in the generated profile, e.g. http://localhost:4318. |
captureArgs |
false |
When true, adds <captureArgs>true</captureArgs> to the generated configuration. |
maxArgLength |
256 |
Included in generated configuration only when non-default. |
The goal is idempotent — re-running it when the profile already exists prints a warning and exits without modifying the file.
# Specify packages explicitly
mvn com.antwerkz:cartographer-maven-plugin:setup -Dcartographer.packages=com.example.myapp
# Use a custom profile ID and include a Jaeger endpoint
mvn com.antwerkz:cartographer-maven-plugin:setup \
-Dcartographer.profileId=tracing \
-Dcartographer.endpoint=http://localhost:4318
# Point at a pom in a different directory
mvn com.antwerkz:cartographer-maven-plugin:setup -Dcartographer.pomFile=/path/to/other/pom.xml| Parameter | Default | Description |
|---|---|---|
packages |
(required) | Root package(s) to instrument. All classes whose name starts with any listed package are included. |
outputDir |
${project.build.directory}/cartographer |
Directory where per-test trace JSON files are written. |
endpoint |
(none) | OTLP HTTP endpoint for live export, e.g. http://localhost:4318. |
captureArgs |
false |
When true, records method argument values as span attributes (arg.0, arg.1, …). |
maxArgLength |
256 |
Maximum length of a captured argument string before truncation. |
skip |
false |
When true (or -Dcartographer.skip), skips instrumentation entirely without error. |
mvn installRequires Java 17+ and Maven 4.