This guide explains how to use Jaeger for distributed tracing in IRCTC microservices. Jaeger provides end-to-end visibility into request flows across all services.
Distributed tracing tracks requests as they flow through multiple microservices, providing:
- Request Flow Visualization: See how requests traverse services
- Performance Analysis: Identify bottlenecks and slow operations
- Error Tracking: Trace errors across service boundaries
- Dependency Mapping: Understand service dependencies
Client Request
β
API Gateway (creates trace)
β
Service A (adds span)
β
Service B (adds span)
β
Service C (adds span)
β
Jaeger (collects and visualizes)
- Jaeger All-in-One: Complete Jaeger stack (collector, query, UI)
- OTLP Exporter: OpenTelemetry Protocol exporter in each service
- Micrometer Tracing: Spring Boot integration for tracing
# Using Docker Compose
docker-compose up -d jaeger
# Or using Docker directly
docker run -d \
--name irctc-jaeger \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
jaegertracing/all-in-one:latestOpen your browser and navigate to:
http://localhost:16686
Make requests to your services:
# Example: Create a booking
curl -X POST http://localhost:8090/api/bookings \
-H "Content-Type: application/json" \
-d '{
"userId": 1,
"trainId": 1,
"seatCount": 2
}'- Go to Jaeger UI: http://localhost:16686
- Select service:
irctc-api-gatewayor any service - Click "Find Traces"
- View the trace timeline
Jaeger is configured in docker-compose.yml:
jaeger:
image: jaegertracing/all-in-one:latest
container_name: irctc-jaeger
ports:
- "16686:16686" # Jaeger UI
- "4317:4317" # OTLP gRPC receiver
- "4318:4318" # OTLP HTTP receiver
environment:
- COLLECTOR_OTLP_ENABLED=true
- SPAN_STORAGE_TYPE=badgerEach service is configured in application.yml:
management:
tracing:
enabled: true
sampling:
probability: 1.0 # 100% sampling for development
otlp:
tracing:
endpoint: http://localhost:4318/v1/traces
export:
enabled: trueAll services include tracing dependencies:
<!-- Tracing: Micrometer + OpenTelemetry -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>All microservices are configured for tracing:
- β
API Gateway (
irctc-api-gateway) - β
User Service (
irctc-user-service) - β
Train Service (
irctc-train-service) - β
Booking Service (
irctc-booking-service) - β
Payment Service (
irctc-payment-service) - β
Notification Service (
irctc-notification-service)
- Service Selection: Choose a service from the dropdown
- Time Range: Select time range (Last 15 minutes, 1 hour, etc.)
- Operation: Filter by operation name (optional)
- Tags: Add tags for filtering (e.g.,
http.status_code=200) - Click "Find Traces"
- Timeline View: See spans arranged by time
- Service Map: Visualize service dependencies
- Span Details: Click on spans to see:
- Duration
- Tags (HTTP method, status code, etc.)
- Logs
- Service name
Trace: POST /api/bookings
βββ API Gateway (100ms)
β βββ Authentication (10ms)
β βββ Routing (5ms)
βββ Booking Service (500ms)
β βββ Validate Request (20ms)
β βββ Check Availability (200ms)
β β βββ Train Service Call (180ms)
β βββ Create Booking (150ms)
β βββ Process Payment (100ms)
β βββ Payment Service Call (90ms)
βββ Notification Service (50ms)
βββ Send Confirmation (45ms)
Development:
sampling:
probability: 1.0 # 100% - capture all tracesProduction:
sampling:
probability: 0.1 # 10% - reduce overheadAdd custom spans for important operations:
import io.micrometer.tracing.Span;
import io.micrometer.tracing.Tracer;
@Service
public class BookingService {
private final Tracer tracer;
public Booking createBooking(BookingRequest request) {
Span span = tracer.nextSpan()
.name("create-booking")
.tag("user.id", request.getUserId())
.tag("train.id", request.getTrainId())
.start();
try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
// Business logic
return bookingRepository.save(booking);
} finally {
span.end();
}
}
}Add tags to spans for better filtering:
span.tag("booking.id", booking.getId());
span.tag("payment.status", payment.getStatus());
span.tag("user.role", user.getRole());Errors are automatically captured in traces:
try {
// Operation
} catch (Exception e) {
span.tag("error", true);
span.tag("error.message", e.getMessage());
throw e;
}Problem: Slow booking creation
Solution:
- Search for traces with operation
POST /api/bookings - Sort by duration (longest first)
- Identify slow spans
- Analyze service dependencies
Problem: Payment failures
Solution:
- Filter by tag:
error=true - Filter by service:
payment-service - View error messages in span details
- Trace back to root cause
Problem: Understand service interactions
Solution:
- Go to "Dependencies" tab in Jaeger UI
- View service dependency graph
- Identify critical paths
- Plan for service isolation
Check:
- Jaeger is running:
docker ps | grep jaeger - Services are configured: Check
application.yml - Tracing is enabled:
management.tracing.enabled=true - OTLP endpoint is correct:
http://localhost:4318/v1/traces
Check:
- Service dependencies are correct
- Feign clients have tracing enabled
- Custom spans are properly closed
Solution:
- Reduce sampling rate
- Use span storage limits
- Configure retention policies
Jaeger metrics can be exported to Prometheus:
# In prometheus.yml
scrape_configs:
- job_name: 'jaeger'
static_configs:
- targets: ['jaeger:14269']View Jaeger traces in Grafana:
- Install Jaeger data source plugin
- Configure Jaeger URL:
http://jaeger:16686 - Create dashboard with trace panels
For production, use persistent storage:
jaeger:
environment:
- SPAN_STORAGE_TYPE=elasticsearch
- ES_SERVER_URLS=http://elasticsearch:9200Use adaptive sampling:
sampling:
probability: 0.1 # 10% base rate
# Or use head-based sampling in JaegerDeploy Jaeger in HA mode:
- Separate collector, query, and storage
- Use load balancers
- Configure replication
- Custom Instrumentation: Add custom spans for business operations
- Alerting: Set up alerts for slow traces
- Service Map: Use dependency graph for architecture decisions
- Performance Baselines: Establish performance SLAs
Last Updated: November 2025