← SCRAM AI Lab
Aprende a estructurar logs de chatbots en Loki y Grafana sin provocar explosión de cardinalidad: labels correctos, patrones LogQL y control de costos.
May 21, 2026
463 lecturas

Loki no es Elasticsearch. No indexa el contenido de los logs; indexa labels. Si pones user_id o request_id como label, generas un stream por cada valor único y Loki muere — vimos un cluster crecer de 4GB a 380GB de índices en 48h por meter trace_id como label. Para logs de chatbot LLM, el set correcto de labels es chico, fijo y de baja cardinalidad.
Para que Loki no colapse, debes tratarlo como un índice de metadatos estáticos y no como un motor de búsqueda de texto completo. La regla operativa consiste en asignar únicamente etiquetas fijas de baja cardinalidad a nivel de stream y volcar la carga variable —como identificadores de usuario, prompts y consumo de tokens— dentro del payload JSON para evaluarlo en tiempo de consulta.
Todo lo demás — user_id, session_id, request_id, tokens, costos — va en el JSON payload del log, no como label. Loki te deja queriarlos con | json sin generar streams nuevos.
Un registro estándar emitido en formato JSON por tu servicio backend (FastAPI, NestJS o Go) debe lucir de la siguiente forma antes de pasar por Promtail o Vector:
{
"timestamp": "2025-03-28T14:30:00Z",
"service": "chat-api",
"org_id": "tenant_982",
"tier": "tier_1",
"provider": "anthropic",
"latency_bucket": "fast",
"user_id": "usr_bc41",
"session_id": "sess_81093",
"request_id": "req_fa49e2",
"model": "claude-3-5-sonnet-20241022",
"tokens_in": 840,
"tokens_out": 195,
"latency_ms": 620,
"cost_usd": 0.005445,
"status_code": 200
}
En el pipeline de ingesta, configuras al agente para promover únicamente los primeros cinco campos como etiquetas de stream de Loki, dejando intacto el resto en el cuerpo del mensaje.
Tasa de errores por tier en la última hora:
sum by (tier) (
rate({service="chat-api", level="error"}[1h])
)
p99 de latencia por provider:
quantile_over_time(0.99,
{service="chat-api"} | json | unwrap latency_ms [5m]
) by (provider)
Costo acumulado por hora por org:
sum by (org_id) (
sum_over_time(
{service="chat-api"} | json | unwrap cost_usd [1h]
)
)
Top 10 sessions con más tokens de output:
topk(10,
sum by (session_id) (
sum_over_time(
{service="chat-api"} | json | unwrap tokens_out [1h]
)
)
)
Elegir la plataforma correcta impacta directamente el consumo de memoria en tus nodos y la factura mensual de infraestructura. Esta tabla sintetiza las alternativas comunes frente a cargas de trabajo generativas:
| Criterio | Grafana Loki | Elasticsearch / OpenSearch | SaaS (Datadog / New Relic) |
|---|---|---|---|
| Modelo de indexación | Solo metadatos (streams con labels fijos) | Índice invertido en todos los campos de texto | Propietario, indexación selectiva configurable |
| Consumo de memoria / disco | Muy bajo (índices diminutos en chunks comprimidos) | Alto (el índice suele superar el tamaño de los datos crudos) | Nulo en infraestructura propia (delegado al vendor) |
| Costo de almacenamiento a largo plazo | Bajo (almacena chunks directamente en S3 o GCS) | Medio-alto (requiere almacenamiento en bloque tipo EBS o SSD) | Alto (escala con retención y volumen de eventos por mes) |
| Búsqueda en texto libre de prompts | Fuerza bruta mediante escaneo secuencial (|=) |
Instantánea gracias a tokenización de texto | Rápida según facetas indexadas |
| Riesgo operativo principal | Explosión de cardinalidad por mal uso de labels | OOM por heap saturado y fragmentación de shards | Facturas imprevistas por picos de tráfico |
Para equipos de ingeniería en México, Colombia, Argentina o Brasil, los costos de observabilidad cotizados en dólares estadounidenses representan una carga presupuestaria desproporcionada. Los modelos tradicionales de cobro por giga ingerido de proveedores SaaS comerciales generan fricción constante conforme escala la adopción de un chatbot conversacional.
De acuerdo con la documentación de precios públicos de Amazon Web Services para la región US-East (N. Virginia), el almacenamiento en Amazon S3 Standard tiene un costo base de 0.023 USD por gigabyte al mes, mientras que las soluciones administradas de monitoreo de logs suelen iniciar arriba de 1.50 a 2.50 USD por millón de eventos. Montar Loki sobre almacenamiento de objetos propio permite a los equipos regionales desacoplar el volumen de interacción del chatbot respecto a la factura de monitoreo, manteniendo los costos controlados aun frente a crecimientos abruptos de tráfico.
Lista de cosas que nunca deben ser labels:
session_id (miles de valores por día)user_id (escala con usuarios)request_id / trace_id (uno por request)endpoint con path variables (/users/123/profile != /users/456/profile)error_message dinámicoTodos esos van en el payload JSON. Si necesitas filtrar por ellos, usas | json | session_id="..." en LogQL — más lento que un label pero no destruye el índice.
Aunque delegar atributos al payload JSON salva el índice, introduce un riesgo real: las consultas masivas no optimizadas. Si un ingeniero lanza una query sobre un rango de 30 días usando filtros de texto no indexados, los queriers de Loki intentarán descargar y descomprimir cientos de gigabytes de chunks de S3 de golpe, provocando bloqueos o caídas por Out Of Memory (OOM).
Para mitigar este problema en entornos de producción:
{} | json. Acota primero con {service="chat-api", provider="openai"} para que el querier solo descargue los streams estrictamente necesarios.|= "error_code_500") se ejecutan en texto plano mediante Rust/C-bindings a alta velocidad. Filtrar primero el texto plano y ejecutar después el | json reduce el costo computacional hasta en un 80%.loki.yaml, ajusta max_query_length y limita max_query_parallelism para impedir que una búsqueda desmedida monopolice los recursos del cluster.La pregunta abierta: ¿deberían los logs estructurados de chatbot tener también un span_id de OpenTelemetry para joinear con traces? Sí, y es lo que cuenta el siguiente artículo.
Artículos relacionados
¿Quién autoriza a un agente? Permisos automáticos, aprobación humana y dos turnos
Claude, OpenAI y Microsoft ya traen controles para decidir qué ejecuta un agente. Los comparamos con el nuestro: la llave identifica y el código autoriza.
Traefik v2.10 con auto-renewal certs para 94 containers
Wildcard *.scram2k.com cubre la mayoría, certs individuales para el resto. acme.json shared, DNS-01 para wildcards, HTTP-01 para subdomains. Anti-patrón: cert por container.
OpenTelemetry tracing para pipelines LLM
Domina el tracing de pipelines LLM con OpenTelemetry y GenAI semantic conventions. Optimiza latencias en producción y evita sobrecostos con tail sampling.