From 71986e0eb23db00121aac7cce779759d43833551 Mon Sep 17 00:00:00 2001
From: thanglequoc
Date: Thu, 12 Feb 2026 11:24:59 +0700
Subject: [PATCH 1/2] feat: Update content
---
docs/_data/navigation.yml | 6 +-
docs/_includes/footer.html | 3 +-
docs/advanced-usage.md | 514 ++++++++----
docs/examples.md | 321 -------
docs/{user-guide.md => getting-started.md} | 276 +++---
docs/index.html | 10 +-
wiki/Advanced-Usage.md | 929 ++++++++-------------
wiki/Examples.md | 507 -----------
wiki/Getting-Started.md | 329 ++++++++
wiki/Home.md | 5 +-
wiki/User-Guide.md | 463 ----------
11 files changed, 1157 insertions(+), 2206 deletions(-)
delete mode 100644 docs/examples.md
rename docs/{user-guide.md => getting-started.md} (70%)
delete mode 100644 wiki/Examples.md
create mode 100644 wiki/Getting-Started.md
delete mode 100644 wiki/User-Guide.md
diff --git a/docs/_data/navigation.yml b/docs/_data/navigation.yml
index 4f8a176..7ac59bc 100644
--- a/docs/_data/navigation.yml
+++ b/docs/_data/navigation.yml
@@ -1,9 +1,7 @@
main:
- title: Home
url: /
- - title: User Guide
- url: /user-guide/
- - title: Examples
- url: /examples/
+ - title: Getting Started
+ url: /getting-started/
- title: Advanced Usage
url: /advanced-usage/
diff --git a/docs/_includes/footer.html b/docs/_includes/footer.html
index f0c809e..977080f 100644
--- a/docs/_includes/footer.html
+++ b/docs/_includes/footer.html
@@ -6,8 +6,7 @@
-
+
Get Started
→
@@ -322,12 +322,12 @@
Ready to Track Like a Ni
Block tracking, custom thresholds, constructor tracking, and more — explore all features in the docs and start measuring the smart way.
diff --git a/wiki/Advanced-Usage.md b/wiki/Advanced-Usage.md
index 66dda78..d39e3b0 100644
--- a/wiki/Advanced-Usage.md
+++ b/wiki/Advanced-Usage.md
@@ -1,29 +1,223 @@
-# Advanced Usage 🚀
+# Advanced Usage
-This guide covers advanced features and optimization techniques for Timer Ninja.
+Real-world examples, advanced patterns, and optimization techniques for Timer Ninja.
-## Table of Contents
+---
+
+## Real-World Examples
+
+### Banking Service
+
+A comprehensive banking service demonstrating thresholds, argument tracking, and nested call hierarchies.
+
+```java
+public class BankService {
+ private BalanceService balanceService;
+ private UserService userService;
+ private NotificationService notificationService;
+
+ @TimerNinjaTracker(threshold = 200)
+ public void requestMoneyTransfer(int sourceUserId, int targetUserId, int amount) {
+ User sourceUser = userService.findUser(sourceUserId);
+ User targetUser = userService.findUser(targetUserId);
+ balanceService.deductAmount(sourceUser, amount);
+ balanceService.increaseAmount(targetUser, amount);
+ }
+
+ @TimerNinjaTracker(includeArgs = true, threshold = 500)
+ public void depositMoney(int userId, int amount) {
+ // Deposit logic
+ }
+
+ @TimerNinjaTracker(includeArgs = true)
+ public void payWithCard(int userId, BankCard card, int amount) {
+ User user = userService.findUser(userId);
+ // Card payment logic
+ }
+}
+```
+
+**Output:**
+```
+{===== Start of trace context id: 851ac23b-2669-4883-8c97-032b8fd2d45c =====}
+public void requestMoneyTransfer(int sourceUserId, int targetUserId, int amount) - 1037 ms ¤ [Threshold Exceed !!: 200 ms]
+ |-- public User findUser(int userId) - 105 ms
+ |-- public User findUser(int userId) - 108 ms
+ |-- public void deductAmount(User user, int amount) - 306 ms
+ |-- public void increaseAmount(User user, int amount) - 418 ms
+{====== End of trace context id: 851ac23b-2669-4883-8c97-032b8fd2d45c ======}
+```
+
+### Notification Service — Nested Tracking
+
+Demonstrates nested method tracking with multiple levels.
+
+```java
+public class NotificationService {
+
+ @TimerNinjaTracker
+ public void notify(User user) {
+ notifyViaSMS(user);
+ notifyViaEmail(user);
+ }
+
+ @TimerNinjaTracker
+ private void notifyViaSMS(User user) {
+ try { Thread.sleep(50); }
+ catch (InterruptedException e) { throw new RuntimeException(e); }
+ }
+
+ @TimerNinjaTracker
+ private void notifyViaEmail(User user) {
+ try { Thread.sleep(200); }
+ catch (InterruptedException e) { throw new RuntimeException(e); }
+ }
+}
+```
+
+**Output:**
+```
+{===== Start of trace context id: abc123... =====}
+public void notify(User user) - 258 ms
+ |-- private void notifyViaSMS(User user) - 53 ms
+ |-- private void notifyViaEmail(User user) - 205 ms
+{====== End of trace context id: abc123... ======}
+```
+
+### Loan Processing — Mixed Annotation + Block Tracking
+
+Combines `@TimerNinjaTracker` with `TimerNinjaBlock` for phased tracking within a single method.
+
+```java
+public class LoanService {
+
+ @TimerNinjaTracker(includeArgs = true, threshold = 100)
+ public void processLoanApplication(int userId, double loanAmount, int termMonths) {
+ User user = userService.findUser(userId);
+
+ TimerNinjaBlock.measure("credit score check", () -> {
+ simulateDelay(60);
+ });
+
+ TimerNinjaBlock.measure("income verification", () -> {
+ simulateDelay(80);
+ });
+
+ BlockTrackerConfig riskConfig = new BlockTrackerConfig()
+ .setTimeUnit(ChronoUnit.MILLIS)
+ .setThreshold(30);
+
+ TimerNinjaBlock.measure("risk assessment", riskConfig, () -> {
+ simulateDelay(40);
+ });
+
+ String approvalStatus = TimerNinjaBlock.measure("final approval", () -> {
+ simulateDelay(50);
+ return "APPROVED";
+ });
+ }
+}
+```
+
+**Output:**
+```
+{===== Start of trace context id: ghi789... =====}
+public void processLoanApplication(int userId, double loanAmount, int termMonths) - Args: [userId={123}, loanAmount={50000.0}, termMonths={36}] - 345 ms
+ |-- [Block] credit score check - 60 ms
+ |-- [Block] income verification - 80 ms
+ |-- [Block] risk assessment - 40 ms
+ |-- [Block] final approval - 50 ms
+{====== End of trace context id: ghi789... ======}
+```
+
+### E-commerce Order Processing
+
+```java
+@Service
+public class OrderService {
+
+ @TimerNinjaTracker
+ public Order createOrder(OrderRequest request) {
+ Order order = validateAndCreateOrder(request);
+ PaymentResult paymentResult = processPayment(order);
+ updateInventory(order);
+ sendConfirmation(order);
+ return order;
+ }
+
+ @TimerNinjaTracker(threshold = 500, includeArgs = true)
+ private PaymentResult processPayment(Order order) {
+ return paymentService.charge(
+ order.getUserId(), order.getPaymentMethod(), order.getTotalAmount()
+ );
+ }
+
+ @TimerNinjaTracker(threshold = 200)
+ private void updateInventory(Order order) {
+ order.getItems().forEach(item ->
+ inventoryService.deductStock(item.getProductId(), item.getQuantity())
+ );
+ }
+
+ @TimerNinjaTracker
+ private void sendConfirmation(Order order) {
+ notificationService.sendEmailConfirmation(order.getUserEmail(), order);
+ }
+}
+```
+
+**Output:**
+```
+{===== Start of trace context id: jkl012... =====}
+public Order createOrder(OrderRequest request) - 2150 ms
+ |-- public Order validateAndCreateOrder(OrderRequest request) - 120 ms
+ |-- public PaymentResult processPayment(Order order) - Args: [order={id=ORD-12345, ...}] - 1250 ms ¤ [Threshold Exceed !!: 500 ms]
+ |-- public PaymentResult charge(int userId, String paymentMethod, double amount) - 1180 ms
+ |-- public void updateInventory(Order order) - 450 ms
+ |-- public void sendConfirmation(Order order) - 330 ms
+{====== End of trace context id: jkl012... ======}
+```
+
+### Constructor Tracking
-1. [Nested Tracking Deep Dive](#nested-tracking-deep-dive)
-2. [Advanced Threshold Strategies](#advanced-threshold-strategies)
-3. [Block Tracking Patterns](#block-tracking-patterns)
-4. [Performance Optimization](#performance-optimization)
-5. [Integration with Other Libraries](#integration-with-other-libraries)
-6. [Custom Logging Strategies](#custom-logging-strategies)
-7. [Troubleshooting Complex Scenarios](#troubleshooting-complex-scenarios)
+Track constructor initialization chains to identify slow startup:
+
+```java
+public class TransportationService {
+ private ShippingService shippingService;
+
+ @TimerNinjaTracker
+ public TransportationService() {
+ this.shippingService = new ShippingService();
+ }
+}
+
+public class ShippingService {
+ @TimerNinjaTracker
+ public ShippingService() {
+ // Shipping service initialization
+ }
+}
+```
+
+**Output:**
+```
+{===== Start of trace context id: def456... =====}
+public TransportationService() - 150 ms
+ |-- public ShippingService() - 80 ms
+{====== End of trace context id: def456... ======}
+```
---
## Nested Tracking Deep Dive
-Timer Ninja automatically detects and preserves nested method calls that are also annotated with `@TimerNinjaTracker`. This provides a complete view of the execution stack.
-
-### Multi-Level Nesting Example
+Timer Ninja automatically detects and preserves nested method calls annotated with `@TimerNinjaTracker`, providing a complete view of the execution stack.
```java
@Service
public class OrderProcessingService {
-
+
@TimerNinjaTracker
public void processOrder(Order order) {
validateOrder(order);
@@ -40,7 +234,6 @@ public class OrderProcessingService {
@TimerNinjaTracker
private void validateCustomer(Long customerId) {
Customer customer = customerService.findById(customerId);
- // Validation logic
}
@TimerNinjaTracker(threshold = 100)
@@ -55,405 +248,150 @@ public class OrderProcessingService {
}
```
-### Output with Multi-Level Nesting
-
+**Output:**
```
{===== Start of trace context id: abc123... =====}
public void processOrder(Order order) - 1850 ms
|-- private void validateOrder(Order order) - 450 ms
| |-- private void validateCustomer(Long customerId) - 320 ms
- | | |-- public Customer findById(Long customerId) - 280 ms
| |-- private void validateItems(List items) - 110 ms
| |-- private void validateItem(OrderItem item) - 52 ms ¤ [Threshold Exceed !!: 50 ms]
- | |-- private void validateItem(OrderItem item) - 48 ms
|-- public void processPayment(Order order) - 1200 ms
|-- public void shipOrder(Order order) - 200 ms
{====== End of trace context id: abc123... ======}
```
-### Key Points
-
-1. **Automatic Hierarchy**: Timer Ninja automatically builds the call tree
-2. **Indentation Levels**: Each nested level is indented with `|--`
-3. **Independent Thresholds**: Each method can have its own threshold
-4. **Context Sharing**: All methods in a call chain share the same trace context ID
+**Key points:**
+- **Automatic hierarchy** — Timer Ninja builds the call tree automatically
+- **Independent thresholds** — each method can have its own threshold
+- **Context sharing** — all methods in a call chain share the same trace context ID
---
## Advanced Threshold Strategies
-### Dynamic Thresholds Based on Input
+### Dynamic Thresholds with BlockTrackerConfig
+
+Use `BlockTrackerConfig` to set thresholds dynamically based on runtime conditions:
```java
@Service
public class QueryService {
-
+
@TimerNinjaTracker(includeArgs = true)
public void executeQuery(String query, int expectedRows) {
- // Use block tracking with dynamic threshold
- int threshold = calculateThreshold(expectedRows);
-
+ int threshold = Math.min(100 + (expectedRows / 10), 1000);
+
BlockTrackerConfig config = new BlockTrackerConfig()
.setThreshold(threshold)
.setTimeUnit(ChronoUnit.MILLIS);
-
+
TimerNinjaBlock.measure("query execution", config, () -> {
database.execute(query);
});
}
-
- private int calculateThreshold(int expectedRows) {
- // More rows = higher acceptable threshold
- return Math.min(100 + (expectedRows / 10), 1000);
- }
-}
-```
-
-### Adaptive Thresholds in Production
-
-```java
-@Service
-public class AdaptiveTrackingService {
-
- private final Map adaptiveThresholds = new ConcurrentHashMap<>();
-
- @TimerNinjaTracker(includeArgs = true)
- public void processRequest(String operation, RequestData data) {
- int threshold = adaptiveThresholds.getOrDefault(operation, 200);
-
- // Track execution
- long startTime = System.currentTimeMillis();
- try {
- // Process the request
- performOperation(operation, data);
- } finally {
- long duration = System.currentTimeMillis() - startTime;
-
- // Adjust threshold based on observed performance
- adjustThreshold(operation, duration, threshold);
- }
- }
-
- private void adjustThreshold(String operation, long duration, int currentThreshold) {
- if (duration > currentThreshold * 2) {
- // Consistently slow - increase threshold
- adaptiveThresholds.put(operation, (int) (currentThreshold * 1.5));
- } else if (duration < currentThreshold / 2 && duration > 50) {
- // Faster than expected - consider lowering threshold
- adaptiveThresholds.put(operation, (int) (currentThreshold * 0.8));
- }
- }
}
```
### Threshold Tiers
-```java
-@Service
-public class TieredTrackingService {
-
- // Fast operations
- @TimerNinjaTracker(threshold = 50)
- public void cacheLookup(String key) {
- // Very fast cache access
- }
-
- // Standard operations
- @TimerNinjaTracker(threshold = 200)
- public void databaseQuery(String query) {
- // Standard database operation
- }
-
- // Slow operations
- @TimerNinjaTracker(threshold = 1000)
- public void externalApiCall(String endpoint) {
- // External API call
- }
-
- // Very slow operations
- @TimerNinjaTracker(threshold = 5000)
- public void batchProcess(String batchId) {
- // Batch processing
- }
-}
-```
-
----
-
-## Block Tracking Patterns
-
-### Pattern 1: Phased Processing
-
-```java
-@Service
-public class DataPipelineService {
-
- @TimerNinjaTracker
- public void runPipeline(String dataId) {
- // Phase 1: Extraction
- RawData raw = TimerNinjaBlock.measure("extract", () -> {
- return extractor.extract(dataId);
- });
-
- // Phase 2: Transformation
- ProcessedData processed = TimerNinjaBlock.measure("transform", () -> {
- return transformer.transform(raw);
- });
-
- // Phase 3: Loading
- TimerNinjaBlock.measure("load", () -> {
- loader.load(processed);
- });
-
- // Phase 4: Cleanup
- TimerNinjaBlock.measure("cleanup", () -> {
- cleanupService.cleanup(dataId);
- });
- }
-}
-```
-
-### Pattern 2: Conditional Tracking
+Assign different thresholds based on expected operation speed:
```java
-@Service
-public class ConditionalTrackingService {
-
- @TimerNinjaTracker
- public void processWithTracking(boolean enableDetailedTracking, Data data) {
- // Always track main processing
- processMain(data);
-
- // Conditionally track detailed steps
- if (enableDetailedTracking) {
- TimerNinjaBlock.measure("detailed validation", () -> {
- validateDetailed(data);
- });
-
- TimerNinjaBlock.measure("detailed transformation", () -> {
- transformDetailed(data);
- });
- } else {
- // Fast path without detailed tracking
- validateQuick(data);
- transformQuick(data);
- }
- }
-}
-```
+@TimerNinjaTracker(threshold = 50) // Fast: cache lookups
+public void cacheLookup(String key) { }
-### Pattern 3: Retry Logic Tracking
-
-```java
-@Service
-public class RetryTrackingService {
-
- @TimerNinjaTracker
- public Result executeWithRetry(String operation, Data data) {
- int maxRetries = 3;
- int attempt = 0;
-
- while (attempt < maxRetries) {
- attempt++;
-
- try {
- Result result = TimerNinjaBlock.measure(
- String.format("attempt %d", attempt),
- () -> executeOperation(operation, data)
- );
- return result;
- } catch (Exception e) {
- if (attempt == maxRetries) {
- throw new RuntimeException("Operation failed after " + maxRetries + " attempts", e);
- }
-
- // Track delay before retry
- TimerNinjaBlock.measure("retry delay", () -> {
- try {
- Thread.sleep(calculateBackoff(attempt));
- } catch (InterruptedException ie) {
- Thread.currentThread().interrupt();
- }
- });
- }
- }
-
- throw new IllegalStateException("Should not reach here");
- }
-
- private long calculateBackoff(int attempt) {
- return (long) Math.pow(2, attempt) * 100; // Exponential backoff
- }
-}
-```
+@TimerNinjaTracker(threshold = 200) // Standard: database queries
+public void databaseQuery(String query) { }
-### Pattern 4: Parallel Processing Tracking
+@TimerNinjaTracker(threshold = 1000) // Slow: external API calls
+public void externalApiCall(String endpoint) { }
-```java
-@Service
-public class ParallelTrackingService {
-
- @TimerNinjaTracker
- public void processInParallel(List tasks) {
- ExecutorService executor = Executors.newFixedThreadPool(4);
-
- try {
- List> futures = tasks.stream()
- .map(task -> executor.submit(() ->
- TimerNinjaBlock.measure(
- "task-" + task.getId(),
- () -> processTask(task)
- )
- ))
- .collect(Collectors.toList());
-
- // Track the waiting/aggregation phase
- TimerNinjaBlock.measure("aggregate results", () -> {
- futures.forEach(future -> {
- try {
- future.get();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
- });
-
- } finally {
- executor.shutdown();
- }
- }
-}
+@TimerNinjaTracker(threshold = 5000) // Very slow: batch processing
+public void batchProcess(String batchId) { }
```
---
-## Performance Optimization
-
-### Minimizing Overhead
-
-Timer Ninja is designed to be lightweight, but here are tips to minimize overhead:
+## Block Tracking Patterns
-#### 1. Selective Tracking
+### Phased Processing (ETL Pipeline)
```java
-// ❌ Bad - tracking everything
-@TimerNinjaTracker
-public String getUserName(Long userId) {
- return userRepository.findById(userId).getName();
-}
-
@TimerNinjaTracker
-public String getUserEmail(Long userId) {
- return userRepository.findById(userId).getEmail();
-}
+public void runPipeline(String dataId) {
+ RawData raw = TimerNinjaBlock.measure("extract", () -> {
+ return extractor.extract(dataId);
+ });
-// ✅ Good - tracking only the repository call
-public String getUserName(Long userId) {
- return userRepository.findById(userId).getName();
-}
+ ProcessedData processed = TimerNinjaBlock.measure("transform", () -> {
+ return transformer.transform(raw);
+ });
-@Repository
-public class UserRepository {
- @TimerNinjaTracker
- public User findById(Long userId) {
- // Database query
- }
+ TimerNinjaBlock.measure("load", () -> {
+ loader.load(processed);
+ });
}
```
-#### 2. Use Thresholds Effectively
+### Conditional Tracking
```java
-// ❌ Too many fast operations cluttering logs
-@TimerNinjaTracker(threshold = 1)
-public void fastOperation() {
- // Very fast operation
-}
-
-// ✅ Appropriate threshold
-@TimerNinjaTracker(threshold = 100)
-public void meaningfulOperation() {
- // Operation that should be reasonably fast
-}
-```
-
-#### 3. Avoid Circular Dependencies in toString()
+@TimerNinjaTracker
+public void processWithTracking(boolean enableDetailedTracking, Data data) {
+ processMain(data);
-```java
-// ❌ Bad - circular reference in toString()
-public class User {
- private List orders;
-
- @Override
- public String toString() {
- return "User{orders=" + orders + "}"; // Orders contain Users
- }
-}
+ if (enableDetailedTracking) {
+ TimerNinjaBlock.measure("detailed validation", () -> {
+ validateDetailed(data);
+ });
-// ✅ Good - selective toString()
-public class User {
- private List orders;
-
- @Override
- public String toString() {
- return String.format("User{id=%d, ordersCount=%d}", id, orders.size());
+ TimerNinjaBlock.measure("detailed transformation", () -> {
+ transformDetailed(data);
+ });
}
}
```
-### Memory Management
-
-#### 1. Clean Up Trace Contexts
-
-Timer Ninja automatically cleans up trace contexts, but you can ensure proper cleanup:
+### Retry Logic Tracking
```java
-@Service
-public class SafeExecutionService {
-
- @TimerNinjaTracker
- public void executeSafely(Task task) {
+@TimerNinjaTracker
+public Result executeWithRetry(String operation, Data data) {
+ int maxRetries = 3;
+ int attempt = 0;
+
+ while (attempt < maxRetries) {
+ attempt++;
try {
- task.run();
+ return TimerNinjaBlock.measure(
+ String.format("attempt %d", attempt),
+ () -> executeOperation(operation, data)
+ );
} catch (Exception e) {
- // Trace context is still cleaned up automatically
- throw e;
+ if (attempt == maxRetries) {
+ throw new RuntimeException("Failed after " + maxRetries + " attempts", e);
+ }
+ TimerNinjaBlock.measure("retry delay", () -> {
+ Thread.sleep(calculateBackoff(attempt));
+ });
}
}
-}
-```
-
-#### 2. Large Argument Objects
-
-For large objects, avoid including them in logs:
-
-```java
-@TimerNinjaTracker // No includeArgs for large objects
-public void processLargeData(LargeDataSet dataSet) {
- // Processing logic
-}
-
-// Or provide a summary
-@TimerNinjaTracker(includeArgs = true)
-public void processData(DataSummary summary) {
- // Processing with summary
+ throw new IllegalStateException("Should not reach here");
}
```
---
-## Integration with Other Libraries
-
-### Spring Boot Integration
+## Integration with Spring Boot
```java
@Configuration
-public class TimerNinjaConfiguration {
-
+public class TimerNinjaConfig {
+
@Bean
public CommandLineRunner setupTimerNinja() {
return args -> {
- // Enable System.out for development
if (isDevelopmentEnvironment()) {
io.github.thanglequoc.timerninja.TimerNinjaConfiguration
.getInstance()
@@ -461,140 +399,89 @@ public class TimerNinjaConfiguration {
}
};
}
-
- private boolean isDevelopmentEnvironment() {
- return Arrays.asList(args).contains("--dev") ||
- "dev".equals(System.getProperty("spring.profiles.active"));
- }
}
@RestController
-@RequestMapping("/api")
-public class ApiController {
-
+@RequestMapping("/api/users")
+public class UserController {
+
@TimerNinjaTracker
- @GetMapping("/data/{id}")
- public ResponseEntity getData(@PathVariable Long id) {
- return ResponseEntity.ok(dataService.findById(id));
+ @GetMapping("/{id}")
+ public ResponseEntity getUser(@PathVariable Long id) {
+ User user = userService.findById(id);
+ return ResponseEntity.ok(user);
}
-}
-```
-
-### Custom Metrics Integration
-```java
-@Service
-public class MetricsIntegrationService {
-
- private final MeterRegistry meterRegistry;
-
- public MetricsIntegrationService(MeterRegistry meterRegistry) {
- this.meterRegistry = meterRegistry;
- }
-
- @TimerNinjaTracker(includeArgs = true)
- public void trackWithMetrics(String operation, Data data) {
- // Timer Ninja tracks execution time
- // Also record custom metrics
- Timer.Sample sample = Timer.start(meterRegistry);
-
- try {
- executeOperation(operation, data);
-
- // Record success metrics
- sample.stop(Timer.builder("operation.duration")
- .tag("operation", operation)
- .tag("status", "success")
- .register(meterRegistry));
-
- } catch (Exception e) {
- // Record failure metrics
- sample.stop(Timer.builder("operation.duration")
- .tag("operation", operation)
- .tag("status", "failure")
- .register(meterRegistry));
-
- throw e;
- }
+ @TimerNinjaTracker(includeArgs = true, threshold = 100)
+ @PostMapping
+ public ResponseEntity createUser(@RequestBody CreateUserRequest request) {
+ User user = userService.create(request);
+ return ResponseEntity.status(HttpStatus.CREATED).body(user);
}
}
```
-### AOP Integration with Other Aspects
-
-```java
-@Aspect
-@Component
-public class MonitoringAspect {
-
- @Around("@annotation(io.github.thanglequoc.timerninja.TimerNinjaTracker)")
- public Object aroundTimerNinjaTracker(ProceedingJoinPoint joinPoint) throws Throwable {
- String methodName = joinPoint.getSignature().getName();
-
- // Additional monitoring logic
- Metrics.increment("method.calls", "method", methodName);
-
- try {
- Object result = joinPoint.proceed();
-
- // Success metrics
- Metrics.increment("method.success", "method", methodName);
- return result;
-
- } catch (Exception e) {
- // Failure metrics
- Metrics.increment("method.failure", "method", methodName, "error", e.getClass().getSimpleName());
- throw e;
- }
- }
-}
+**Output for GET request:**
+```
+{===== Start of trace context id: mno345... =====}
+public ResponseEntity getUser(Long id) - 85 ms
+ |-- public User findById(Long id) - 70 ms
+ |-- public User queryDatabase(Long id) - 65 ms
+{====== End of trace context id: mno345... ======}
```
---
-## Custom Logging Strategies
+## Performance Optimization
-### Structured Logging
+### Selective Tracking
```java
-@Service
-public class StructuredLoggingService {
-
- @TimerNinjaTracker(includeArgs = true)
- public void processRequest(Request request) {
- // Timer Ninja provides timing
- // Add structured logging for additional context
- log.info("Processing request: {}", request.toStructuredLog());
-
- // Business logic
+// ❌ Bad — tracking thin wrappers
+@TimerNinjaTracker
+public String getUserName(Long userId) {
+ return userRepository.findById(userId).getName();
+}
+
+// ✅ Good — tracking the meaningful operation
+@Repository
+public class UserRepository {
+ @TimerNinjaTracker
+ public User findById(Long userId) {
+ // Database query
}
}
```
-### Conditional Argument Logging
+### Avoid Circular toString()
+
+When using `includeArgs = true`, ensure argument objects don't have circular `toString()` references:
```java
-@Service
-public class ConditionalArgLoggingService {
-
- @TimerNinjaTracker
- public void processSensitiveData(SensitiveData data) {
- // Never log sensitive data
- // Use block tracking for phases instead
- TimerNinjaBlock.measure("validation", () -> {
- validateData(data);
- });
-
- TimerNinjaBlock.measure("processing", () -> {
- processData(data);
- });
+// ❌ Bad — circular reference
+public class User {
+ private List orders;
+ @Override
+ public String toString() {
+ return "User{orders=" + orders + "}"; // Orders contain Users!
+ }
+}
+
+// ✅ Good — selective toString()
+public class User {
+ private List orders;
+ @Override
+ public String toString() {
+ return String.format("User{id=%d, ordersCount=%d}", id, orders.size());
}
}
```
-### Custom Log Formats
+---
+
+## Custom Logging Configuration
-Timer Ninja uses SLF4J, so you can customize the format through your logging configuration:
+Timer Ninja uses SLF4J. Customize the output format through your logging configuration:
**logback.xml**
```xml
@@ -604,12 +491,12 @@ Timer Ninja uses SLF4J, so you can customize the format through your logging con
%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
-
+
-
+
@@ -618,157 +505,77 @@ Timer Ninja uses SLF4J, so you can customize the format through your logging con
---
-## Troubleshooting Complex Scenarios
-
-### Issue 1: Missing Nested Traces
+## Troubleshooting
-**Problem:** Nested method calls not appearing in traces.
+### No Output in Logs
-**Solution:**
+1. Check if SLF4J provider is configured (Logback, Log4j2, etc.)
+2. Verify log level is at least `INFO`
+3. Enable `System.out` for testing: `TimerNinjaConfiguration.getInstance().toggleSystemOutLog(true);`
-1. Verify annotations are present on nested methods
-```java
-@TimerNinjaTracker
-public void parent() {
- child(); // Must also have @TimerNinjaTracker
-}
-
-@TimerNinjaTracker
-public void child() {
- // Child logic
-}
-```
-
-2. Check if methods are private/internal
-```java
-// Public/package-private methods are tracked
-// Private methods are also tracked if annotated
-```
-
-3. Verify AspectJ weaving is working correctly
-```groovy
-// Ensure aspect dependency is included
-aspect 'io.github.thanglequoc:timer-ninja:1.2.0'
-```
-
-### Issue 2: Performance Degradation
-
-**Problem:** Timer Ninja causing noticeable performance impact.
-
-**Solution:**
-
-1. Reduce tracking overhead
-```java
-// Remove tracking from hot paths
-// Use higher thresholds
-@TimerNinjaTracker(threshold = 500) // Instead of 100
-```
-
-2. Disable in production
-```java
-@TimerNinjaTracker(enabled = isDevelopment())
-public void debugMethod() {
- // Only tracked in dev
-}
-```
-
-3. Use selective tracking
-```java
-// Track only entry points
-@TimerNinjaTracker
-public void processRequest() {
- // Nested methods not tracked
-}
-```
-
-### Issue 3: Large Trace Outputs
-
-**Problem:** Trace logs are too large and cluttered.
-
-**Solution:**
+### Methods Not Being Tracked
-1. Use thresholds effectively
-```java
-@TimerNinjaTracker(threshold = 200)
-// Only shows methods taking > 200ms
-```
+1. Verify AspectJ plugin is configured correctly
+2. Check that the dependency includes the aspect: `aspect 'io.github.thanglequoc:timer-ninja:1.3.0'`
+3. Ensure `enabled = true` (or not set) on the annotation
-2. Disable argument logging
-```java
-@TimerNinjaTracker(includeArgs = false) // Default
-// Or selectively enable
-@TimerNinjaTracker(includeArgs = true)
-public void criticalOperation(Data data) {
- // Only log args for critical operations
-}
-```
+### Missing Nested Traces
-3. Use block tracking for phases instead of individual methods
-```java
-@TimerNinjaTracker
-public void processOrder(Order order) {
- TimerNinjaBlock.measure("validation", () -> validate(order));
- TimerNinjaBlock.measure("processing", () -> process(order));
- TimerNinjaBlock.measure("notification", () -> notify(order));
-}
-// One line instead of multiple method traces
-```
+1. Verify `@TimerNinjaTracker` is present on nested methods — all access levels (public, private, etc.) are tracked if annotated
+2. Verify AspectJ weaving is working: ensure the `aspect` dependency is declared
-### Issue 4: Thread Safety Concerns
+### Large Trace Outputs
-**Problem:** Tracking in multi-threaded environments.
+1. Use thresholds to filter noise: `@TimerNinjaTracker(threshold = 200)`
+2. Disable argument logging by default, enable selectively
+3. Use block tracking for phases instead of individual method tracking
-**Solution:**
+### Thread Safety
-Timer Ninja is thread-safe by design. Each thread maintains its own trace context:
+Timer Ninja is thread-safe by design. Each thread maintains its own trace context via `ThreadLocal`:
```java
@TimerNinjaTracker
public void parallelProcessing() {
ExecutorService executor = Executors.newFixedThreadPool(4);
-
- // Each task gets its own trace context
+
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
- processTask(taskId); // Independent trace
+ processTask(taskId); // Independent trace per thread
});
}
-
executor.shutdown();
}
```
---
-## Best Practices Summary
+## Best Practices
### Do ✅
-1. **Track entry points** (controllers, main methods, public services)
-2. **Use appropriate thresholds** based on expected performance
-3. **Enable argument logging** for debugging critical operations
-4. **Combine annotation and block tracking** for comprehensive monitoring
-5. **Track external operations** (API calls, database queries, file I/O)
-6. **Monitor constructor chains** for slow initialization
-7. **Use structured logging** for additional context
-8. **Disable in production** when performance is critical
+1. **Track entry points** — controllers, main methods, public service methods
+2. **Use appropriate thresholds** — based on expected performance for each operation tier
+3. **Enable argument logging selectively** — for debugging critical operations only
+4. **Combine annotation and block tracking** — annotations for methods, blocks for phases within methods
+5. **Track external operations** — API calls, database queries, file I/O
+6. **Monitor constructor chains** — identify slow initialization
+7. **Use structured logging** — configure logback/log4j2 for Timer Ninja output
### Don't ❌
-1. **Track every method** - focus on meaningful operations
-2. **Use very low thresholds** - creates noise in logs
-3. **Log sensitive data** - even with `includeArgs`
-4. **Create circular toString()** - causes infinite loops
-5. **Track simple getters/setters** - adds unnecessary overhead
-6. **Ignore nested tracking** - leverage call hierarchy insights
-7. **Forget to configure AspectJ** - tracking won't work
-8. **Use in hot paths** without considering performance impact
+1. **Track every method** — focus on meaningful operations
+2. **Use very low thresholds** — creates noise in logs
+3. **Log sensitive data** with `includeArgs` — mask or exclude sensitive fields
+4. **Create circular `toString()` references** — causes infinite loops with argument logging
+5. **Track simple getters/setters** — adds unnecessary overhead
+6. **Forget to configure AspectJ weaving** — tracking won't work without it
+7. **Use in hot paths** without considering performance impact
---
## Further Reading
-- **[User Guide](User-Guide)** - Detailed feature documentation
-- **[Examples](Examples)** - Real-world usage patterns
-- **[Home](Home)** - Quick start and installation guide
\ No newline at end of file
+- **[Getting Started](Getting-Started)** — Installation and feature documentation
+- **[Home](Home)** — Quick start and installation guide
diff --git a/wiki/Examples.md b/wiki/Examples.md
deleted file mode 100644
index e8737d6..0000000
--- a/wiki/Examples.md
+++ /dev/null
@@ -1,507 +0,0 @@
-# Examples 💡
-
-This page provides real-world examples demonstrating Timer Ninja usage patterns.
-
-## Table of Contents
-
-1. [Basic Method Tracking](#basic-method-tracking)
-2. [Banking Service Example](#banking-service-example)
-3. [Notification Service Example](#notification-service-example)
-4. [Constructor Tracking](#constructor-tracking)
-5. [Loan Processing Example](#loan-processing-example)
-6. [E-commerce Order Processing](#e-commerce-order-processing)
-7. [API Controller Example](#api-controller-example)
-
----
-
-## Basic Method Tracking
-
-### Simple Tracking
-
-```java
-@TimerNinjaTracker
-public void processRequest() {
- // Business logic
- System.out.println("Processing request...");
-}
-```
-
-**Output:**
-```
-{===== Start of trace context id: abc123... =====}
-public void processRequest() - 42 ms
-{====== End of trace context id: abc123... ======}
-```
-
-### With Time Unit
-
-```java
-@TimerNinjaTracker(timeUnit = ChronoUnit.MICROS)
-public void calculateMetrics() {
- // Precision calculation
-}
-```
-
-**Output:**
-```
-public void calculateMetrics() - 52341 µs
-```
-
----
-
-## Banking Service Example
-
-This example shows a comprehensive banking service with multiple tracking scenarios.
-
-### Money Transfer Service
-
-```java
-public class BankService {
- private BalanceService balanceService;
- private UserService userService;
- private NotificationService notificationService;
-
- public BankService() {
- BankRecordBook masterRecordBook = BankRecordBook.getInstance();
- this.notificationService = new NotificationService();
- this.balanceService = new BalanceService(masterRecordBook, notificationService);
- this.userService = new UserService(masterRecordBook);
- }
-
- /**
- * Transfer money between users with threshold tracking
- */
- @TimerNinjaTracker(threshold = 200)
- public void requestMoneyTransfer(int sourceUserId, int targetUserId, int amount) {
- User sourceUser = userService.findUser(sourceUserId);
- User targetUser = userService.findUser(targetUserId);
- balanceService.deductAmount(sourceUser, amount);
- balanceService.increaseAmount(targetUser, amount);
- }
-
- /**
- * Deposit money with argument tracking
- */
- @TimerNinjaTracker(includeArgs = true, threshold = 500)
- public void depositMoney(int userId, int amount) {
- // Deposit logic
- }
-
- /**
- * Payment with card - full argument tracking
- */
- @TimerNinjaTracker(includeArgs = true)
- public void payWithCard(int userId, BankCard card, int amount) {
- User user = userService.findUser(userId);
- // Card payment logic
- }
-}
-```
-
-### Output Example
-
-```
-{===== Start of trace context id: 851ac23b-2669-4883-8c97-032b8fd2d45c =====}
-public void requestMoneyTransfer(int sourceUserId, int targetUserId, int amount) - 1037 ms ¤ [Threshold Exceed !!: 200 ms]
- |-- public User findUser(int userId) - 105 ms
- |-- public User findUser(int userId) - 108 ms
- |-- public void deductAmount(User user, int amount) - 306 ms
- |-- public void increaseAmount(User user, int amount) - 418 ms
-{====== End of trace context id: 851ac23b-2669-4883-8c97-032b8fd2d45c ======}
-```
-
----
-
-## Notification Service Example
-
-This example demonstrates nested method tracking with multiple levels.
-
-### Notification System
-
-```java
-public class NotificationService {
-
- public NotificationService() {
- // Constructor initialization
- }
-
- @TimerNinjaTracker
- public void notify(User user) {
- // Main notification method calls sub-methods
- notifyViaSMS(user);
- notifyViaEmail(user);
- }
-
- @TimerNinjaTracker
- private void notifyViaSMS(User user) {
- // SMS notification logic
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-
- @TimerNinjaTracker
- private void notifyViaEmail(User user) {
- // Email notification logic
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-}
-```
-
-### Output Example
-
-```
-{===== Start of trace context id: abc123... =====}
-public void notify(User user) - 258 ms
- |-- private void notifyViaSMS(User user) - 53 ms
- |-- private void notifyViaEmail(User user) - 205 ms
-{====== End of trace context id: abc123... ======}
-```
-
----
-
-## Constructor Tracking
-
-### Service Initialization Chain
-
-```java
-public class TransportationService {
- private ShippingService shippingService;
-
- @TimerNinjaTracker
- public TransportationService() {
- this.shippingService = new ShippingService();
- // Additional initialization
- }
-}
-
-public class ShippingService {
- @TimerNinjaTracker
- public ShippingService() {
- // Shipping service initialization
- }
-}
-```
-
-### Usage
-```java
-TransportationService service = new TransportationService();
-```
-
-### Output Example
-```
-{===== Start of trace context id: def456... =====}
-public TransportationService() - 150 ms
- |-- public ShippingService() - 80 ms
-{====== End of trace context id: def456... ======}
-```
-
-### Disabled Constructor Tracking
-
-```java
-public class LocationService {
- @TimerNinjaTracker(enabled = false)
- public LocationService() {
- // This won't be tracked
- }
-}
-```
-
-**Output:**
-```
-There isn't any tracker enabled in the tracking context
-```
-
----
-
-## Loan Processing Example
-
-This example combines annotation-based tracking with block tracking.
-
-### Loan Application Processing
-
-```java
-public class LoanService {
- private UserService userService;
-
- public LoanService(UserService userService) {
- this.userService = userService;
- }
-
- /**
- * Process loan application with multi-phase tracking
- * Combines @TimerNinjaTracker with TimerNinjaBlock for granular tracking
- */
- @TimerNinjaTracker(includeArgs = true, threshold = 100)
- public void processLoanApplication(int userId, double loanAmount, int termMonths) {
- User user = userService.findUser(userId);
-
- // Phase 1: Credit check - block tracking
- TimerNinjaBlock.measure("credit score check", () -> {
- simulateDelay(60); // Simulate credit check
- });
-
- // Phase 2: Income verification - block tracking
- TimerNinjaBlock.measure("income verification", () -> {
- simulateDelay(80); // Simulate income verification
- });
-
- // Phase 3: Risk assessment - block tracking with custom config
- BlockTrackerConfig riskConfig = new BlockTrackerConfig()
- .setTimeUnit(ChronoUnit.MILLIS)
- .setThreshold(30);
-
- TimerNinjaBlock.measure("risk assessment", riskConfig, () -> {
- simulateDelay(40); // Simulate risk assessment
- });
-
- // Phase 4: Final approval - block tracking with return value
- String approvalStatus = TimerNinjaBlock.measure("final approval", () -> {
- simulateDelay(50); // Simulate approval process
- return "APPROVED";
- });
- }
-
- private void simulateDelay(int milliseconds) {
- try {
- Thread.sleep(milliseconds);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- throw new RuntimeException("Processing interrupted", e);
- }
- }
-}
-```
-
-### Output Example
-
-```
-{===== Start of trace context id: ghi789... =====}
-public void processLoanApplication(int userId, double loanAmount, int termMonths) - Args: [userId={123}, loanAmount={50000.0}, termMonths={36}] - 345 ms
- |-- [Block] credit score check - 60 ms
- |-- [Block] income verification - 80 ms
- |-- [Block] risk assessment - 40 ms
- |-- [Block] final approval - 50 ms
-{====== End of trace context id: ghi789... ======}
-```
-
----
-
-## E-commerce Order Processing
-
-### Order Service Example
-
-```java
-@Service
-public class OrderService {
-
- private PaymentService paymentService;
- private InventoryService inventoryService;
- private NotificationService notificationService;
-
- @TimerNinjaTracker
- public Order createOrder(OrderRequest request) {
- // Phase 1: Validate order
- Order order = validateAndCreateOrder(request);
-
- // Phase 2: Process payment
- PaymentResult paymentResult = processPayment(order);
-
- // Phase 3: Update inventory
- updateInventory(order);
-
- // Phase 4: Send confirmation
- sendConfirmation(order);
-
- return order;
- }
-
- @TimerNinjaTracker(threshold = 500, includeArgs = true)
- private PaymentResult processPayment(Order order) {
- return paymentService.charge(
- order.getUserId(),
- order.getPaymentMethod(),
- order.getTotalAmount()
- );
- }
-
- @TimerNinjaTracker(threshold = 200)
- private void updateInventory(Order order) {
- order.getItems().forEach(item -> {
- inventoryService.deductStock(item.getProductId(), item.getQuantity());
- });
- }
-
- @TimerNinjaTracker
- private void sendConfirmation(Order order) {
- notificationService.sendEmailConfirmation(order.getUserEmail(), order);
- }
-}
-```
-
-### Output Example
-
-```
-{===== Start of trace context id: jkl012... =====}
-public Order createOrder(OrderRequest request) - 2150 ms
- |-- public Order validateAndCreateOrder(OrderRequest request) - 120 ms
- |-- public PaymentResult processPayment(Order order) - Args: [order={id=ORD-12345, userId=789, amount=99.99}] - 1250 ms ¤ [Threshold Exceed !!: 500 ms]
- |-- public PaymentResult charge(int userId, String paymentMethod, double amount) - 1180 ms
- |-- public void updateInventory(Order order) - 450 ms
- |-- public void sendConfirmation(Order order) - 330 ms
-{====== End of trace context id: jkl012... ======}
-```
-
----
-
-## API Controller Example
-
-### REST Controller with Tracking
-
-```java
-@RestController
-@RequestMapping("/api/users")
-public class UserController {
-
- private UserService userService;
- private CacheService cacheService;
-
- @TimerNinjaTracker
- @GetMapping("/{id}")
- public ResponseEntity getUser(@PathVariable Long id) {
- User user = userService.findById(id);
- return ResponseEntity.ok(user);
- }
-
- @TimerNinjaTracker(includeArgs = true, threshold = 100)
- @PostMapping
- public ResponseEntity createUser(@RequestBody CreateUserRequest request) {
- User user = userService.create(request);
- return ResponseEntity.status(HttpStatus.CREATED).body(user);
- }
-
- @TimerNinjaTracker(threshold = 200)
- @PutMapping("/{id}")
- public ResponseEntity updateUser(
- @PathVariable Long id,
- @RequestBody UpdateUserRequest request
- ) {
- User user = userService.update(id, request);
- return ResponseEntity.ok(user);
- }
-
- @TimerNinjaTracker
- @DeleteMapping("/{id}")
- public ResponseEntity deleteUser(@PathVariable Long id) {
- userService.delete(id);
- return ResponseEntity.noContent().build();
- }
-}
-```
-
-### Output Example for GET Request
-
-```
-{===== Start of trace context id: mno345... =====}
-public ResponseEntity getUser(Long id) - 85 ms
- |-- public User findById(Long id) - 70 ms
- |-- public User queryDatabase(Long id) - 65 ms
-{====== End of trace context id: mno345... ======}
-```
-
-### Output Example for POST Request
-
-```
-{===== Start of trace context id: pqr678... =====}
-public ResponseEntity createUser(CreateUserRequest request) - Args: [request={name='John Doe', email=john@example.com}] - 350 ms ¤ [Threshold Exceed !!: 100 ms]
- |-- public User create(CreateUserRequest request) - 320 ms
- |-- public void validateRequest(CreateUserRequest request) - 20 ms
- |-- public User saveToDatabase(User user) - 280 ms
- |-- public void invalidateCache(Long userId) - 15 ms
-{====== End of trace context id: pqr678... ======}
-```
-
----
-
-## Data Processing Pipeline
-
-### Batch Processing Example
-
-```java
-@Service
-public class DataProcessingService {
-
- private DataExtractor extractor;
- private DataTransformer transformer;
- private DataLoader loader;
-
- @TimerNinjaTracker
- public void processBatch(String batchId) {
- // Phase 1: Extract data
- List records = extractor.extract(batchId);
-
- // Phase 2: Transform data
- List processed = transformRecords(records);
-
- // Phase 3: Load data
- loader.load(processed);
- }
-
- private List transformRecords(List records) {
- BlockTrackerConfig config = new BlockTrackerConfig()
- .setTimeUnit(ChronoUnit.SECONDS)
- .setThreshold(1);
-
- return TimerNinjaBlock.measure("transform records", config, () -> {
- return records.stream()
- .map(this::transform)
- .collect(Collectors.toList());
- });
- }
-
- @TimerNinjaTracker(threshold = 50)
- private ProcessedRecord transform(DataRecord record) {
- return transformer.transform(record);
- }
-}
-```
-
-### Output Example
-
-```
-{===== Start of trace context id: stu901... =====}
-public void processBatch(String batchId) - 5230 ms
- |-- public List extract(String batchId) - 1500 ms
- |-- [Block] transform records - 3 s
- |-- public ProcessedRecord transform(DataRecord record) - 52 ms ¤ [Threshold Exceed !!: 50 ms]
- |-- public ProcessedRecord transform(DataRecord record) - 55 ms ¤ [Threshold Exceed !!: 50 ms]
- |-- public ProcessedRecord transform(DataRecord record) - 48 ms
- |-- ... (100 more records)
- |-- public void load(List records) - 230 ms
-{====== End of trace context id: stu901... ======}
-```
-
----
-
-## Key Takeaways
-
-1. **Entry Point Tracking**: Track high-level methods to capture full call hierarchies
-2. **Threshold Usage**: Use thresholds to filter noise and focus on slow operations
-3. **Argument Tracking**: Enable `includeArgs` for debugging and performance analysis
-4. **Block Tracking**: Use `TimerNinjaBlock` for granular tracking without method extraction
-5. **Constructor Tracking**: Track initialization chains to identify slow startup times
-6. **Mixed Tracking**: Combine annotation and block tracking for comprehensive monitoring
-
----
-
-## Further Reading
-
-- **[User Guide](User-Guide)** - Detailed feature documentation
-- **[Advanced Usage](Advanced-Usage)** - Advanced features and optimization
-- **[Home](Home)** - Quick start and installation guide
\ No newline at end of file
diff --git a/wiki/Getting-Started.md b/wiki/Getting-Started.md
new file mode 100644
index 0000000..d423ea3
--- /dev/null
+++ b/wiki/Getting-Started.md
@@ -0,0 +1,329 @@
+# 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:**
+```groovy
+implementation group: 'io.github.thanglequoc', name: 'timer-ninja', version: '1.3.0'
+```
+
+**Maven:**
+```xml
+
+ io.github.thanglequoc
+ timer-ninja
+ 1.3.0
+ compile
+
+```
+
+### Declare AspectJ Plugin
+
+**Gradle** — using [FreeFair AspectJ Gradle plugin](https://github.com/freefair/gradle-plugins):
+
+```groovy
+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](https://github.com/dev-aspectj/aspectj-maven-plugin):
+
+```xml
+
+ dev.aspectj
+ aspectj-maven-plugin
+ 1.14.1
+
+
+ org.aspectj
+ aspectjtools
+ 1.9.25
+
+
+
+ ${java.version}
+
+
+ io.github.thanglequoc
+ timer-ninja
+
+
+
+
+
+
+ compile
+ test-compile
+
+
+
+
+```
+
+---
+
+## Annotation-based Tracking
+
+The `@TimerNinjaTracker` annotation is the primary way to track method execution time.
+
+### Basic Usage
+
+Annotate any method to start tracking:
+
+```java
+@TimerNinjaTracker
+public void performTask() {
+ // Your business logic
+}
+```
+
+### Tracking Constructors
+
+You can also track constructor execution:
+
+```java
+@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:
+
+```java
+@TimerNinjaTracker(enabled = false)
+public void dontTrackThis() {
+ // This will NOT be tracked
+}
+```
+
+### Time Unit Selection
+
+Choose the appropriate time unit for your measurement:
+
+```java
+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:
+
+```java
+@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:
+
+```java
+@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
+
+```java
+@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
+
+```java
+public void processData() {
+ TimerNinjaBlock.measure("database query", () -> {
+ database.query("SELECT * FROM users");
+ });
+}
+```
+
+### Block with Return Value
+
+```java
+public void processData() {
+ String result = TimerNinjaBlock.measure("fetch data", () -> {
+ return api.fetchUserData();
+ });
+ System.out.println(result);
+}
+```
+
+### Block with Custom Configuration
+
+```java
+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
+
+```java
+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
+
+1. **Trace Context ID** — Auto-generated UUID. All tracked methods in the same call stack share this ID.
+2. **Trace Timestamp** — When the trace context was initiated (UTC timezone).
+3. **Start/End Markers** — Delimit the trace boundaries.
+4. **Method Lines** — Each tracked method shows: signature, arguments (if enabled), execution time, threshold indicator.
+5. **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:
+
+```java
+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:
+
+```xml
+
+```
+
+---
+
+## Further Reading
+
+- **[Advanced Usage](Advanced-Usage)** — Advanced features, real-world examples, and optimization techniques
+- **[Home](Home)** — Quick start and installation guide
diff --git a/wiki/Home.md b/wiki/Home.md
index 9d43963..967541c 100644
--- a/wiki/Home.md
+++ b/wiki/Home.md
@@ -169,9 +169,8 @@ public void requestMoneyTransfer(int sourceUserId, int targetUserId, int amount)
## Documentation
-- **[User Guide](User-Guide)** - Detailed feature documentation and configuration options
-- **[Examples](Examples)** - Real-world usage examples and patterns
-- **[Advanced Usage](Advanced-Usage)** - Advanced features and optimization techniques
+- **[Getting Started](Getting-Started)** — Installation, feature documentation, and configuration options
+- **[Advanced Usage](Advanced-Usage)** — Real-world examples, advanced features, and optimization techniques
## Example Projects
diff --git a/wiki/User-Guide.md b/wiki/User-Guide.md
deleted file mode 100644
index 64103ad..0000000
--- a/wiki/User-Guide.md
+++ /dev/null
@@ -1,463 +0,0 @@
-# User Guide 📘
-
-This guide provides detailed documentation on how to use Timer Ninja's features effectively.
-
-## Table of Contents
-
-1. [Annotation-based Tracking](#annotation-based-tracking)
-2. [Block Tracking](#block-tracking)
-3. [Configuration Options](#configuration-options)
-4. [Understanding Trace Output](#understanding-trace-output)
-5. [Best Practices](#best-practices)
-
----
-
-## Annotation-based Tracking
-
-The `@TimerNinjaTracker` annotation is the primary way to track method execution time.
-
-### Basic Usage
-
-Annotate any method or constructor to start tracking:
-
-```java
-@TimerNinjaTracker
-public void performTask() {
- // Your business logic
-}
-```
-
-### Tracking Constructors
-
-You can also track constructor execution:
-
-```java
-@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
-
-The `@TimerNinjaTracker` annotation supports several configuration options:
-
-| 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
-
-### 1. Enable/Disable Tracking
-
-Control whether a specific method is tracked:
-
-```java
-@TimerNinjaTracker(enabled = true)
-public void trackThis() {
- // This will be tracked
-}
-
-@TimerNinjaTracker(enabled = false)
-public void dontTrackThis() {
- // This will NOT be tracked
-}
-```
-
-**Use Case:** Temporarily disable tracking for a method without removing the annotation.
-
-### 2. Time Unit Selection
-
-Choose the appropriate time unit for your measurement needs:
-
-```java
-import java.time.temporal.ChronoUnit;
-
-@TimerNinjaTracker(timeUnit = ChronoUnit.SECONDS)
-public void longRunningOperation() {
- // For operations taking seconds
-}
-
-@TimerNinjaTracker(timeUnit = ChronoUnit.MILLIS)
-public void standardOperation() {
- // For operations taking milliseconds (default)
-}
-
-@TimerNinjaTracker(timeUnit = ChronoUnit.MICROS)
-public void preciseOperation() {
- // For operations requiring microsecond precision
-}
-```
-
-**Supported Units:**
-- `ChronoUnit.SECONDS` - Seconds
-- `ChronoUnit.MILLIS` - Milliseconds (default)
-- `ChronoUnit.MICROS` - Microseconds
-
-### 3. Include Method Arguments
-
-Log method arguments for better debugging context:
-
-```java
-@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:
-
-```java
-public class User {
- private int id;
- private String name;
- private String email;
-
- @Override
- public String toString() {
- return String.format("User{id=%d, name='%s', email='%s'}", id, name, email);
- }
-}
-```
-
-### 4. Threshold Filtering
-
-Filter out fast methods to focus on performance issues:
-
-```java
-@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
-
-**Combining with Arguments:**
-```java
-@TimerNinjaTracker(includeArgs = true, threshold = 200)
-public void requestMoneyTransfer(int sourceUserId, int targetUserId, int amount) {
- // Only logs slow transfers with full argument details
-}
-```
-
-**Use Case:** Focus on slow operations while seeing the exact arguments that caused the delay.
-
----
-
-## Block Tracking
-
-For granular tracking within a method without extracting separate methods, use `TimerNinjaBlock`.
-
-### Basic Block Tracking
-
-```java
-public void processData() {
- // Regular code not tracked
-
- TimerNinjaBlock.measure("database query", () -> {
- database.query("SELECT * FROM users");
- });
-
- // More code not tracked
-}
-```
-
-### Block with Return Value
-
-```java
-public void processData() {
- String result = TimerNinjaBlock.measure("fetch data", () -> {
- return api.fetchUserData();
- });
-
- System.out.println(result);
-}
-```
-
-### Block with Custom Configuration
-
-```java
-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
-
-```java
-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
-
-1. **Trace Context ID**: Auto-generated UUID for a trace
- - Initiated by the first `@TimerNinjaTracker` method encountered
- - All subsequent tracked methods in the same call stack share this ID
-
-2. **Trace Timestamp**: When the trace context was initiated (UTC timezone)
-
-3. **Start/End Markers**: Delimit the trace boundaries
-
-4. **Method Lines**: Each tracked method shows:
- - Method signature
- - Arguments (if `includeArgs = true`)
- - Execution time with time unit
- - Threshold indicator (if exceeded)
-
-5. **Indentation (`|--`)**: Shows call hierarchy
- - Indented methods are called by the method above
- - Helps visualize the execution stacktrace
-
-### 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
-```
-
-This summary shows the range and total execution time without detailed traces.
-
----
-
-## Global Configuration
-
-Timer Ninja uses a singleton configuration class for global settings.
-
-### Enable System.out Logging
-
-For simple console applications or quick testing:
-
-```java
-TimerNinjaConfiguration.getInstance().toggleSystemOutLog(true);
-```
-
-**Note:** 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 see debug information:
-
-```
-
-```
-
-**Use Case:** Troubleshooting tracking issues or understanding internal behavior.
-
----
-
-## Best Practices
-
-### 1. Choose Appropriate Time Units
-
-- **Seconds**: For long-running operations (API calls, file I/O, batch processing)
-- **Milliseconds**: For general application logic (default)
-- **Microseconds**: For performance-critical code (algorithms, calculations)
-
-### 2. Use Thresholds Strategically
-
-```java
-// Too low - noise
-@TimerNinjaTracker(threshold = 10)
-
-// Too high - miss issues
-@TimerNinjaTracker(threshold = 5000)
-
-// Balanced - catches real issues
-@TimerNinjaTracker(threshold = 200)
-```
-
-### 3. Combine with Arguments for Debugging
-
-```java
-@TimerNinjaTracker(includeArgs = true, threshold = 200)
-public void searchUsers(String query, int limit) {
- // See which queries are slow
-}
-```
-
-### 4. Track Entry Points
-
-Add tracking to high-level entry points (REST controllers, main methods) to capture full execution traces:
-
-```java
-@RestController
-public class UserController {
-
- @TimerNinjaTracker
- @GetMapping("/users/{id}")
- public User getUser(@PathVariable Long id) {
- // All nested tracked methods will appear in the trace
- return userService.findById(id);
- }
-}
-```
-
-### 5. Use Block Tracking for Phases
-
-For methods with distinct phases, use block tracking instead of breaking into small methods:
-
-```java
-public void processOrder(Order order) {
- // Validation phase
- TimerNinjaBlock.measure("validation", () -> {
- validateOrder(order);
- });
-
- // Processing phase
- TimerNinjaBlock.measure("processing", () -> {
- processPayment(order);
- updateInventory(order);
- });
-
- // Notification phase
- TimerNinjaBlock.measure("notification", () -> {
- sendConfirmation(order);
- });
-}
-```
-
-### 6. Disable in Production When Needed
-
-```java
-@TimerNinjaTracker(enabled = isDevelopment())
-public void debugMethod() {
- // Only tracked in development environment
-}
-
-private boolean isDevelopment() {
- return "dev".equals(System.getProperty("environment"));
-}
-```
-
-### 7. Don't Track Everything
-
-Focus on:
-- Critical business logic
-- External API calls
-- Database operations
-- File I/O operations
-- Complex algorithms
-
-Avoid tracking:
-- Simple getters/setters
-- Very fast operations (< 1ms)
-- Trivial utility methods
-
----
-
-## Troubleshooting
-
-### No Output in Logs
-
-1. Check if SLF4J provider is configured
-2. Verify log level is at least INFO
-3. Enable System.out for testing:
- ```java
- TimerNinjaConfiguration.getInstance().toggleSystemOutLog(true);
- ```
-
-### Methods Not Being Tracked
-
-1. Verify AspectJ plugin is configured correctly
-2. Check that the dependency includes the aspect:
- ```groovy
- aspect 'io.github.thanglequoc:timer-ninja:1.2.0'
- ```
-3. Ensure `enabled = true` (or not set) on the annotation
-
-### Missing Method Arguments
-
-1. Set `includeArgs = true` on the annotation
-2. Verify `toString()` is implemented for argument objects
-3. Check if objects are null or contain sensitive data
-
-### Inconsistent Tracking
-
-1. Ensure you're using the correct version (1.2.0 or later)
-2. Check for conflicting AOP configurations
-3. Review logs for DEBUG information
- ```xml
-
- ```
-
----
-
-## Further Reading
-
-- **[Examples](Examples)** - Real-world usage patterns
-- **[Advanced Usage](Advanced-Usage)** - Advanced features and optimization
-- **[Home](Home)** - Quick start and installation guide
\ No newline at end of file
From aba49a3e36ed5d5819df86a510a1ab20c0622e05 Mon Sep 17 00:00:00 2001
From: thanglequoc
Date: Thu, 12 Feb 2026 11:41:37 +0700
Subject: [PATCH 2/2] feat: Rework document, add GA
---
docs/_config.yml | 14 +++++++++++++-
docs/_includes/head.html | 39 +++++++++++++++++++++++++++++++++++++++
docs/advanced-usage.md | 2 +-
docs/getting-started.md | 2 +-
docs/index.html | 27 +++++++++++++++++++++++----
docs/robots.txt | 8 ++++++++
6 files changed, 85 insertions(+), 7 deletions(-)
create mode 100644 docs/robots.txt
diff --git a/docs/_config.yml b/docs/_config.yml
index 188d89e..c794a61 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,7 +1,17 @@
title: Timer Ninja
-description: "A sneaky library for Java Method Timing — Track execution time with a single annotation. Zero boilerplate."
+tagline: "Measure Java Method Execution Time with a Single Annotation"
+description: "Timer Ninja is a lightweight Java library to measure method execution time, track code performance, and visualize call hierarchies — with just one annotation. No heavy frameworks required."
url: "https://thanglequoc.github.io"
baseurl: "/timer-ninja"
+lang: en
+
+# Social / Open Graph
+twitter:
+ card: summary_large_image
+social:
+ name: Timer Ninja
+ links:
+ - https://github.com/thanglequoc/timer-ninja
# Build settings
markdown: kramdown
@@ -16,6 +26,7 @@ sass:
# Plugins
plugins:
- jekyll-seo-tag
+ - jekyll-sitemap
# Defaults
defaults:
@@ -24,6 +35,7 @@ defaults:
type: "pages"
values:
layout: "default"
+ image: /assets/images/mascot.png
# Exclude from build
exclude:
diff --git a/docs/_includes/head.html b/docs/_includes/head.html
index 7035025..b403712 100644
--- a/docs/_includes/head.html
+++ b/docs/_includes/head.html
@@ -2,6 +2,15 @@
+
+
+
+
{% if page.title %}{{ page.title }} — {{ site.title }}{% else %}{{ site.title }}{% endif %}
@@ -22,6 +31,36 @@
{% seo %}
+
+
+
+
+
+{% if page.url == "/" or page.url == "/index.html" %}
+
+{% endif %}
+