Developer Integration Guide¶
This document provides integration guides and API references for the JAiRouter distributed tracing system for developers.
Core APIs¶
TracingService¶
The core interface of the tracing service, providing Span management and tracing context operations.
Main Methods¶
@Component
public class TracingService {
/**
* Create a root Span
* @param operation Operation name
* @return Span object
*/
public Span createRootSpan(String operation);
/**
* Create a child Span
* @param operation Operation name
* @return Span object
*/
public Span createChildSpan(String operation);
/**
* Execute operation in current context
* @param operation Operation name
* @param function Execution function
* @return Execution result
*/
public <T> T withSpan(String operation, Function<Span, T> function);
/**
* Add tag to current Span
* @param key Tag key
* @param value Tag value
*/
public void addTag(String key, String value);
/**
* Record event to current Span
* @param event Event name
*/
public void addEvent(String event);
/**
* Record event to current Span (with attributes)
* @param event Event name
* @param attributes Event attributes
*/
public void addEvent(String event, Map<String, Object> attributes);
/**
* Record exception to current Span
* @param throwable Exception object
*/
public void recordException(Throwable throwable);
}
TracingContext¶
Tracing context manager for getting and manipulating tracing information of the current thread.
public class TracingContext {
/**
* Get current context
* @return TracingContext object
*/
public static Optional<TracingContext> current();
/**
* Get current Trace ID
* @return Trace ID
*/
public static String getCurrentTraceId();
/**
* Get current Span ID
* @return Span ID
*/
public static String getCurrentSpanId();
/**
* Add attribute to current context
* @param key Attribute key
* @param value Attribute value
*/
public void addAttribute(String key, Object value);
/**
* Get attribute value
* @param key Attribute key
* @return Attribute value
*/
public Object getAttribute(String key);
}
Custom Tracing Components¶
Implementing Custom Sampling Strategy¶
@Component
public class CustomSamplingStrategy implements SamplingStrategy {
@Override
public boolean shouldSample(SamplingContext context) {
// Custom sampling logic
String userId = context.getAttribute("userId");
String operation = context.getOperation();
// 100% sampling for VIP users
if (isVipUser(userId)) {
return true;
}
// 50% sampling for critical operations
if (isCriticalOperation(operation)) {
return Math.random() < 0.5;
}
// Default 10% sampling
return Math.random() < 0.1;
}
@Override
public String getName() {
return "custom";
}
}
Creating Custom Exporter¶
@Component
public class CustomExporter implements SpanExporter {
private final HttpClient httpClient;
public CustomExporter(HttpClient httpClient) {
this.httpClient = httpClient;
}
@Override
public CompletableResultCode export(Collection<SpanData> spans) {
try {
// Custom export logic
List<CustomSpan> customSpans = spans.stream()
.map(this::convertToCustomSpan)
.collect(Collectors.toList());
// Send to custom backend
HttpResponse response = httpClient.post("/traces")
.body(customSpans)
.execute();
return response.isSuccess() ?
CompletableResultCode.ofSuccess() :
CompletableResultCode.ofFailure();
} catch (Exception e) {
log.error("Failed to export tracing data", e);
return CompletableResultCode.ofFailure();
}
}
@Override
public CompletableResultCode flush() {
// Flush logic
return CompletableResultCode.ofSuccess();
}
@Override
public CompletableResultCode shutdown() {
// Shutdown logic
return CompletableResultCode.ofSuccess();
}
private CustomSpan convertToCustomSpan(SpanData spanData) {
// Conversion logic
return new CustomSpan();
}
}
Annotation Support¶
@TraceSpan Annotation¶
Method-level annotation for automatically creating Spans.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TraceSpan {
/**
* Span name
*/
String name() default "";
/**
* Tag key-value pairs
*/
String[] tags() default {};
/**
* Whether to record exceptions
*/
boolean recordException() default true;
}
Usage example:
@Service
public class UserService {
@TraceSpan(name = "user-authentication", tags = {"operation", "login"})
public User authenticate(String username, String password) {
// Authentication logic
return userRepository.findByUsername(username);
}
@TraceSpan(name = "user-profile-update")
public void updateUserProfile(UserProfile profile) {
// Update user profile
userProfileRepository.save(profile);
}
}
@TraceAsync Annotation¶
Tracing annotation for asynchronous methods.
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
@TraceAsync("data-processing")
@PostMapping("/api/process")
public CompletableFuture<ProcessResult> processData(@RequestBody DataRequest request) {
return asyncService.processData(request);
}
}
Reactive Programming Integration¶
Mono/Flux Tracing Support¶
@Component
public class ReactiveTracingService {
public <T> Mono<T> traceMono(String operation, Mono<T> mono) {
return Mono.defer(() -> {
Span span = tracingService.createChildSpan(operation);
return mono
.doOnSuccess(result -> {
span.addTag("result", "success");
span.end();
})
.doOnError(error -> {
span.recordException(error);
span.addTag("result", "error");
span.end();
});
});
}
public <T> Flux<T> traceFlux(String operation, Flux<T> flux) {
return Flux.defer(() -> {
Span span = tracingService.createChildSpan(operation);
AtomicLong count = new AtomicLong(0);
return flux
.doOnNext(item -> count.incrementAndGet())
.doOnComplete(() -> {
span.addTag("item.count", String.valueOf(count.get()));
span.addTag("result", "success");
span.end();
})
.doOnError(error -> {
span.recordException(error);
span.addTag("result", "error");
span.end();
});
});
}
}
Usage example:
@Service
public class DataService {
@Autowired
private ReactiveTracingService reactiveTracingService;
public Mono<DataResult> fetchData(String id) {
return reactiveTracingService.traceMono("fetch-data",
dataRepository.findById(id)
.map(this::transformData));
}
public Flux<DataItem> streamData() {
return reactiveTracingService.traceFlux("stream-data",
dataRepository.findAll()
.filter(this::isValidItem));
}
}
Extensions and Plugins¶
Creating Custom Tracing Interceptor¶
@Component
public class CustomTracingInterceptor implements TracingInterceptor {
@Override
public void preHandle(TracingContext context, Span span) {
// Pre-handle processing
span.addTag("custom.interceptor", "pre-handle");
// Add custom attributes
String userAgent = getCurrentUserAgent();
if (userAgent != null) {
span.addTag("http.user_agent", userAgent);
}
}
@Override
public void postHandle(TracingContext context, Span span, Object result) {
// Post-handle processing
if (result instanceof ResponseEntity) {
ResponseEntity<?> response = (ResponseEntity<?>) result;
span.addTag("http.status_code", String.valueOf(response.getStatusCodeValue()));
}
}
@Override
public void afterCompletion(TracingContext context, Span span, Exception ex) {
// After completion processing
if (ex != null) {
span.recordException(ex);
}
span.end();
}
}
Custom Metrics Collector¶
@Component
public class CustomMetricsCollector {
private final MeterRegistry meterRegistry;
private final Counter customCounter;
private final Timer customTimer;
public CustomMetricsCollector(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.customCounter = Counter.builder("custom.tracing.operations")
.description("Custom tracing operation count")
.register(meterRegistry);
this.customTimer = Timer.builder("custom.tracing.duration")
.description("Custom tracing operation duration")
.register(meterRegistry);
}
public <T> T measureOperation(String operation, Supplier<T> supplier) {
customCounter.increment(1.0, "operation", operation);
return Timer.Sample.start(meterRegistry)
.stop(customTimer.tag("operation", operation));
}
}
Test Support¶
Unit Testing¶
@ExtendWith(MockitoExtension.class)
class TracingServiceTest {
@Mock
private SpanExporter spanExporter;
@InjectMocks
private TracingService tracingService;
@Test
void testCreateRootSpan() {
// Given
String operation = "test-operation";
// When
Span span = tracingService.createRootSpan(operation);
// Then
assertThat(span).isNotNull();
assertThat(span.getName()).isEqualTo(operation);
}
@Test
void testAddTag() {
// Given
Span span = tracingService.createRootSpan("test");
// When
tracingService.addTag("test.key", "test.value");
// Then
// Verify tag was added to Span
}
}
Integration Testing¶
@SpringBootTest
@AutoConfigureTestDatabase
class TracingIntegrationTest {
@Autowired
private TestRestTemplate restTemplate;
@Autowired
private TracingService tracingService;
@Test
void testTracingIntegration() {
// Given
HttpHeaders headers = new HttpHeaders();
headers.set("X-Trace-Debug", "true");
HttpEntity<String> entity = new HttpEntity<>("{}", headers);
// When
ResponseEntity<String> response = restTemplate.exchange(
"/api/test", HttpMethod.POST, entity, String.class);
// Then
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
// Verify tracing data was generated
verify(tracingService).createRootSpan(anyString());
}
}
Best Practices¶
1. Span Naming Conventions¶
// ✅ Good naming
tracingService.createSpan("user-authentication");
tracingService.createSpan("database-query");
tracingService.createSpan("external-api-call");
// ❌ Avoid naming
tracingService.createSpan("op1");
tracingService.createSpan("doSomething");
2. Tag Usage Conventions¶
// ✅ Meaningful tags
span.addTag("http.method", "POST");
span.addTag("user.id", userId);
span.addTag("business.operation", "payment");
// ❌ Avoid tags
span.addTag("tag1", "value");
span.addTag("data", largeObject.toString()); // Too large value
3. Exception Handling¶
// ✅ Proper exception recording
try {
performOperation();
} catch (BusinessException e) {
span.recordException(e);
span.addTag("error.type", "business");
throw e;
}
// ❌ Avoid recording sensitive information
try {
performOperation();
} catch (Exception e) {
span.addTag("error.details", e.getMessage()); // May contain sensitive information
throw e;
}
4. Performance Considerations¶
// ✅ Efficient tracing
@TraceSpan("critical-operation")
public void criticalOperation() {
// Critical business logic
}
// ❌ Avoid over-tracing in high-frequency paths
@TraceSpan("high-frequency-op") // May impact performance
public void highFrequencyOperation() {
// High-frequency method calls
}
Next Steps¶
- API Reference - Detailed API documentation
- Configuration Reference - Complete configuration options
- Performance Tuning - Performance optimization guide