High-Performance Distributed Tracing for Microservices
Bus Tracer is a distributed tracing framework that provides real-time monitoring and performance analysis for microservices architectures. It helps developers track requests as they propagate through distributed systems, making it easier to diagnose performance bottlenecks and errors.
- Request Tracking: Trace requests across service boundaries
- Span Management: Automatic span creation and propagation
- Context Propagation: Seamless context passing between services
- Transaction Correlation: Correlate related operations across services
- Performance Metrics: Capture timing data for each operation
- Call Graphs: Visual representation of service calls
- Timeline Views: Time-based view of request execution
- Service Maps: Topology view of service dependencies
- Performance Heatmaps: Identify bottlenecks visually
- Error Tracking: Track and visualize error propagation
- Latency Analysis: Identify slow operations and services
- Dependency Analysis: Understand service dependencies
- Error Analysis: Track error patterns and root causes
- Throughput Analysis: Monitor request rates and capacities
- Resource Usage: Track resource consumption per request
<dependency>
<groupId>org.miaixz</groupId>
<artifactId>bus-tracer</artifactId>
<version>x.x.x</version>
</dependency>@EnableTracing
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}import org.miaixz.bus.tracer.Tracer;
import org.miaixz.bus.tracer.Span;
@Service
public class UserService {
@Autowired
private Tracer tracer;
public User getUser(String userId) {
// Create a new span
Span span = tracer.createSpan("getUser");
try {
// Your business logic
User user = userRepository.findById(userId);
return user;
} finally {
// Close the span
span.close();
}
}
}import org.miaixz.bus.tracer.annotation.Trace;
@Service
public class OrderService {
@Trace(operationName = "createOrder")
public Order createOrder(OrderRequest request) {
// Method is automatically traced
return orderRepository.save(request);
}
@Trace
public void processPayment(Order order) {
// Method is automatically traced with default operation name
paymentService.charge(order);
}
}@Service
public class ProductService {
@Trace(operationName = "getProduct")
public Product getProduct(String productId) {
Span span = tracer.getCurrentSpan();
if (span != null) {
// Add custom tags
span.tag("product.id", productId);
span.tag("product.category", "electronics");
}
return productRepository.findById(productId);
}
}@Service
public class CartService {
@Trace(operationName = "addToCart")
public void addToCart(String userId, String productId) {
Span span = tracer.getCurrentSpan();
if (span != null) {
// Add baggage (propagates to downstream services)
span.setBaggageItem("user.id", userId);
span.setBaggageItem("session.id", getSessionId());
}
cartService.addItem(productId);
}
}@Service
public class ApiService {
@Trace(operationName = "externalApiCall", sampler = ProbabilisticSampler.class)
public Response callExternalAPI(Request request) {
// Only traced if sampler allows
return restTemplate.postForObject(apiUrl, request, Response.class);
}
}@Service
public class PaymentService {
@Trace(operationName = "processPayment")
public PaymentResult processPayment(Payment payment) {
Span span = tracer.getCurrentSpan();
try {
return paymentGateway.charge(payment);
} catch (PaymentException e) {
if (span != null) {
span.tag("error", "true");
span.tag("error.message", e.getMessage());
span.log("payment_failed");
}
throw e;
}
}
}extend:
tracer:
enabled: true
application-name: my-application
sampler-type: probabilistic
sampler-rate: 0.1 # 10% of requestsextend:
tracer:
enabled: true
application-name: my-application
# Sampling configuration
sampler-type: rate-limiting
sampler-rate: 100 # max 100 traces per second
# Exporter configuration
exporter-type: zipkin
zipkin-url: http://localhost:9411
# Span configuration
max-span-count: 1000
span-timeout: 30000 # 30 seconds
# Baggage configuration
baggage-limit: 10
baggage-max-length: 256extend:
tracer:
exporter-type: zipkin
zipkin-url: http://zipkin:9411
zipkin-sender: httpextend:
tracer:
exporter-type: jaeger
jaeger-url: http://jaeger:14268
jaeger-sender: http@Component
public class TracingMetrics {
@EventListener
public void handleSpanFinished(SpanFinishedEvent event) {
Span span = event.getSpan();
// Record metrics
meterRegistry.timer("tracer.span.duration",
"operation", span.getOperationName(),
"status", span.getStatus().name()
).record(span.getDuration(), TimeUnit.MICROSECONDS);
// Count errors
if (span.getStatus() == Status.ERROR) {
meterRegistry.counter("tracer.span.errors",
"operation", span.getOperationName()
).increment();
}
}
}@Component
public class CustomExporter implements SpanExporter {
@Override
public void export(List<Span> spans) {
// Export to your backend
for (Span span : spans) {
// Process span data
tracingRepository.save(span);
}
}
}// Good
@Trace(operationName = "user.login")
public User login(String username, String password) {
// ...
}
// Avoid
@Trace // Uses method name
public User login(String username, String password) {
// ...
}@Trace(operationName = "database.query")
public List<User> getUsersByStatus(UserStatus status) {
Span span = tracer.getCurrentSpan();
span.tag("db.type", "postgresql");
span.tag("db.operation", "select");
span.tag("db.table", "users");
span.tag("query.status", status.name());
return userRepository.findByStatus(status);
}@Trace(operationName = "payment.process")
public PaymentResult processPayment(Payment payment) {
Span span = tracer.getCurrentSpan();
try {
return paymentGateway.charge(payment);
} catch (Exception e) {
span.tag("error", "true");
span.tag("error.type", e.getClass().getSimpleName());
span.tag("error.message", e.getMessage());
span.log(Collections.singletonMap("event", "error"));
throw e;
}
}For high-traffic services, use sampling to reduce overhead:
extend:
tracer:
sampler-type: probabilistic
sampler-rate: 0.01 # Trace 1% of requests| Bus Tracer Version | Spring Boot Version | JDK Version |
|---|---|---|
| 8.x | 3.x+ | 17+ |
| 7.x | 2.x+ | 11+ |
A: Typically less than 5% overhead when using sampling. Can be adjusted based on sampling rate.
A: Yes, the tracer properly handles async context propagation.
A: Use tag sanitizers or configure specific tags to be redacted.
A: Yes, you can configure multiple exporters to send traces to different backends.
Contributions are welcome! Please feel free to submit a Pull Request.