Error Rate (24h)
0.34%
Target: < 1.0%
▼ -0.41pp vs ayer
P95 Latencia
723ms
Target: < 800ms
▼ -1.2s vs semana pasada
Planes Nutricionales
162
Target: ≥ 150/día
▲ +12 vs ayer
Usuarios Activos
347
de 400 registrados
▲ +23 semana
Eventos Ingestados
48.7K
3 datasets · fijo $/mes
∞ sin límite GB
📊 Request Volume & Errores
nutritrack-api
buckets 1h
● Requests
● Errores
● Warnings
00:0003:0006:0009:00
12:0015:0018:0021:00ahora
⚡ P95 Latencia por Endpoint
APL: percentile()
últimas 24h
EndpointP50 / P95 / P99
POST /api/recommendations
312ms / 723ms / 2.1s
GET /api/meal-plan
89ms / 234ms / 445ms
POST /api/auth/login
145ms / 298ms / 612ms
GET /api/users/profile
54ms / 122ms / 203ms
POST /api/webhooks/stripe
201ms / 489ms / 876ms
⚠️ /api/recommendations tiene P99 > 2s los lunes 09:00–11:00. Causa probable: cold start + modelo ML sin caché.
APL Query — Investigación Problema Lunes
▶ ejecutada · 847ms
// P95 por endpoint y hora del día (detectar picos lunes mañana)
['nutritrack-api']
| where _time > ago(7d)
| where endpoint == "/api/recommendations"
| extend hour = hourofday(_time), dow = dayofweek(_time)
| summarize p95 = percentile(duration_ms, 95), cnt = count()
by dow, hour
| where dow == 1 // lunes
| order by p95 desc
['nutritrack-api']
| where _time > ago(7d)
| where endpoint == "/api/recommendations"
| extend hour = hourofday(_time), dow = dayofweek(_time)
| summarize p95 = percentile(duration_ms, 95), cnt = count()
by dow, hour
| where dow == 1 // lunes
| order by p95 desc
RESULTADO · 24 filas
| dow | hour | p95_ms | count | diagnóstico |
|---|---|---|---|---|
| Lunes | 09:00 | 4.230 | 312 | ⚠ Cold start + sin caché |
| Lunes | 10:00 | 3.180 | 487 | ⚠ Modelo ML fría |
| Lunes | 11:00 | 1.240 | 521 | ≈ Cache hit rate 40% |
| Lunes | 12:00 | 712 | 498 | ✓ Estabilizado |
| Lunes | 14:00 | 598 | 445 | ✓ Normal |
APL Query — Revenue por Plan (24h)
▶ ejecutada · 234ms
['nutritrack-business']
| where _time > ago(24h) and event == "subscription_activated"
| summarize mrr = sum(amount_eur), subs = count() by plan
| order by mrr desc
| where _time > ago(24h) and event == "subscription_activated"
| summarize mrr = sum(amount_eur), subs = count() by plan
| order by mrr desc
RESULTADO · 3 filas
| plan | mrr_eur | subs |
|---|---|---|
| enterprise | 2.890 € | 4 |
| pro | 1.470 € | 21 |
| starter | 390 € | 26 |
🔔 Monitors & Alertas
4 activos
Error Rate Spike
errores > 50 / 5min → Slack #alerts
Zero Planes Nutricionales
0 planes generados en 15min → PagerDuty
P99 Latencia Alta
/api/recommendations P99 > 3s → email dev
Deploy Annotation
Correlación deploy ↔ métricas automática
🗄️ Datasets
retención: 30d
nutritrack-api
32.4K
nutritrack-business
11.8K
nutritrack-infra
4.5K
📡 Live Log Stream
axiom stream
streaming
| time | level | endpoint | userId | ms |
|---|---|---|---|---|
| 14:32:18 | INFO | POST /api/recommendations | usr_8821 | 487ms |
| 14:32:15 | INFO | GET /api/meal-plan | usr_3340 | 112ms |
| 14:32:12 | ERROR | POST /api/webhooks/stripe | sys | 2.340ms |
| 14:32:09 | INFO | POST /api/auth/login | usr_1192 | 234ms |
| 14:32:07 | WARN | GET /api/recommendations | usr_5571 | 980ms |
| 14:32:04 | INFO | GET /api/users/profile | usr_7743 | 67ms |
| 14:32:01 | INFO | POST /api/recommendations | usr_2209 | 521ms |
| 14:31:58 | ERROR | POST /api/meal-plan | usr_9982 | timeout |
💻 Integración Next.js — NutriTrack
next-axiom
app/api/recommendations/route.ts
TypeScript
import { withAxiom, AxiomRequest } from 'next-axiom';
import { generateRecommendations } from '@/lib/ml';
export const POST = withAxiom(async (req: AxiomRequest) => {
const { userId, dietaryProfile } = await req.json();
const t0 = Date.now();
req.log.info('Recommendations requested', {
userId, endpoint: '/api/recommendations'
});
try {
const recs = await generateRecommendations(dietaryProfile);
req.log.info('Recommendations generated', {
userId, duration_ms: Date.now() - t0,
count: recs.length, endpoint: '/api/recommendations'
});
return Response.json({ recommendations: recs });
} catch (err) {
req.log.error('Recommendations failed', {
userId, error: err.message,
duration_ms: Date.now() - t0
});
return Response.json({ error: 'Failed' }, { status: 500 });
}
});
import { generateRecommendations } from '@/lib/ml';
export const POST = withAxiom(async (req: AxiomRequest) => {
const { userId, dietaryProfile } = await req.json();
const t0 = Date.now();
req.log.info('Recommendations requested', {
userId, endpoint: '/api/recommendations'
});
try {
const recs = await generateRecommendations(dietaryProfile);
req.log.info('Recommendations generated', {
userId, duration_ms: Date.now() - t0,
count: recs.length, endpoint: '/api/recommendations'
});
return Response.json({ recommendations: recs });
} catch (err) {
req.log.error('Recommendations failed', {
userId, error: err.message,
duration_ms: Date.now() - t0
});
return Response.json({ error: 'Failed' }, { status: 500 });
}
});
CLI — debug lunes en vivo
bash
# Stream real-time (como tail -f)
axiom stream nutritrack-api
# Query errores última hora
axiom query "['nutritrack-api'] | where level == 'error' | take 20" -f json
# Ingest logs de Docker
docker logs nutritrack-ml 2&1 | axiom ingest nutritrack-infra
axiom stream nutritrack-api
# Query errores última hora
axiom query "['nutritrack-api'] | where level == 'error' | take 20" -f json
# Ingest logs de Docker
docker logs nutritrack-ml 2&1 | axiom ingest nutritrack-infra