Observabilidad activa

NutriFlow — Better Stack Config Completa

Uptime · Logs · On-call · Heartbeats · Status Page  |  Generado por CULTIVA IA · 16 Jun 2026

Uptime global 99.98% últimos 30 días
Monitores activos 6 HTTP · TCP · SSL · Heartbeat
Incidentes este mes 1 resuelto en 4 min
Ingenieros on-call 3 rotación semanal
Logs / 24h 148K 0 errores críticos
📡

Monitores de Uptime

6 operativos
API REST
api.nutriflow.app/health
OK
99.97%Uptime 30d
142msLatencia avg
30sFrecuencia
Dashboard React
app.nutriflow.app
OK
100%Uptime 30d
89msLatencia avg
60sFrecuencia
PostgreSQL TCP
db.nutriflow.internal:5432
TCP OK
100%Uptime 30d
8msLatencia avg
60sFrecuencia
SSL Certificate
nutriflow.app
73d
OKEstado cert
73dExpira en
30dAlerta antes
Redis Cache
cache.nutriflow.internal:6379
TCP OK
99.99%Uptime 30d
2msLatencia avg
30sFrecuencia
Webhook Stripe
api.nutriflow.app/webhooks/stripe
POST
99.95%Uptime 30d
67msLatencia avg
60sFrecuencia
🏗️

Configuración Terraform

Terraform
nutriflow-monitoring/main.tf HCL / Terraform
# NutriFlow — Configuración Better Stack via Terraform
# Proveedor: BetterStackHQ/better-uptime

terraform {
  required_providers {
    betteruptime = {
      source  = "BetterStackHQ/better-uptime"
      version = "~> 0.5"
    }
  }
}

# ── Política de escalado ────────────────────────────────────
resource "betteruptime_escalation_policy" "default" {
  name     = "NutriFlow Producción"
  repeat   = 3
  steps = [
    { type = "notify_on_call", wait_before = 0  },
    { type = "call_on_call",   wait_before = 5  },
    { type = "notify_team",    wait_before = 10 },
    { type = "notify_all",     wait_before = 15 }
  ]
}

# ── Monitor API REST ────────────────────────────────────────
resource "betteruptime_monitor" "api" {
  url              = "https://api.nutriflow.app/health"
  monitor_type     = "status"
  check_frequency  = 30
  regions          = ["us", "eu", "asia"]
  confirmation_period = 2  # 2 fallos consecutivos antes de alertar
  request_headers  = [{
    name  = "x-health-token"
    value = "${var.health_check_token}"
  }]
  expected_status_codes = [200]
  policy_id        = betteruptime_escalation_policy.default.id
}

# ── Monitor DB TCP ──────────────────────────────────────────
resource "betteruptime_monitor" "database" {
  url             = "tcp://db.nutriflow.internal:5432"
  monitor_type    = "tcp"
  check_frequency = 60
  policy_id       = betteruptime_escalation_policy.default.id
}

# ── Monitor SSL ─────────────────────────────────────────────
resource "betteruptime_monitor" "ssl" {
  url              = "https://nutriflow.app"
  monitor_type     = "ssl"
  ssl_expiration   = 30  # alerta 30 días antes de expirar
  policy_id        = betteruptime_escalation_policy.default.id
}

# ── Heartbeat cron semanal ──────────────────────────────────
resource "betteruptime_heartbeat" "weekly_report" {
  name              = "Reporte Semanal NutriFlow"
  period            = 10080  # 7 días en minutos
  grace             = 30     # 30 min de gracia
  policy_id         = betteruptime_escalation_policy.default.id
}
📊

Logging Estructurado — Pino + Logtail

Node.js
src/lib/logger.ts TypeScript
import pino from "pino";

// Logger global NutriFlow con transporte Logtail
export const logger = pino(
  pino.transport({
    target: "@logtail/pino",
    options: {
      sourceToken: process.env.LOGTAIL_SOURCE_TOKEN,
    },
  })
);

// Ejemplo: expediente médico sincronizado
logger.info({
  event: "expediente.sync",
  pacienteId: "pac_es_00421",
  clinicaId: "cli_madrid_07",
  campos_actualizados: ["peso", "imc", "plan_semana"],
  duration_ms: 84,
}, "Expediente sincronizado OK");

// Error de facturación con contexto completo
logger.error({
  event: "facturacion.error",
  clinicaId: "cli_barcelona_03",
  plan: "pro_anual",
  stripe_error: "card_declined",
  retryable: true,
  intento: 2,
}, "Pago rechazado");
Live Log Stream — Logtail api.nutriflow.app
09:41:02 INFO
Expediente sincronizado OK
pacienteId=pac_es_00421 clinicaId=cli_madrid_07 duration_ms=84
09:41:08 INFO
Plan nutricional generado
pacienteId=pac_es_00389 semanas=4 calorias_obj=1850
09:41:15 WARN
Latencia elevada en endpoint /analisis
duration_ms=1240 umbral=1000 region=eu-west-1
09:41:23 ERROR
Pago rechazado
clinicaId=cli_barcelona_03 stripe_error=card_declined intento=2
09:41:31 INFO
Usuario autenticado
userId=usr_dietista_089 clinicaId=cli_sevilla_11 method=email
09:41:39 INFO
Reporte PDF exportado
pacienteId=pac_es_00302 paginas=8 duration_ms=432
09:41:47 INFO
Cita agendada
clinicaId=cli_valencia_05 fecha=2026-06-23 pacienteId=pac_es_00521
❤️

Heartbeat — Cron Semanal

Node.js
Reporte Semanal de Clínicas
Cada lunes 06:00 UTC · Grace 30min
Vivo
7 semanas · 1 fallo (2 Jun) Próximo ping: 21 Jun 06:00 UTC
jobs/weekly-report.ts TypeScript
// Cron semanal con heartbeat — NutriFlow
async function weeklyClinicReport() {
  try {
    const clinicas = await db.getActiveClinics();

    for (const clinica of clinicas) {
      const report = await generateWeeklyReport(clinica);
      await emailService.send({
        to: clinica.adminEmail,
        subject: `Resumen semanal — ${clinica.nombre}`,
        pdf: report.pdfBuffer,
      });
    }

    // ✓ Éxito — notificar a Better Stack
    await fetch(
      "https://uptime.betterstack.com/api/v1/heartbeat/" +
      process.env.HEARTBEAT_WEEKLY_TOKEN
    );

    logger.info({
      clinicas_procesadas: clinicas.length,
    }, "Reporte semanal enviado OK");

  } catch (err) {
    // ✗ Sin ping → Better Stack alerta al on-call
    logger.error({ err }, "Fallo en reporte semanal");
  }
}
🔔

On-Call & Escalado

YAML
Rotación semanal
Ingeniero Semana Canal Estado
MAMaría Antúnez 16–22 Jun Slack · Push · Llamada ACTIVO
JPJavier Peris 23–29 Jun Slack · Push · Llamada PRÓXIMO
LGLaura Gómez 30 Jun – 6 Jul Slack · Push · Llamada PENDIENTE
ATAlberto Torres (CTO) Escalado final Llamada · SMS · Email LIDER
Cadena de escalado (incidente producción)
1
Notificación Slack + Push
Mensaje directo a ingeniero on-call + app móvil
T+0 min
2
Llamada telefónica on-call
Llamada automática si no hay acknowledgment
T+5 min
3
Escalado a CTO (Alberto Torres)
Notificación + llamada al líder técnico
T+10 min
4
Alerta a todo el equipo de ingeniería
Canal #incidentes-criticos + todos los móviles
T+15 min
🌐

Página de Estado Pública

status.nutriflow.app
NutriFlow Status
status.nutriflow.app
Todos los sistemas operativos
API REST Operativo
Dashboard de clínicas Operativo
Sincronización de expedientes Operativo
Facturación & Pagos Operativo
Uptime últimos 90 días
src/lib/incidents.ts TypeScript
// Crear incidente programático en Better Stack
// (e.g. detectado por job de monitoreo interno)

export async function createIncident(
  name: string,
  summary: string
) {
  const res = await fetch(
    "https://uptime.betterstack.com/api/v2/incidents",
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.BETTERSTACK_API_TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        requester_email: "ops@nutriflow.app",
        name,
        summary,
        status_page_ids: [
          process.env.BETTERSTACK_STATUS_PAGE_ID
        ],
      }),
    }
  );
  return res.json();
}

// Uso: latencia elevada detectada internamente
await createIncident(
  "Latencia elevada en /analisis",
  "Tiempos de respuesta > 1s en endpoint de análisis nutricional. " +
  "Investigando causa. Sin pérdida de datos."
);