Lenguaje de expresión común (CEL): cómo la entrada CEL mejora la recopilación de datos en las integraciones de Elastic Agent

Aprende cómo se diferencia el lenguaje de expresión común de otros lenguajes de programación, cómo lo ampliamos para la entrada CEL de Filebeat y la flexibilidad que te ofrece para expresar la lógica de recopilación de datos en integraciones con Elastic Agent.

Agent Builder ya está disponible para el público en general. Inicia una prueba de Elastic Cloud y consulta la documentación de Agent Builder aquí.

Las integraciones de Elastic Agent permiten a los usuarios realizar la ingesta de datos en Elasticsearch desde una amplia variedad de fuentes. Combinan la lógica de recopilación, canalizaciones de ingesta, dashboards y otros artefactos en un paquete que se puede instalar y administrar desde la interfaz web de Kibana.

Las integraciones configuran las entradas de Filebeat para realizar la recopilación de datos. Para recopilar datos de APIs HTTP, a menudo utilizamos la entrada HTTP JSON. Sin embargo, incluso las APIs básicas de listado pueden diferir mucho en los detalles, y el modelo de transformaciones configuradas en YAML de la entrada HTTP JSON puede hacer que sea engorroso y a veces imposible expresar la lógica de recopilación requerida.

Se introdujo la entrada del lenguaje de expresión común (CEL) para permitir una interacción más flexible con las APIs HTTP. CEL es un lenguaje diseñado para integrarse en aplicaciones que requieren una forma rápida, segura y extensible de expresar condiciones y transformaciones de datos. La entrada CEL permite a un creador de integraciones escribir una expresión que puede leer configuraciones, realizar un seguimiento de su propio estado, realizar solicitudes, procesar respuestas y, en última instancia, devolver eventos listos para su ingesta.

En este artículo, veremos cómo se diferencia el lenguaje de expresión común de otros lenguajes de programación, cómo lo ampliamos para la entrada del CEL, y la flexibilidad y el poder que te da para expresar tu lógica de recopilación de datos.

CEL y cómo funciona en la entrada

CEL es un lenguaje de expresión. No tiene declaraciones. Cuando escribes CEL, no le dices qué hacer al escribir declaraciones, sino que le dices qué valor producir al escribir una expresión. Cada expresión CEL produce un valor, y las expresiones más pequeñas pueden combinarse en una expresión mayor para producir un resultado según reglas más complejas. Más adelante veremos cómo usar expresiones que se pueden escribir con declaraciones en otros idiomas.

CEL es intencionadamente un lenguaje completo no Turing. No permite bucles ilimitados. Más adelante, veremos cómo puedes procesar listas y mapas mediante macros, pero al evitar bucles sin límites, el lenguaje garantiza un tiempo de ejecución predecible y limitado para expresiones individuales.

La entrada CEL está configurada con un programa CEL (una expresión) y un estado inicial. El estado se proporcionará como entrada al programa. El programa se evalúa para producir un estado de salida. Si el estado de salida incluye una lista de eventos, estos se eliminarán y se publicarán. El resto del estado de salida se usará como entrada para la próxima evaluación. Si el estado de salida incluye uno o más eventos y la advertencia want_more: true, la siguiente evaluación se realizará inmediatamente. De lo contrario, permanecerá en suspensión durante el resto del intervalo configurado antes de continuar. Aquí tienes un diagrama simplificado del flujo de control de la entrada:

La salida de cada evaluación se pasará como entrada a la siguiente evaluación, mientras se ejecute la entrada. Los datos de salida bajo la clave "cursor" se mantendrán en el disco y se volverán a cargar después de que la entrada se reinicie, pero el resto del estado no se conservará entre los reinicios.

El lenguaje CEL en sí tiene funcionalidades limitadas y evita efectos secundarios, pero es extensible. La implementación cel-go agrega algunas funcionalidades, como sintaxis y tipos opcionales. La biblioteca Mito se basa en cel-go y agrega más funcionalidades, como la capacidad de hacer solicitudes HTTP. La entrada CEL usa la versión de CEL de Mito.

Trabajando con Mito

Para construir o depurar una integración mediante una entrada CEL, lo más importante que debes entender es qué estado de salida producirá tu programa CEL para un estado de entrada dado. Durante el desarrollo, puede ser engorroso que tu programa CEL la ejecute la entrada, rodeada por toda la pila de Elastic Stack. Una forma de lograr un ciclo de retroalimentación más rápido es usar la herramienta de línea de comando de Mito, que te permitirá ejecutar un programa CEL directamente y ver la salida que produce para una entrada dada.

Mito está escrito en Go y se puede instalar de la siguiente manera:

Cuando ejecutas un programa CEL con Mito, normalmente le asignas dos archivos: un archivo JSON con el estado inicial de entrada y otro archivo con el código fuente de tu programa CEL:

Para facilitar la copia y el pegado, los ejemplos en este artículo están escritos como comandos únicos que hacen que el shell cree archivos temporales al instante al envolver el contenido de cada archivo en <(echo '...content...'). En tu propio desarrollo, trabajar con archivos reales será más fácil.

Obtención de datos de incidencias desde GitHub

El siguiente ejemplo incluye un programa CEL completo que obtendrá datos sobre los problemas de la API de GitHub. Su estado de entrada inicial tiene una URL para el endpoint de la API y cierta información sobre cómo debe manejar la paginación. El programa CEL utiliza los datos del estado de entrada para generar una solicitud. Decodificará la respuesta, generará eventos a partir de ella y los devolverá como parte de su estado de salida.

Su primera evaluación produce la siguiente salida:

Los eventos se eliminarán y, cuando se ejecuten en la entrada CEL, se publicarán para su ingesta. El resto de la salida se proporcionará a la siguiente evaluación del programa CEL como estado de entrada.

Para entender cómo funciona ese programa CEL, veremos algunos ejemplos más pequeños de CEL y hablaremos con más detalle sobre cómo funciona la entrada de CEL.

Conceptos básicos de CEL

En el lenguaje CEL, no hay declaraciones, solo hay expresiones. Cada expresión CEL exitosa se evalúa hasta un valor final. Esta es una de las expresiones CEL más pequeñas que puedes escribir, junto con su salida:

Muchas expresiones simples son intuitivas. Las operaciones matemáticas solo se admiten en valores del mismo tipo (por ejemplo, int con int), así que convierte los tipos según necesites (aquí de int a double):

No existen variables en el lenguaje CEL, pero una expresión puede recibir un nombre y usarse en una expresión más amplia con la ayuda de la macro as de Mito. En este ejemplo, la expresión (1 + 1) evalúa al valor 2 y .as(n, ...) da a ese valor el nombre n para su uso en la expresión "one plus one is "+string(n):

También es posible acumular información en un mapa y usarla más tarde en la expresión, como se demuestra aquí al usar with:

Mira ese ejemplo otra vez. Observa que la parte anidada, ({ "data": data, "size": size(data), }), nos da la forma del valor final. Es un mapa con las claves "data" y "size". Los valores de esas claves dependen de data, que la define la parte exterior de la expresión. Leer las expresiones de CEL desde adentro hacia afuera puede ayudar a ver rápidamente qué devolverán.

CEL no tiene declaraciones de flujo de control, como if, pero la ramificación condicional se puede hacer con el operador ternario:

Los bucles ilimitados y la recursión no son compatibles, ya que CEL no es un lenguaje de Turing completo. Esto hace que el tiempo de ejecución sea predecible y proporcional al tamaño de los datos de entrada y a la complejidad de la expresión.

Aunque los bucles sin límites no son posibles en expresiones CEL individuales, puedes procesar listas y mapas con macros como map:

En esta sección, abordamos los siguientes temas:

  • Textos, números, listas y mapas.
  • Concatenación de texto.
  • Operaciones matemáticas.
  • Conversión de tipos.
  • Condicionales.
  • Nombrando subexpresiones.
  • Procesando colecciones.

A continuación, veremos cómo hacer solicitudes HTTP.

Solicitudes

Mito extiende el CEL con la capacidad de realizar solicitudes HTTP:

Las solicitudes se pueden construir explícitamente antes de que se ejecuten. Esto permite utilizar diferentes métodos HTTP y agregar encabezados y un cuerpo.

En este ejemplo, construimos una URL con la ayuda de format_query, agregamos un encabezado a la solicitud y parseamos el cuerpo de la respuesta con decode_json. Cuando se te da la opción -log_requests, Mito registra información detallada en formato JSON sobre cada solicitud y respuesta.

Gestión del estado y evaluaciones

Ahora que ya vimos cómo realizar solicitudes y los conceptos básicos de CEL necesarios para producir el estado de salida deseado, veamos más de cerca qué debemos incluir en el estado de salida y cómo eso nos permite dirigir el procesamiento posterior.

El programa CEL de una integración debe asegurarse de que su estado de salida sea adecuado para su uso como entrada de la siguiente evaluación. La configuración establece el estado inicial, y eso debe repetirse en la salida con los cambios apropiados. Una forma sencilla de hacerlo es usar state.with({ ... }), para repetir el mapa de estado con algunas sobrescrituras. Un patrón común para programas pequeños es envolver todo el programa en state.with(), para que la propagación de estados no tenga que repetirse en cada rama que genere datos de salida (por ejemplo: éxito, errores).

Cuando hay valores de estado que se inicializan mediante una evaluación en lugar de estar codificados de forma fija en el estado de entrada inicial, el programa tendrá que comprobar si existe un valor antes de establecer el inicial. Eso es algo en lo que el soporte para sintaxis y tipos opcionales puede ayudar. Al usar un signo de interrogación antes del nombre del campo en una clave de mapa, el acceso se vuelve opcional: puede o no resolver a un valor, pero son posibles accesos opcionales adicionales y es fácil proporcionar un valor por defecto si no hay un valor presente:

En ese ejemplo, el valor del contador leído desde el estado se convierte en int porque todos los números se serializan en el estado como números de coma flotante, de acuerdo con las convenciones establecidas por el tipo Number de JSON y JavaScript. También debe tenerse en cuenta que Mito honra "want_more": true, pero cuando se ejecuta en la entrada CEL, la evaluación solo se repetiría si la salida también contiene eventos.

Es un requisito de los programas CEL ejecutados por la entrada CEL que devuelvan una clave "events" en su mapa de salida. Su valor puede ser una lista de mapas de eventos, una lista vacía o un solo mapa de eventos. El caso de evento único generalmente se utiliza para errores. El evento se publicará mediante la entrada, pero su valor también se registrará y, si estableces un valor de error.message, se usará para actualizar el estado de salud de la Fleet de la integración. Si tu programa produce un solo evento sin error, es mejor envolverlo en una lista.

Echa otro vistazo a la salida de nuestro programa de problemas de GitHub de antes:

El programa gestionó eficazmente su estado, mediante:

  • Valores de estado iniciales repetidos en url, per_pagey max_pages.
  • Agregar un estado que debería persistir durante los reinicios en cursor.page.
  • Devolver eventos listos para publicar en la lista events.
  • Solicito una reevaluación inmediata con want_more: true.

Ahora que entiendes el acceso opcional y la gestión de estados, así como los conceptos básicos de CEL y las solicitudes HTTP, deberías poder leer el programa completo de incidencias de GitHub. Intenta ejecutarlo con Mito y experimentar con algunos cambios.

Revisión y recursos

En este artículo, analizamos qué es el lenguaje CEL y cómo se ha extendido en la biblioteca Mito para su uso en la entrada CEL. Vimos la flexibilidad de CEL en un programa de ejemplo que obtiene información de incidencias de la API de GitHub, y repasamos todos los detalles necesarios para comprender ese programa, incluidos el acceso a la configuración en el estado inicial, la interacción con las API HTTP, el retorno de eventos para su ingesta y la gestión del estado para ejecuciones posteriores del programa.

Para aprender más y construir integraciones mediante la entrada CEL, hay varios recursos que valen la pena explorar:

Y quizás el recurso más valioso para crear integraciones con la entrada CEL sea el código CEL de las integraciones existentes de Elastic, que se puede encontrar en GitHub:

cel.yml.hbs archivos en el repositorio de integraciones de Elastic - GitHub

Contenido relacionado

¿Estás listo para crear experiencias de búsqueda de última generación?

No se logra una búsqueda suficientemente avanzada con los esfuerzos de uno. Elasticsearch está impulsado por científicos de datos, operaciones de ML, ingenieros y muchos más que son tan apasionados por la búsqueda como tú. Conectemos y trabajemos juntos para crear la experiencia mágica de búsqueda que te dará los resultados que deseas.

Pruébalo tú mismo