Design and Implementation of a Lightweight TimeTracker Utility for Java
This article introduces a concise and elegant Java TimeTracker utility class that leverages AutoCloseable, try‑with‑resources, and functional interfaces to provide flexible performance monitoring, automatic duration calculation, and configurable exception handling while reducing repetitive timing code.
Introduction
Performance monitoring and debugging are common challenges during development. Existing tools can be heavyweight, so a lightweight, flexible, and elegant solution is often needed. Manually recording start and end times leads to repetitive code.
This article presents the design and implementation of a simple yet elegant TimeTracker utility class that satisfies basic performance tracking needs and supports functional interfaces, try‑with‑resources, and multiple invocation mechanisms.
Initial Pain Point
Typical timing code looks like this:
long start = System.currentTimeMillis();
try {
// business logic
} finally {
// calculate elapsed time
}Repeating such boilerplate is verbose and error‑prone.
Evolution: Embracing try‑with‑resources
By implementing AutoCloseable , the timing logic can be wrapped in a try‑with‑resources block:
try (TimeTracker ignored = new TimeTracker("Database operation")) {
// business code, timing handled automatically!
}This makes the code cleaner and ensures automatic resource management.
The TimeTracker class implements AutoCloseable and records the start time in its constructor; the close() method computes and logs the elapsed time.
Pro: Functional Interface
To reduce boilerplate further, a functional interface can be used:
TimeTracker.track("User query", () -> {
return userService.findById(123);
});Even a single‑line lambda works:
TimeTracker.track("Operation", () -> riskyMethod());For methods with return values:
String result = TimeTracker.track("Simple task", () -> {
Thread.sleep(1000);
return "Done";
});Pro Max: Exception Handling
The initial .track() method swallows exceptions and rethrows them as RuntimeException :
public static <T> T track(String operationName, ThrowableSupplier<T> execution) {
try {
return trackThrows(operationName, execution);
} catch (Exception e) {
throw new RuntimeException("Execution failed: " + operationName, e);
}
}For scenarios requiring explicit exception handling, a trackThrows variant is provided:
try {
TimeTracker.trackThrows("Operation", () -> {
return riskyMethod(); // preserve original exception
});
} catch (SpecificException e) {
// precise handling
}Full Source Code
The complete TimeTracker class includes Javadoc, static factory methods, overloads for returning and non‑returning lambdas, and inner functional interfaces ThrowableSupplier and ThrowableRunnable :
/**
* Performance tracking utility class for measuring code execution time with flexible exception handling.
*
*
Key features:
*
*
Accurate timing
*
Support for methods with and without return values
*
Two exception handling modes
*
Automatic resource management
*
*
* @author [Your Name]
* @version 1.0
*/
public class TimeTracker implements AutoCloseable {
private final String operationName;
private final long startTime;
private final boolean logEnabled;
public TimeTracker(String operationName) {
this(operationName, true);
}
private TimeTracker(String operationName, boolean logEnabled) {
this.operationName = operationName;
this.startTime = System.nanoTime();
this.logEnabled = logEnabled;
if (logEnabled) {
System.out.printf("Start: %s%n", operationName);
}
}
public static TimeTracker of(String operationName) {
return new TimeTracker(operationName);
}
public static
T track(String operationName, ThrowableSupplier
execution) {
try {
return trackThrows(operationName, execution);
} catch (Exception e) {
throw new RuntimeException("Execution failed: " + operationName, e);
}
}
public static
T trackThrows(String operationName, ThrowableSupplier
execution) throws Exception {
try (TimeTracker ignored = new TimeTracker(operationName, true)) {
return execution.get();
}
}
public static void track(String operationName, ThrowableRunnable execution) {
try {
trackThrows(operationName, execution);
} catch (Exception e) {
throw new RuntimeException("Execution failed: " + operationName, e);
}
}
public static void trackThrows(String operationName, ThrowableRunnable execution) throws Exception {
try (TimeTracker ignored = new TimeTracker(operationName, true)) {
execution.run();
}
}
@Override
public void close() {
if (logEnabled) {
long timeElapsed = (System.nanoTime() - startTime) / 1_000_000;
System.out.printf("%s completed, elapsed: %d ms%n", operationName, timeElapsed);
}
}
@FunctionalInterface
public interface ThrowableSupplier
{
T get() throws Exception;
}
@FunctionalInterface
public interface ThrowableRunnable {
void run() throws Exception;
}
}Demo Usage
A demonstration class shows various usage patterns, including simple tasks, exception‑throwing tasks, nested tracking, and multi‑resource try‑with‑resources:
import java.io.IOException;
public class TimeTrackerDemo {
public void demonstrateUsage() {
// 1. Non‑checked‑exception version
TimeTracker.track("Simple task", () -> {
Thread.sleep(1000);
return "Done";
});
// 2. Version that may throw checked exception
try {
TimeTracker.trackThrows("Potentially failing task", () -> {
if (Math.random() < 0.5) {
throw new IOException("Simulated IO exception");
}
return "Success";
});
} catch (Exception e) {
e.printStackTrace();
}
// 3. Nested usage example
try {
TimeTracker.trackThrows("Complex flow", () -> {
TimeTracker.track("Subtask 1", () -> {
Thread.sleep(500);
});
return TimeTracker.trackThrows("Subtask 2", () -> {
Thread.sleep(500);
return "All done";
});
});
} catch (Exception e) {
e.printStackTrace();
}
// 4. try‑with‑resources example
try (TimeTracker tracker = TimeTracker.of("Resource demo")) {
performResourceIntensiveTask();
}
// 5. Multiple resources
try (TimeTracker t1 = TimeTracker.of("Stage 1");
TimeTracker t2 = TimeTracker.of("Stage 2");
CustomResource resource = acquireResource()) {
processResourcesSequentially(resource);
} catch (Exception e) {
e.printStackTrace();
}
// 6. Ignored return value example
try (TimeTracker ignored = TimeTracker.of("Background task")) {
performBackgroundTask();
}
}
private void performResourceIntensiveTask() throws InterruptedException {
Thread.sleep(1000);
System.out.println("Resource‑intensive task completed");
}
private CustomResource acquireResource() {
return new CustomResource();
}
private void processResourcesSequentially(CustomResource resource) {
resource.process();
}
private void performBackgroundTask() {
System.out.println("Executing background task");
}
private static class CustomResource implements AutoCloseable {
void process() {
System.out.println("Processing resource");
}
@Override
public void close() {
System.out.println("Resource closed");
}
}
}Improvement Suggestions
Integrate a logging framework such as SLF4J for flexible output.
Add statistical dimensions (max, min, average) for timing data.
Collect additional performance metrics for monitoring.
Support asynchronous operations.
Conclusion
While the utility class itself is not groundbreaking, it solves a practical problem by reducing repetitive timing code, handling exceptions gracefully, and leveraging modern Java language features to keep code clean and maintainable.
Good tool design balances practicality and ease of use; the TimeTracker exemplifies this principle.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.