Datos y bases de datos

Gestión centralizada de logs con Elasticsearch, Logstash y Kibana

Intermedio9 min de lecturaActualizado

Esta página se tradujo del inglés con ayuda de IA y se cotejó con el original. Leer la versión en inglés

Los logs solo resultan útiles cuando están estructurados, centralizados y se pueden consultar pocos segundos después de escribirse. Esta guía recorre la stack de Elastic de principio a fin: emitir logs en JSON, enviarlos con Beats o Elastic Agent, parsearlos con Logstash o ingest pipelines, controlar los mappings con index templates, gestionar la retención con ILM y dimensionar los shards para que el clúster se mantenga sano.

Lo que aprenderás

  • Describir el papel de cada componente de la stack de Elastic y elegir entre Logstash e ingest pipelines
  • Emitir logs JSON estructurados y alinearlos con un esquema de campos coherente
  • Escribir reglas de parsing con grok y dissect y enriquecer los eventos durante la ingesta
  • Controlar los mappings con index templates y component templates, y la retención con index lifecycle management
  • Dimensionar shards e índices para que el clúster siga estable a medida que crece el volumen de logs

Un único servidor de aplicaciones que escribe en un fichero se depura sin problemas: abres el fichero, buscas y listo. Veinte contenedores detrás de un balanceador de carga, que se reinician en cada despliegue y desaparecen al reducir instancias, son un problema completamente distinto. Para cuando te enteras de que algo ha fallado, puede que el contenedor que registró la causa ya no exista. La centralización de logs existe para que ese escenario sea imposible: cada línea sale de la máquina en el momento en que se escribe, se descompone en campos y se puede consultar en todos los servicios a la vez.

La stack de Elastic (Elasticsearch para el almacenamiento y la búsqueda, Logstash para el procesamiento, Kibana para las consultas y la visualización, y los agentes de envío Beats o Elastic Agent) sigue siendo la solución más extendida. Esta guía cubre la versión mayor actual, Elastic 9, y se centra en las decisiones que determinan si la plataforma sigue siendo útil y asequible a medida que crece el volumen.

Qué hace cada componente

  • Elasticsearch almacena documentos en índices y responde a consultas. Es un motor de búsqueda distribuido construido sobre Apache Lucene, así que comparte el índice invertido y los mecanismos de análisis descritos en la guía de Apache Solr; aquí no los repetiremos. Lo importante es que se trata de un almacén casi en tiempo real: un documento pasa a ser consultable tras un refresh, por defecto en menos de un segundo.
  • Kibana es la interfaz de consulta y visualización, y también la consola de administración de pipelines, políticas de ciclo de vida, reglas de alerta y dashboards.
  • Beats son agentes de envío de propósito único escritos en Go: Filebeat para ficheros de log y contenedores, Metricbeat para métricas de sistema y de servicios, Heartbeat para comprobaciones de disponibilidad, y Packetbeat y Auditbeat para datos de red y de auditoría.
  • Elastic Agent es el agente unificado, más reciente. Un solo proceso gestiona logs, métricas y datos de seguridad, y descarga integraciones (configuración de entrada, ingest pipelines, index templates y dashboards prediseñados, todo empaquetado) desde un servidor Fleet central. Para un despliegue nuevo es la opción por defecto; los Beats siguen plenamente soportados y son más sencillos cuando solo necesitas una cosa.
  • Logstash es un pipeline de procesamiento independiente con entradas, filtros y salidas. Hace de buffer, transforma y enruta, y puede leer fuentes que ningún otro componente sabe leer.

La stack es solo la mitad del cuadro. Logs, métricas y trazas son tres vistas del mismo sistema, y una aplicación que expone endpoints de salud y de métricas, como se describe en Actuator y observabilidad, te da las otras dos.

Escribe los logs en origen como si fuera a leerlos una máquina

El cambio de mayor impacto en cualquier proyecto de logging ocurre antes de enviar nada: hacer que la aplicación emita eventos estructurados en lugar de prosa.

{
  "@timestamp": "2026-09-07T08:14:22.481Z",
  "log.level": "ERROR",
  "service.name": "payment-api",
  "service.version": "4.2.1",
  "trace.id": "3f1c0a9d5e6b47c8",
  "transaction.id": "9b2e77a1",
  "event.dataset": "payment.settlement",
  "http.request.method": "POST",
  "url.path": "/v1/settlements",
  "http.response.status_code": 502,
  "message": "Settlement rejected by acquirer",
  "error.type": "AcquirerTimeoutException",
  "labels": { "merchant_id": "M-88213" }
}

Cada campo se puede consultar, agregar y representar en un gráfico sin ningún parser de por medio. Las aplicaciones Java lo consiguen con un encoder JSON de Logback o Log4j2; los encoders compatibles con ECS emiten directamente los nombres de campo anteriores. Cuando un documento parece incorrecto, pegarlo en un formateador de JSON deja a la vista al instante un nivel de anidamiento de más o una clave duplicada.

Lo que nos lleva a la segunda decisión: adoptar una convención de nombres de campos y mantenerla. El Elastic Common Schema define nombres para los conceptos que registra cualquier sistema (host.name, service.name, event.duration, error.stack_trace, user.id), y usarlo significa que los dashboards y las reglas de detección prediseñados funcionan con tus datos, y que los logs de dos equipos se pueden correlacionar. Si te inventas tus propios nombres, cada dashboard se convierte en un desarrollo a medida.

Hay dos reglas que merece la pena incluir en el estándar de código. Nunca registres secretos, números de tarjeta ni datos personales que no guardarías en una base de datos, porque una plataforma de logs suele estar menos protegida que la base de datos. Y coloca los identificadores que permiten correlacionar (trace id, request id, tenant id) en un mapped diagnostic context para que todas las líneas los lleven automáticamente.

Envío y parsing

Filebeat sigue los ficheros a medida que crecen, guarda su posición de lectura en un registro para que un reinicio no duplique ni pierda datos, gestiona las stack traces multilínea y aplica backpressure cuando el destino se ralentiza.

filebeat.inputs:
  - type: filestream
    id: payment-api
    paths: ["/var/log/payment-api/*.json"]
    parsers:
      - ndjson:
          target: ""
          overwrite_keys: true
          add_error_key: true

processors:
  - add_host_metadata: ~
  - add_kubernetes_metadata: ~
  - drop_event:
      when:
        equals:
          log.level: "TRACE"

output.elasticsearch:
  hosts: ["https://es01:9200"]
  api_key: "${ES_API_KEY}"

Los contenedores cambian el modelo de envío, no el principio: escribe en stdout, deja que el runtime lo recoja y ejecuta un agente por nodo con autodiscovery que añada los labels del pod, el namespace y el contenedor. Consulta Fundamentos de Docker para entender por qué escribir los logs en stdout es lo nativo en contenedores.

Para las fuentes que no puedes modificar (el feed syslog de un firewall, un appliance que emite líneas de ancho fijo), el parsing es inevitable. Hay dos herramientas para ello.

Dissect divide por delimitadores fijos. Es muchísimo más rápido que una expresión regular y debería ser tu primera opción cuando el formato es estable:

%{clientip} - %{user} [%{timestamp}] "%{method} %{path} %{httpversion}" %{status} %{bytes}

Grok es una biblioteca de expresiones regulares con nombre y se ocupa de las entradas realmente irregulares:

%{IPORHOST:source.ip} %{USER:user.name} \[%{HTTPDATE:event.created}\] "%{WORD:http.request.method} %{URIPATHPARAM:url.original}" %{NUMBER:http.response.status_code:int}

Los patrones grok son también la causa clásica de que la ingesta se venga abajo: un patrón sin anclar que sufre un backtracking catastrófico satura un núcleo de CPU. Ancla los patrones, prefiere dissect siempre que puedas y prueba en el Grok Debugger de Kibana antes de desplegar.

La siguiente decisión es dónde se ejecuta el parsing. Un ingest pipeline de Elasticsearch se ejecuta dentro del clúster, en los nodos con rol ingest, y no requiere ningún servicio adicional:

PUT _ingest/pipeline/payment-logs
{
  "processors": [
    { "dissect": { "field": "message", "pattern": "%{ts} %{level} [%{thread}] %{logger} - %{msg}" } },
    { "date":    { "field": "ts", "formats": ["ISO8601"], "target_field": "@timestamp" } },
    { "geoip":   { "field": "source.ip", "target_field": "source.geo", "ignore_missing": true } },
    { "remove":  { "field": ["ts", "message"], "ignore_missing": true } }
  ],
  "on_failure": [
    { "set": { "field": "event.kind", "value": "pipeline_error" } },
    { "set": { "field": "error.message", "value": "{{ _ingest.on_failure_message }}" } }
  ]
}

Fíjate en el bloque on_failure. Sin él, una línea mal formada hace que se rechace el documento y pierdes justo el evento que más querías ver. Etiqueta los fallos e indéxalos igualmente.

Logstash compensa el servicio adicional cuando necesitas un buffer duradero entre productores ruidosos y el clúster (su persistent queue sobrevive a los reinicios), cuando los eventos deben repartirse a más de un destino o cuando la transformación es lo bastante pesada como para no querer gastar en ella la CPU de los nodos de datos. Una arquitectura de producción habitual es Beats hacia Kafka como buffer, Logstash consumiendo de Kafka y Elasticsearch como destino final.

Mappings, data streams y retención

Elasticsearch deduce un mapping para cualquier campo que no haya visto antes. Esa comodidad provoca dos problemas crónicos: un campo que llega primero como número y después como cadena se rechaza, y una explosión de mappings (miles de campos creados dinámicamente a partir de claves sin límite como labels.<request-id>) infla el cluster state hasta volver inestable el clúster.

Esto se controla con component templates combinados en un index template:

PUT _index_template/logs-payment
{
  "index_patterns": ["logs-payment-*"],
  "data_stream": {},
  "priority": 200,
  "composed_of": ["logs-mappings", "logs-settings"],
  "template": {
    "settings": { "index.number_of_shards": 2, "index.number_of_replicas": 1,
                  "index.lifecycle.name": "logs-90d" },
    "mappings": {
      "dynamic": "strict",
      "properties": {
        "@timestamp":  { "type": "date" },
        "service.name":{ "type": "keyword" },
        "log.level":   { "type": "keyword" },
        "message":     { "type": "text" },
        "http.response.status_code": { "type": "integer" }
      }
    }
  }
}

La distinción entre keyword y text es la que hay que interiorizar: keyword guarda el valor completo para filtrado exacto, agregación y ordenación; text se analiza en tokens para la búsqueda de texto completo y no se puede agregar de forma eficiente. Los nombres de servicio, los niveles de log y los nombres de host son keywords. Los mensajes de texto libre son text.

Un data stream es la forma moderna de manejar series temporales de solo inserción. Escribes siempre en un mismo nombre, logs-payment-prod, y Elasticsearch gestiona detrás una serie de índices subyacentes ocultos, pasando a uno nuevo (rollover) al alcanzar un umbral de tamaño o antigüedad. El rollover importa porque borrar datos antiguos equivale entonces a eliminar un índice entero, algo casi gratuito, mientras que borrar documentos mediante una consulta es caro.

El index lifecycle management automatiza todo el ciclo de vida de esos datos:

PUT _ilm/policy/logs-90d
{
  "policy": { "phases": {
    "hot":    { "actions": { "rollover": { "max_primary_shard_size": "40gb", "max_age": "1d" } } },
    "warm":   { "min_age": "3d",  "actions": { "forcemerge": { "max_num_segments": 1 },
                                               "shrink": { "number_of_shards": 1 } } },
    "cold":   { "min_age": "15d", "actions": { "set_priority": { "priority": 0 } } },
    "delete": { "min_age": "90d", "actions": { "delete": {} } }
  }}
}

Los nodos hot reciben las escrituras en discos rápidos; los nodos warm guardan datos recientes que apenas se escriben; los nodos cold conservan lo que se consulta de vez en cuando. Fija la retención por dataset (los eventos de auditoría y los logs de depuración rara vez justifican la misma ventana) y recuerda que el gigabyte más barato es el que nunca llegaste a indexar. Descartar en el agente de envío los eventos TRACE y las peticiones de health check suele reducir el volumen a la mitad sin perder nada útil para investigar.

Consultas y dashboards

En la vista Discover de Kibana, KQL cubre la mayor parte del filtrado del día a día: service.name:"payment-api" and http.response.status_code >= 500 and not url.path:"/health". La sintaxis Lucene clásica sigue disponible para expresiones regulares y búsquedas difusas (fuzzy).

ES|QL, el lenguaje de consulta con pipes de Elastic, es el gran cambio de la generación actual. Expresa filtrado, transformación y agregación en una sola cadena legible:

FROM logs-payment-*
| WHERE http.response.status_code >= 500 AND @timestamp > NOW() - 1 hour
| STATS failures = COUNT(*) BY service.name, error.type
| SORT failures DESC
| LIMIT 20

Diseña los dashboards en torno a preguntas y no en torno a los campos disponibles: tasa de errores por servicio, percentiles de latencia, endpoints que más fallan, marcas de despliegue. Las reglas de alerta de Kibana convierten cualquiera de ellos en una notificación cuando se supera un umbral. Y recurre a los runtime fields para ese atributo ocasional que olvidaste extraer: se calculan en el momento de la consulta, así que no cuestan nada hasta que se usan y te ahorran una reindexación.

Dimensionado y operación

La mayoría de los clústeres de logs con problemas sufren la misma enfermedad: demasiados shards. Cada shard es un índice Lucene con su sobrecarga de memoria y de descriptores de fichero, y el cluster state que los registra debe replicarse en todos los nodos.

  • Apunta a 10–50 GB por shard y, aproximadamente, a 20 shards o menos por gigabyte de heap en cada nodo.
  • Configura el heap de la JVM en torno a la mitad de la RAM de la máquina y nunca por encima de ~31 GB, para que los punteros sigan comprimidos; el resto de la RAM alimenta la page cache de la que depende Lucene.
  • Separa los roles de los nodos cuando el clúster crezca: los nodos master-eligible dedicados (tres, para tener quórum) mantienen estable el cluster state cuando los nodos de datos están ocupados.
  • Vigila los disk watermarks. Al 85 %, Elasticsearch deja de asignar shards nuevos a un nodo; al 95 %, pone los índices en solo lectura. Ambos sorprenden a los equipos a las tres de la madrugada.
  • Haz snapshots a un almacenamiento de objetos. La replicación protege frente a la pérdida de un nodo, no frente a un ingest pipeline defectuoso ni a un borrado por error.

Hay dos alternativas que merecen una mención justa. OpenSearch es un fork con licencia Apache, API compatible y sus propios dashboards, y es la elección natural cuando la licencia de Elastic supone un bloqueo. Grafana Loki hace la apuesta arquitectónica contraria: indexar solo un pequeño conjunto de labels, guardar el contenido en bruto de los logs comprimido en un almacenamiento de objetos y hacer el equivalente a un grep en el momento de la consulta. Es mucho más barato a gran volumen y mucho más débil en búsqueda de texto completo sobre todos los datos. Si tus consultas son siempre del tipo «este servicio, esta ventana de tiempo», Loki encaja; si son del tipo «encuentra este token en cualquier parte», no. Cuando el volumen de logs supera lo que debería albergar un clúster de búsqueda, los patrones de almacenamiento y procesamiento de la guía de arquitectura big data pasan a ser la alternativa.

Puntos clave

  • Los logs JSON estructurados con un esquema coherente como ECS eliminan el parsing del pipeline y hacen que cada campo se pueda consultar y representar en gráficos.
  • Elastic Agent con las integraciones de Fleet es el agente por defecto para despliegues nuevos; los Beats siguen siendo sencillos y Logstash se gana su sitio para el buffering, el reparto a varios destinos y las transformaciones pesadas.
  • Prefiere dissect a grok cuando el formato es fijo, ancla los patrones grok y añade siempre un handler on_failure para que las líneas erróneas se etiqueten en lugar de perderse.
  • Controla los mappings con index templates y component templates, distingue keyword de text y evita las explosiones de mappings por nombres de campo sin límite.
  • Escribe en data streams, haz rollover por tamaño o antigüedad y expresa la retención como una política de ILM con fases hot, warm, cold y delete para cada dataset.
  • Mantén los shards entre 10 y 50 GB y el heap por debajo de unos 31 GB, vigila los disk watermarks y haz snapshots a un almacenamiento de objetos, porque las réplicas no son copias de seguridad.

Fuentes y lecturas recomendadas

Preguntas frecuentes

¿Sigo necesitando Logstash ahora que existen los ingest pipelines?
A menudo no. Los ingest pipelines de Elasticsearch se encargan del parsing, el enriquecimiento y la extracción de fechas dentro del propio clúster, y las integraciones de Elastic Agent ya traen sus pipelines escritos. Logstash se gana su sitio cuando necesitas un buffer delante del clúster, traducir protocolos de fuentes que Beats no sabe leer, repartir los eventos a varios destinos o aplicar transformaciones pesadas que prefieres no ejecutar en los nodos de datos.
¿Cuánto tiempo debo conservar los logs?
El suficiente para investigar los incidentes que de verdad investigas, más lo que exija el cumplimiento normativo: para la mayoría de los equipos, unos días de datos calientes consultables y semanas o meses de almacenamiento más frío. Plasma la respuesta en una política de ILM en lugar de un script de borrado, y fija la retención por tipo de dato: los logs de peticiones, los registros de auditoría y las trazas de depuración rara vez merecen la misma ventana.
¿Por qué mi clúster está en amarillo o en rojo?
Amarillo significa que hay shards réplica sin asignar: los datos están intactos pero sin redundancia, algo habitual en un clúster de desarrollo de un solo nodo, donde las réplicas nunca pueden asignarse. Rojo significa que un shard primario está sin asignar y parte de los datos no está disponible. La API allocation explain indica el motivo exacto, que suele ser los disk watermarks, un nodo que ha abandonado el clúster o muy pocos nodos para el número de réplicas solicitado.
¿Es la stack de Elastic la única opción para los logs?
No. OpenSearch es un fork de Elasticsearch con licencia Apache, API compatible y su propia herramienta de dashboards, y Grafana Loki sigue otro enfoque: solo indexa labels y guarda las líneas de log en bruto en un almacenamiento de objetos, lo que resulta mucho más barato a gran volumen pero flojea en la búsqueda de texto completo. Decide en función de tus patrones de consulta, el coste por gigabyte y las herramientas que tu equipo ya opera.