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.
| Componente | Rol | Dónde |
|---|---|---|
| OpenTelemetry Collector | Recibe OTLP gRPC | 192.168.100.205:4317 |
| SigNoz | UI de trazas + métricas + logs | repo smile-infra-monitor |
| Grafana | Dashboards embebidos en Kuatia vía auth-proxy | repo smile-infra-monitor |
| OpenReplay (cloud) | Session replay del frontend | openreplay.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,ioredisse instrumentan automáticamente.- Resource attributes:
service.name=kuatia-api(override conOTEL_SERVICE_NAME)service.namespace=kuatia(constante)deployment.environment=$KUATIA_ENVIRONMENTservice.version=$npm_package_version
Métricas manuales
Counters, histograms y gauges instrumentados en código (api/src/telemetry/telemetry.service.ts):
| Métrica | Tipo | Atributos |
|---|---|---|
kuatia_dossiers_created_total | counter | tenantId |
kuatia_tasks_completed_total | counter | tenantId, outcome |
kuatia_documents_uploaded_total | counter | tenantId, mimeType, dedup |
kuatia_document_dedup_hits_total | counter | tenantId |
kuatia_workflow_duration_seconds | histogram | tenantId, dossierTypeId |
kuatia_queue_job_duration_seconds | histogram | queueName, jobName, status |
kuatia_integration_call_duration_seconds | histogram | tenantId, externalSystemId |
kuatia_storage_bytes_total | gauge | tenantId |
kuatia_documents_count | gauge | tenantId |
kuatia_documents_size_bytes | gauge | tenantId |
kuatia_dossiers_count | gauge | tenantId, state |
kuatia_accounts_count | gauge | tenantId, accountTypeId |
kuatia_tasks_count | gauge | tenantId, 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_idautomáticos en cada log cuando hay span activo (mixin que consultatrace.getActiveSpan()de OTel API). En SigNoz/Grafana, click en un log → ir al trace correspondiente sin copiar IDs.- Nivel controlado por
LOG_LEVEL(defaultinfo). KUATIA_ENVIRONMENT=developmentusapino-pretty.qa|staging|productionqueda JSON compacto.redact.pathscensurapassword,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:
| Alerta | Trigger |
|---|---|
HighErrorRate | Ratio de integration calls 4xx/5xx > 5% durante 5m |
HighWorkflowLatencyP95 | p95 de kuatia_workflow_duration_seconds > 30s durante 10m |
QueueJobFailures | Jobs BullMQ failed > 6/min durante 5m (break-down por queueName) |
StorageGrowthSpike | deriv(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:
keycloak—GET ${KEYCLOAK_AUTH_SERVER_URL}/realms/${KEYCLOAK_REALM}(timeout 3s)minio—client.listBuckets()typesense—GET ${TYPESENSE_HOST}:${TYPESENSE_PORT}/healthsmtp—nodemailer.verify()siSMTP_HOSTestá seteado, sinoskipped
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')inyectaX-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) ykuatia-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_KEYestá configurado. - Dashboard en openreplay.com.
Variables de ambiente relevantes
| Variable | Default | Notas |
|---|---|---|
OTEL_ENDPOINT | (vacío → SDK no arranca) | OTLP gRPC del collector central |
OTEL_SERVICE_NAME | kuatia-api | Service name del exporter |
LOG_LEVEL | info | trace/debug/info/warn/error/fatal |
KUATIA_ENVIRONMENT | development | Habilita 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 |