Common Expression Language (CEL): Wie CEL-Eingaben die Datenerfassung in Elastic Agent-Integrationen verbessern

Erfahren Sie, wie sich die Common Expression Language von anderen Programmiersprachen unterscheidet, wie wir sie für CEL-Eingaben von Filebeat erweitert haben und welche Flexibilität sie Ihnen bietet, um Ihre Datenerfassungslogik in Elastic Agent-Integrationen auszudrücken.

Elastic Agent-Integrationen ermöglichen es Nutzern, Daten aus einer Vielzahl von Quellen in Elasticsearch zu ingestieren. Sie kombinieren Erfassungslogik, Ingest-Pipelines, Dashboards und andere Artefakte in einem Paket, das über die Kibana-Web-Schnittstelle installiert und verwaltet werden kann.

Integrationen konfigurieren Filebeat-Eingaben, um die Datenerfassung durchzuführen. Um Daten aus HTTP-APIs zu sammeln, haben wir oft HTTP-JSON-Eingaben verwendet. Jedoch können selbst grundlegende Listing-APIs in den Details stark voneinander abweichen, und das Modell der YAML-konfigurierten Transformationen der HTTP-JSON-Eingabe kann es umständlich und manchmal unmöglich machen, die erforderliche Sammlungslogik auszudrücken.

Common Expression Language (CEL)-Eingaben wurde eingeführt, um eine flexiblere Interaktion mit HTTP-APIs zu ermöglichen. CEL ist eine Sprache, die dafür entwickelt wurde, in Anwendungen eingebettet zu werden, die eine schnelle, sichere und erweiterbare Möglichkeit zur Darstellung von Bedingungen und Datentransformationen erfordern. CEL-Eingaben ermöglichen es einem Integration Builder, einen Ausdruck zu schreiben, der Einstellungen lesen, seinen eigenen Status verfolgen, Anfragen stellen, Reaktionen verarbeiten und letztendlich Ereignisse zurückgeben kann, die für den Ingest bereit sind.

In diesem Artikel werden wir uns ansehen, wie sich CEL von anderen Programmiersprachen unterscheidet, wie wir es für CEL-Eingaben erweitert haben und welche Flexibilität und Möglichkeiten es Ihnen bietet, um Ihre Datenerfassungslogik auszudrücken.

CEL und seine Funktionsweise als Eingabe

CEL ist eine Ausdruckssprache. Es enthält keine Anweisungen. Wenn Sie CEL schreiben, geben Sie dem Programm nicht durch Anweisungen vor, was es tun soll, sondern teilen ihm durch einen Ausdruck mit, welchen Wert es erzeugen soll. Jeder CEL-Ausdruck erzeugt einen Wert, und kleinere Ausdrücke können zu einem größeren Ausdruck kombiniert werden, um ein Ergebnis nach komplexeren Regeln zu erzeugen. Später werden wir sehen, wie man Ausdrücke für Dinge verwendet, die in anderen Sprachen möglicherweise mit Anweisungen geschrieben werden.

CEL ist bewusst eine nicht Turing-vollständige Sprache. Es erlaubt keine unbegrenzten Schleifen. Später werden wir sehen, wie man Listen und Maps mithilfe von Makros verarbeiten kann. Durch die Vermeidung unbegrenzter Schleifen garantiert die Sprache jedoch eine vorhersehbare und begrenzte Ausführungszeit für einzelne Ausdrücke.

Die CEL-Eingabe wird mit einem CEL-Programm (einem Ausdruck) und einem Anfangszustand konfiguriert. Der Zustand wird als Eingabe für das Programm bereitgestellt. Das Programm wird ausgewertet, um einen Ausgabezustand zu erzeugen. Wenn der Ausgabezustand eine Liste von Ereignissen enthält, werden diese entfernt und veröffentlicht. Der Rest des Ausgabezustands wird als Eingabe für die nächste Auswertung verwendet. Wenn der Ausgabezustand ein oder mehrere Ereignisse und den Flag want_more: trueenthält, wird die nächste Auswertung sofort durchgeführt; ansonsten schläft er für den Rest der konfigurierten Intervallzeit, bevor er weiterfährt. Hier ist ein vereinfachtes Diagramm des Kontrollflusses der Eingabe:

Die Ausgabe jeder Auswertung wird als Eingabe an die nächste Auswertung weitergeleitet, solange die Eingabe läuft. Ausgabedaten unter dem Schlüssel „cursor“ werden auf der Festplatte beibehalten und nach dem Neustart der Eingabe wieder geladen, aber der Rest des Zustands wird nicht über Neustarts hinweg beibehalten.

Die CEL-Sprache selbst hat begrenzte Funktionalität und vermeidet Nebenwirkungen, ist jedoch erweiterbar. Die cel-go-Implementierung fügt einige Funktionen hinzu, darunter optionale Syntax und Typen. Die Mito-Bibliothek baut auf cel-go auf und fügt weitere Funktionen hinzu, darunter die Möglichkeit, HTTP-Anfragen zu stellen. Die CEL-Eingabe verwendet die Mito-Version von CEL.

Zusammenarbeit mit Mito

Für den Aufbau oder die Fehlersuche einer Integration mit CEL-Eingaben ist es am wichtigsten zu verstehen, welchen Ausgangszustand Ihr CEL-Programm für einen gegebenen Eingabezustand erzeugt. Während der Entwicklung kann es umständlich sein, Ihr CEL-Programm über die Eingabe auszuführen, umgeben vom vollständigen Elastic Stack. Eine Möglichkeit, eine schnellere Feedback-Schleife zu erreichen, besteht darin, das Befehlszeilentool von Mito zu verwenden, mit dem Sie ein CEL-Programm direkt ausführen und den Ausgang sehen können, den es für eine bestimmte Eingabe erzeugt.

Mito ist in Go geschrieben und kann wie folgt installiert werden:

Wenn Sie ein CEL-Programm mit Mito ausführen, geben Sie ihm in der Regel zwei Dateien: eine JSON-Datei mit dem anfänglichen Eingabezustand und eine weitere Datei mit dem Quellcode Ihres CEL-Programms.

Um das Kopieren und Einfügen zu erleichtern, sind die Beispiele in diesem Artikel als einzelne Befehle geschrieben, bei denen die Shell temporäre Dateien spontan erstellt, indem der Inhalt jeder Datei in <(echo '...content...')eingeschlossen wird. Bei Ihren eigenen Entwicklungsprojekten wird Ihnen die Arbeit mit realen Dateien leichter fallen.

Problemdaten von GitHub abrufen

Das folgende Beispiel enthält ein vollständiges CEL-Programm, das Daten zu Problemen aus der GitHub-API abruft. Der anfängliche Eingabezustand enthält eine URL für den API-Endpunkt sowie einige Informationen darüber, wie die Paginierung gehandhabt werden soll. Das CEL-Programm verwendet die Daten im Eingabezustand, um eine Anfrage zu generieren. Es entschlüsselt die Antwort, erzeugt daraus Ereignisse und gibt sie als Teil seines Ausgabezustands zurück.

Die erste Auswertung liefert folgende Ausgabe:

Die Ereignisse werden entfernt, und wenn sie in der CEL-Eingabe ausgeführt werden, werden sie zur Ingestion veröffentlicht. Der Rest der Ausgabe wird dem nächsten CEL-Programmbewertungsvorgang als Eingabestand bereitgestellt.

Um zu verstehen, wie dieses CEL-Programm funktioniert, betrachten wir einige kleinere CEL-Beispiele und besprechen weitere Details zur Funktionsweise der CEL-Eingabe.

CEL-Grundlagen

In der CEL-Sprache gibt es keine Anweisungen, sondern nur Ausdrücke. Jeder erfolgreiche CEL-Ausdruck wird zu einem endgültigen Wert ausgewertet. Hier ist einer der kleinsten CEL-Ausdrücke, die Sie schreiben können, zusammen mit seiner Ausgabe:

Viele einfache Ausdrücke sind intuitiv. Mathematische Operationen werden nur bei Werten desselben Typs unterstützt (zum Beispiel int mit int), konvertieren Sie also Typen nach Bedarf (hier von int zu double):

In der CEL-Sprache gibt es keine Variablen, aber ein Ausdruck kann mit Hilfe des as-Makros von Mito benannt und in einem größeren Ausdruck verwendet werden. In diesem Beispiel wird der Ausdruck (1 + 1) zum Wert 2 ausgewertet, und .as(n, ...) gibt diesem Wert den Namen n zur Verwendung im Ausdruck "one plus one is "+string(n):

Es ist auch möglich, Informationen in einer Map zu sammeln und sie später im Ausdruck zu verwenden, wie hier mit with gezeigt wird:

Sehen Sie sich dieses Beispiel erneut an. Beachten Sie, dass der verschachtelte Teil – ({ "data": data, "size": size(data), }) – uns die Form des Endwertes vorgibt. Es ist eine Map mit den Schlüsseln "data" und "size". Die Werte für diese Schlüssel hängen von data ab, was durch den äußeren Teil des Ausdrucks definiert ist. Das Lesen von CEL-Ausdrücken von innen nach außen kann helfen, schnell zu erkennen, was sie zurückgeben.

CEL hat keine Kontrollflussanweisungen wie if, aber bedingte Verzweigungen können mit dem ternären Operator durchgeführt werden:

Unbeschränkte Schleifen und Rekursion werden nicht unterstützt, da CEL keine Turing-vollständige Sprache ist. Das macht die Ausführungszeit vorhersehbar und proportional zur Größe der Eingabedaten und zur Komplexität des Ausdrucks.

Obwohl unbeschränkte Schleifen in einzelnen CEL-Ausdrücken nicht möglich sind, können Sie Listen und Maps mit Makros wie mapbearbeiten:

In diesem Abschnitt haben wir Folgendes behandelt:

  • Zeichenfolgen, Zahlen, Listen und Maps.
  • Verkettung von Zeichenfolgen.
  • Mathematische Operationen.
  • Typumwandlung.
  • Bedingungen.
  • Benennung von Unterausdrücken.
  • Verarbeiten von Sammlungen.

Als Nächstes sehen wir uns an, wie man HTTP-Anfragen stellt.

Anfragen

Mito erweitert CEL um die Möglichkeit, HTTP-Anfragen zu stellen:

Anfragen können explizit erstellt werden, bevor sie ausgeführt werden. Dadurch ist es möglich, verschiedene HTTP-Methoden zu verwenden und Header sowie einen Body hinzuzufügen.

In diesem Beispiel erstellen wir eine URL mit Hilfe von format_query, fügen der Anfrage einen Header hinzu und parsen den Response Body mit decode_json. Wenn die Option -log_requests angeboten wird, protokolliert Mito detaillierte Informationen im JSON-Format zu jeder Anfrage und Antwort.

Zustandsverwaltung und Evaluierungen

Nachdem wir nun behandelt haben, wie man Anfragen stellt und welche CEL-Grundlagen erforderlich sind, um den gewünschten Ausgabezustand zu erzeugen, schauen wir uns genauer an, was wir in den Ausgabezustand einfügen sollten und wie wir dadurch die spätere Verarbeitung steuern können.

Das CEL-Programm einer Integration muss sicherstellen, dass sein Ausgabezustand als Eingabe für die nächste Auswertung geeignet ist. Die Konfiguration legt den Ausgabezustand fest, und dieser sollte in der Ausgabe mit allen entsprechenden Änderungen wiederholt werden. Eine einfache Möglichkeit ist, state.with({ ... })zu verwenden, um die Zustands-Map mit einigen Überschreibungen zu wiederholen. Ein gängiges Muster für kleine Programme ist es, das gesamte Programm in state.with()einzuschließen, sodass die Zustandspropagation nicht in jedem Branch wiederholt werden muss, der Ausgabedaten erzeugt (zum Beispiel Erfolg, Fehler).

Wenn es Statuswerte gibt, die durch eine Auswertung initialisiert werden und nicht fest im anfänglichen Eingabezustand kodiert sind, muss das Programm nach einem vorhandenen Wert suchen, bevor es den anfänglichen Wert setzt. Dabei kann die Unterstützung für optionale Syntax und Typen helfen. Durch die Verwendung eines Fragezeichens vor dem Feldnamen in einem Map-Schlüssel wird der Zugriff optional: Er kann zu einem Wert aufgelöst werden oder auch nicht, aber weitere optionale Zugriffe sind möglich, und es ist einfach, einen Standardwert bereitzustellen, wenn kein Wert vorhanden ist.

In diesem Beispiel wird der aus dem Zustand gelesene Zählerwert in int umgewandelt, da alle Zahlen im Zustand als Gleitkommazahlen serialisiert werden, entsprechend den Konventionen von JSON und dem Number-Typ von JavaScript. Es sollte auch beachtet werden, dass "want_more": true hier von Mito beachtet wird, aber wenn es in der CEL-Eingabe ausgeführt wird, wird die Auswertung nur wiederholt, wenn die Ausgabe auch Ereignisse enthält.

Es ist eine Voraussetzung für CEL-Programme, die über die CEL-Eingabe ausgeführt werden, dass sie in ihrer Ausgabezuordnung einen "events"-Schlüssel zurückgeben. Sein Wert kann eine Liste von Ereignis-Maps, eine leere Liste oder ein einzelnes Ereignis-Map sein. Der Fall mit dem einzelnen Ereignis wird üblicherweise für Fehler verwendet. Das Ereignis wird von der Eingabe veröffentlicht, aber auch sein Wert wird geloggt, und wenn ein error.message Wert gesetzt wird, wird dieser verwendet, um den Fleet-Gesundheitsstatus der Integration zu aktualisieren. Wenn Ihr Programm ein einzelnes Ereignis erzeugt, das keinen Fehler verursacht, ist es am besten, es in eine Liste einzuschließen.

Werfen Sie noch einmal einen Blick auf die Ausgabe unseres GitHub-Problem-Programms von vorhin:

Das Programm hat seinen Zustand effektiv verwaltet, indem:

  • Wiederholte Anfangszustandswerte in url, per_page, und max_pages.
  • Hinzufügen eines Zustands, der über Neustarts hinweg erhalten bleiben soll, in cursor.page.
  • Wiederkehrende Veranstaltungen, bereit zur Veröffentlichung in der events-Liste.
  • Sofortige Neubewertung mit want_more: true angefordert.

Jetzt, da Sie den optionalen Zugriff und die Statusverwaltung sowie die CEL-Grundlagen und HTTP-Anfragen verstehen, sollte das vollständige GitHub-Problem-Programm lesbar sein. Versuchen Sie, es mit Mito auszuführen und mit einigen Änderungen zu experimentieren.

Überprüfung und Ressourcen

In diesem Artikel haben wir untersucht, was die CEL-Sprache ist und wie sie in der Mito-Bibliothek für die Nutzung in der CEL-Eingabe erweitert wurde. Wir haben die Flexibilität von CEL in einem Beispielprogramm gesehen, das Probleminformationen von der GitHub-API abruft, und sind alle Details durchgegangen, die zum Verständnis dieses Programms notwendig sind. Dazu gehören der Zugriff auf Einstellungen im Ausgabezustand, die Interaktion mit HTTP-APIs, die Rückgabe von Ereignissen zum Ingestieren und die Verwaltung des Zustands für spätere Programmausführungen.

Wenn Sie mehr erfahren und Integrationen mit CEL-Eingaben erstellen möchten, gibt es eine Reihe von Ressourcen, die Sie sich ansehen sollten:

Die vielleicht wertvollste Ressource für die Entwicklung von Integrationen mit CEL-Eingaben ist der CEL-Code bestehender Elastic-Integrationen, der auf GitHub zu finden ist:

cel.yml.hbs Dateien im Elastic-Integrations-Repository – GitHub

Zugehörige Inhalte

Sind Sie bereit, hochmoderne Sucherlebnisse zu schaffen?

Eine ausreichend fortgeschrittene Suche kann nicht durch die Bemühungen einer einzelnen Person erreicht werden. Elasticsearch wird von Datenwissenschaftlern, ML-Ops-Experten, Ingenieuren und vielen anderen unterstützt, die genauso leidenschaftlich an der Suche interessiert sind wie Sie. Lasst uns in Kontakt treten und zusammenarbeiten, um das magische Sucherlebnis zu schaffen, das Ihnen die gewünschten Ergebnisse liefert.

Probieren Sie es selbst aus