Saltar al contenido principal

Observabilidad

Stack centralizado

Kuatia exporta toda su telemetría (trazas + métricas + logs) a un stack centralizado que vive en un repo separado Texware-SRL/smile-infra-monitor, corriendo en el host monitor (192.168.100.205). El stack sirve a múltiples proyectos del ecosistema Smile — no es exclusivo de Kuatia. El collector multiplexa por service.namespace=kuatia (constante de código, no env) para filtrar la telemetría de este proyecto del resto del ecosistema.

ComponenteRolDónde
OpenTelemetry CollectorRecibe OTLP gRPC192.168.100.205:4317
SigNozUI de trazas + métricas + logsrepo smile-infra-monitor
GrafanaDashboards embebidos en Kuatia vía auth-proxyrepo smile-infra-monitor
OpenReplay (cloud)Session replay del frontendopenreplay.com

La API no depende de que el stack esté arriba: si falta OTEL_ENDPOINT, el SDK no arranca y el sistema sigue operando normal.

Activación

En cada VM, en su .env:

OTEL_ENDPOINT=http://192.168.100.205:4317
  • Staging (deploy@192.168.100.210:/home/deploy/kuatia/.env)
  • Prod (deploy@192.168.100.211:/home/deploy/kuatia/.env)

Restart del container kuatia-api-1 para que tome el cambio.

Auto-instrumentación

Inicializada como segundo import de api/src/main.ts, antes de AppModule:

  • http, express, pg, mongoose, ioredis se instrumentan automáticamente.
  • Resource attributes:
    • service.name=kuatia-api (override con OTEL_SERVICE_NAME)
    • service.namespace=kuatia (constante)
    • deployment.environment=$KUATIA_ENVIRONMENT
    • service.version=$npm_package_version

Métricas manuales

Counters, histograms y gauges instrumentados en código (api/src/telemetry/telemetry.service.ts):

MétricaTipoAtributos
kuatia_dossiers_created_totalcountertenantId
kuatia_tasks_completed_totalcountertenantId, outcome
kuatia_documents_uploaded_totalcountertenantId, mimeType, dedup
kuatia_document_dedup_hits_totalcountertenantId
kuatia_workflow_duration_secondshistogramtenantId, dossierTypeId
kuatia_queue_job_duration_secondshistogramqueueName, jobName, status
kuatia_integration_call_duration_secondshistogramtenantId, externalSystemId
kuatia_storage_bytes_totalgaugetenantId
kuatia_documents_countgaugetenantId
kuatia_documents_size_bytesgaugetenantId
kuatia_dossiers_countgaugetenantId, state
kuatia_accounts_countgaugetenantId, accountTypeId
kuatia_tasks_countgaugetenantId, status

Los gauges los emite el StockMetricsService con @Cron(EVERY_MINUTE).

Workers BullMQ

Las 7 colas (document-pipeline, webhooks, notifications, task-lifecycle, bulk-operations, search-reindex, flow-timeout) usan el helper único instrumentQueueJob(telemetry, queueName, job, handler), que abre un span queue.<queueName>.<jobName> con tenantId, jobId, attempt y emite el histograma de duración con status=completed|failed.

Logger estructurado

KuatiaLoggerModule (global) basado en nestjs-pino + pino:

  • trace_id + span_id automáticos en cada log cuando hay span activo (mixin que consulta trace.getActiveSpan() de OTel API). En SigNoz/Grafana, click en un log → ir al trace correspondiente sin copiar IDs.
  • Nivel controlado por LOG_LEVEL (default info).
  • KUATIA_ENVIRONMENT=development usa pino-pretty. qa|staging|production queda JSON compacto.
  • redact.paths censura password, secret, apiKey, authorization, token + req.headers.authorization/cookie + res.headers['set-cookie'].
  • autoLogging: false — no se loguea una línea por cada HTTP request.

Alertas (Grafana provisioning)

Cuatro reglas provisionadas (observability/grafana/provisioning/alerting/kuatia-alerts.yml) que se cargan en la Grafana central:

AlertaTrigger
HighErrorRateRatio de integration calls 4xx/5xx > 5% durante 5m
HighWorkflowLatencyP95p95 de kuatia_workflow_duration_seconds > 30s durante 10m
QueueJobFailuresJobs BullMQ failed > 6/min durante 5m (break-down por queueName)
StorageGrowthSpikederiv(kuatia_storage_bytes_total[1h]) > 5 MB/s durante 30m

Contact point default log-only. Para email/Slack real, configurar en smile-infra-monitor.

Health checks extendidos

GET /health devuelve un payload con 3 checks requeridos (postgres, mongo, redis) que definen el status top-level (ok/degraded) — consumido por wait-for-health.sh, el smoke job de CI y healthchecks de compose/k8s.

4 checks opcionales que NO afectan el status top-level:

  • keycloakGET ${KEYCLOAK_AUTH_SERVER_URL}/realms/${KEYCLOAK_REALM} (timeout 3s)
  • minioclient.listBuckets()
  • typesenseGET ${TYPESENSE_HOST}:${TYPESENSE_PORT}/health
  • smtpnodemailer.verify() si SMTP_HOST está seteado, sino skipped

Dashboards embebidos

Kuatia embebe dashboards de Grafana via reverse-proxy con auth-proxy (Grafana queda apagado para tráfico anónimo — diseñado para pasar compliance de gobierno/banca).

  • Entidad Dashboard (PostgreSQL): name único por tenant, url (template con placeholders o path de Grafana), requiredPermission, embedMode (proxy | direct).
  • <DashboardLoader name="..." /> en el frontend → GET /dashboards/:name/iframe → iframe sandbox.
  • Reverse proxy @All('observability/grafana/*path') inyecta X-WEBAUTH-USER: <user.email> para SSO via Grafana auth-proxy.
  • Seeds del bootstrap: kuatia-activity (counters, rates, p50/p95/p99 de workflow/queue/integración) y kuatia-inventory (disco, docs, dossiers, accounts, mime breakdown).

OpenReplay (Cloud)

Grabación de sesiones del usuario en el frontend:

  • Tracker inicializado post-login con userId, email, tenant.
  • Solo se activa si VITE_OPENREPLAY_PROJECT_KEY está configurado.
  • Dashboard en openreplay.com.

Variables de ambiente relevantes

VariableDefaultNotas
OTEL_ENDPOINT(vacío → SDK no arranca)OTLP gRPC del collector central
OTEL_SERVICE_NAMEkuatia-apiService name del exporter
LOG_LEVELinfotrace/debug/info/warn/error/fatal
KUATIA_ENVIRONMENTdevelopmentHabilita pino-pretty si development
GRAFANA_URL(env)Backend de Grafana centralizado
KUATIA_API_BASE_URL(env)Para componer el iframe.src en mode proxy
VITE_OPENREPLAY_PROJECT_KEY(vacío → tracker no carga)Project key de OpenReplay cloud