NutriFlow — Trazabilidad Distribuida

Jaeger 1.54 + OpenTelemetry SDK · 4 microservicios · Entorno: staging

● Live Última traza: hace 3s
Trazas / hora
1,247
↑ 12% vs. última hora
Latencia P95 global
2.31s
POST /api/plans/generate
Trazas con error
3.4%
42 trazas en la última hora
Servicios instrumentados
4 / 4
OpenTelemetry SDK activo

Cuello de botella detectado — nutrition-service · span: ai_model_inference

El 76% de la latencia total se concentra en la llamada al modelo IA (P99: 5.8s). Se recomienda revisar el timeout del modelo y activar caché de planes similares en Redis.

TRAZA REPRESENTATIVA · POST /api/plans/generate
Trace ID: 4bf92f3577b34da6a3ce929d0e0e4736
Duración total 2.847s
Spans 14
Servicios 4
Estado 1 error
Servicio · Operación
Timeline (0 → 2847ms)
api-gateway · HTTP POST /api/plans/generate
2847ms
auth_middleware (JWT validate)
80ms
rate_limit_check (Redis)
22ms
patient-service · GET /internal/patients/:id
267ms
db.query — SELECT patients (PostgreSQL)
127ms
db.query — SELECT nutrition_history
99ms
nutrition-service · POST /internal/plans/generate
2332ms ← cuello de botella
validate_plan_input
19ms
cache.get — Redis (MISS)
14ms
ai_model_inference · NutriGPT v2.1 (CPU)
2165ms ⚠ P99: 5.8s
db.query — INSERT nutrition_plans
71ms
notification-service · POST /internal/notify (email)
160ms
serialize_response (JSON)
28ms
0 284ms 569ms 853ms 1.14s 1.42s 1.71s 1.99s 2.28s 2.56s 2.85s
Servicios Instrumentados
🔀
api-gateway
Node.js 20 Express 4.x JWT OTLP gRPC
108ms
P99 · 1,247 req/h
🥗
nutrition-service
Python 3.11 Flask 3.x SQLAlchemy 100% sampling
5.8s
P99 · 1,247 req/h
👤
patient-service
Python 3.11 Flask 3.x PostgreSQL 15
312ms
P99 · 1,247 req/h
🔔
notification-service
Node.js 20 SMTP Push API
198ms
P99 · 1,247 req/h
Estrategia de Muestreo (jaeger-sampling.json)
nutrition-service
probabilistic · ruta crítica IA
100%
api-gateway
probabilistic
50%
patient-service
probabilistic (default)
10%
notification-service
probabilistic (default)
10%
GET /health, /ready
operation override
0.1%
Distribución de latencia — última hora
<100ms
187
100-500
312
500-1s
431
1-3s
218
3-6s
82
>6s
17
INSTRUMENTACION · nutrition-service/tracing.py
OpenTelemetry SDK · Python 3.11 · Exporter: OTLP gRPC → Jaeger :4317
Python
# nutrition-service/tracing.py — NutriFlow from opentelemetry import trace from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter from opentelemetry.sdk.resources import Resource from opentelemetry.instrumentation.flask import FlaskInstrumentor from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor from opentelemetry.instrumentation.redis import RedisInstrumentor def init_tracing(service_name: str): resource = Resource.create({ "service.name": service_name, "service.version": "2.1.0", "deployment.environment": "staging", "team": "nutriflow-backend", }) provider = TracerProvider(resource=resource) exporter = OTLPSpanExporter(endpoint="http://jaeger:4317", insecure=True) provider.add_span_processor(BatchSpanProcessor(exporter)) trace.set_tracer_provider(provider) FlaskInstrumentor().instrument() SQLAlchemyInstrumentor().instrument() RedisInstrumentor().instrument() return trace.get_tracer(service_name) # app.py — span personalizado para la inferencia IA tracer = init_tracing("nutrition-service") @app.route("/internal/plans/generate", methods=["POST"]) def generate_plan(): with tracer.start_as_current_span("ai_model_inference") as span: span.set_attribute("model.name", "NutriGPT-v2.1") span.set_attribute("model.device", "cpu") # ← candidato a GPU span.set_attribute("patient.goal", data["goal"]) span.set_attribute("plan.days", 7) result = nutrigpt_model.generate(data) # P99: 5.8s en CPU span.set_attribute("plan.tokens_generated", result["tokens"]) return result, 200