Getting Started
This guide covers everything you need to install Timer Ninja and start tracking method execution time.
Installation
Add the Timer Ninja Dependency
Gradle:
implementation group: 'io.github.thanglequoc', name: 'timer-ninja', version: '1.3.0'
Maven:
<dependency>
<groupId>io.github.thanglequoc</groupId>
<artifactId>timer-ninja</artifactId>
<version>1.3.0</version>
<scope>compile</scope>
</dependency>
Declare AspectJ Plugin
Gradle — using FreeFair AspectJ Gradle plugin:
plugins {
id "io.freefair.aspectj.post-compile-weaving" version '9.1.0'
}
dependencies {
implementation group: 'io.github.thanglequoc', name: 'timer-ninja', version: '1.3.0'
aspect 'io.github.thanglequoc:timer-ninja:1.3.0'
// Enable this if you want to track methods in test classes
testAspect("io.github.thanglequoc:timer-ninja:1.3.0")
}
Maven — using Forked Mojo’s AspectJ Plugin:
<plugin>
<groupId>dev.aspectj</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.14.1</version>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.25</version>
</dependency>
</dependencies>
<configuration>
<complianceLevel>${java.version}</complianceLevel>
<aspectLibraries>
<aspectLibrary>
<groupId>io.github.thanglequoc</groupId>
<artifactId>timer-ninja</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
Set Up Logging Output
Timer Ninja uses SLF4J for logging. Depending on your project, choose one of the following:
Spring Boot projects — no extra setup needed. Spring Boot ships with Logback, so Timer Ninja output will appear in your logs automatically.
Non-Spring projects with an SLF4J provider — if you already have Logback, Log4j2, or another SLF4J-compatible logging framework, you’re good to go. Just make sure the log level for io.github.thanglequoc.timerninja.TimerNinjaUtil is at least INFO.
No logging framework? — enable System.out output at application startup:
TimerNinjaConfiguration.getInstance().toggleSystemOutLog(true);
This prints trace output directly to the console — useful for quick testing or simple console applications.
Annotation-based Tracking
The @TimerNinjaTracker annotation is the primary way to track method execution time.
Basic Usage
Annotate any method to start tracking:
@TimerNinjaTracker
public void performTask() {
// Your business logic
}
Tracking Constructors
You can also track constructor execution:
@TimerNinjaTracker
public class NotificationService {
public NotificationService() {
// Constructor logic
}
}
Output:
{===== Start of trace context id: abc123... =====}
public NotificationService() - 80 ms
{====== End of trace context id: abc123... ======}
Annotation Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
true |
Enable or disable tracking for this method |
timeUnit |
ChronoUnit |
MILLIS |
Time unit for measurement (SECONDS, MILLIS, MICROS) |
includeArgs |
boolean |
false |
Include method arguments in the log trace |
threshold |
int |
-1 |
Minimum execution time required to log (in specified timeUnit) |
Configuration Options
Enable/Disable Tracking
Temporarily disable tracking for a method without removing the annotation:
@TimerNinjaTracker(enabled = false)
public void dontTrackThis() {
// This will NOT be tracked
}
Time Unit Selection
Choose the appropriate time unit for your measurement:
import java.time.temporal.ChronoUnit;
@TimerNinjaTracker(timeUnit = ChronoUnit.SECONDS)
public void longRunningOperation() { }
@TimerNinjaTracker(timeUnit = ChronoUnit.MILLIS) // default
public void standardOperation() { }
@TimerNinjaTracker(timeUnit = ChronoUnit.MICROS)
public void preciseOperation() { }
Include Method Arguments
Log method arguments for better debugging context:
@TimerNinjaTracker(includeArgs = true)
public void processUser(int userId, String name, String email) {
// Method logic
}
Output:
public void processUser(int userId, String name, String email) - Args: [userId={123}, name={John Doe}, email={john@example.com}] - 42 ms
Important: Ensure your objects have proper
toString()implementations for meaningful output.
Threshold Filtering
Filter out fast methods to focus on performance issues:
@TimerNinjaTracker(threshold = 500) // Only log if execution > 500ms
public void potentiallySlowMethod() {
// Method logic
}
When threshold is exceeded:
public void potentiallySlowMethod() - 723 ms ¤ [Threshold Exceed !!: 500 ms]
When below threshold: The method is suppressed from the trace output. If all methods in a trace are below threshold, a summary is shown instead.
Combining Options
@TimerNinjaTracker(includeArgs = true, threshold = 200)
public void requestMoneyTransfer(int sourceUserId, int targetUserId, int amount) {
// Only logs slow transfers with full argument details
}
Block Tracking
For granular tracking within a method without extracting separate methods, use TimerNinjaBlock.
Basic Block Tracking
public void processData() {
TimerNinjaBlock.measure("database query", () -> {
database.query("SELECT * FROM users");
});
}
Block with Return Value
public void processData() {
String result = TimerNinjaBlock.measure("fetch data", () -> {
return api.fetchUserData();
});
System.out.println(result);
}
Block with Custom Configuration
import java.time.temporal.ChronoUnit;
public void processData() {
BlockTrackerConfig config = new BlockTrackerConfig()
.setTimeUnit(ChronoUnit.SECONDS)
.setThreshold(2);
TimerNinjaBlock.measure("long operation", config, () -> {
performLongRunningTask();
});
}
Nested Block Tracking
public void complexProcess() {
TimerNinjaBlock.measure("overall process", () -> {
loadData();
TimerNinjaBlock.measure("data transformation", () -> {
transformData();
});
saveData();
});
}
Output:
{===== Start of trace context id: ... =====}
[Block] overall process - 1500 ms
|-- [Block] data transformation - 500 ms
{====== End of trace context id: ... ======}
Understanding Trace Output
Trace Structure
Timer Ninja trace context id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Trace timestamp: 2023-04-03T14:27:50.322Z
{===== Start of trace context id: a1b2c3d4-e5f6-7890-abcd-ef1234567890 =====}
public void parentMethod() - 100 ms
|-- public void childMethod() - 50 ms
|-- public void anotherChildMethod() - 30 ms
{====== End of trace context id: a1b2c3d4-e5f6-7890-abcd-ef1234567890 ======}
Elements Explained
- Trace Context ID — Auto-generated UUID. All tracked methods in the same call stack share this ID.
- Trace Timestamp — When the trace context was initiated (UTC timezone).
- Start/End Markers — Delimit the trace boundaries.
- Method Lines — Each tracked method shows: signature, arguments (if enabled), execution time, threshold indicator.
- Indentation (
|--) — Shows call hierarchy. Indented methods are called by the method above.
Summary Output
When all methods in a trace are below their thresholds:
Timer Ninja trace context id: abc123...
Trace timestamp: 2023-04-03T14:27:50.322Z
All 3 tracked items within threshold. min: 5 ms, max: 45 ms, total: 50 ms
Global Configuration
Enable System.out Logging
For simple console applications or quick testing:
TimerNinjaConfiguration.getInstance().toggleSystemOutLog(true);
Call this once at application startup. By default, Timer Ninja uses SLF4J logging.
Log Level
The logger class is io.github.thanglequoc.timerninja.TimerNinjaUtil with default level INFO.
To enable debug information:
<logger name="io.github.thanglequoc.timerninja.TimerNinjaThreadContext" level="DEBUG"/>