13 Dezember 2016 Engineering

Painless in Kibanas geskripteten Feldern benutzen

Von Tanya Bragin

Kibana bietet leistungsstarke Möglichkeiten zum Durchsuchen und Visualisieren von Daten, die in Elasticsearch gespeichert sind. Für die Visualisierung sucht Kibana nach Feldern, die in Elasticsearch-Mappings definiert sind, und zeigt sie dem Benutzer als Optionen bei der Diagrammerstellung an. Aber was ist, wenn du vergisst, einen wichtigen Wert in deinem Schema als separates Feld zu definieren? Oder wenn du zwei Felder kombinieren und als eines bearbeiten willst? Da kommen die geskripteten Felder in Kibana ins Spiel.

Eigentlich gibt es die geskripteten Felder schon seit den Anfängen von Kibana 4. Bei ihrer Einführung konnte man sie jedoch nur über Lucene Expressions, eine Skriptsprache in Elasticsearch ausschließlich für Zahlenwerte, definieren. Daher waren die Möglichkeiten der geskripteten Felder auf eine geringe Anzahl von Anwendungsfällen beschränkt. In Version 5.0 führte Elasticsearch Painless ein, eine sichere und leistungsfähige Skriptsprache, die mit verschiedenen Datentypen zurechtkommt. Deswegen sind die geskripteten Felder in Kibana 5.0 so viel leistungsfähiger.

Der Rest dieses Artikels erklärt euch, wie ihr geskriptete Felder für gängige Anwendungsfälle erstellt. Dabei nutzen wir einen Datensatz aus dem „Erste Schritte mit Kibana“-Tutorial und eine Instanz mit Elasticsearch und Kibana in der Elastic Cloud, die ihr kostenlos testen könnt.

Im folgenden Video erfahrt ihr, wie ihr eure eigene Instanz mit Elasticsearch und Kibana in der Elastic Cloud startet und einen Beispieldatensatz ladet.


So funktionieren geskriptete Felder

In Elasticsearch kannst du bei jeder Anfrage geskriptete Felder verwenden. Kibana verbessert diese Funktion noch, indem es dir erlaubt, ein geskriptetes Feld einmalig im Abschnitt Management zu definieren. Dieses Feld kann ab da an mehreren Stellen in der Benutzeroberfläche genutzt werden. Beachte aber, dass geskriptete Felder in Kibana zusammen mit der übrigen Konfiguration im Index .kibana speichert werden und diese Konfiguration ausschließlich für Kibana ist. Das heißt, die geskripteten Kibana-Felder stehen den API-Nutzern von Elasticsearch nicht zur Verfügung.

Wenn du ein geskriptetes Feld in Kibana definierst, kannst du eine Skriptsprache aus allen auf den Elasticsearch Nodes installierten Sprachen auswählen, die dynamische Skripterstellung unterstützen. Standardmäßig sind das „Expression“ und „Painless“ in 5.0 und nur „Expression“ in 2.x. Du kannst andere Skriptsprachen installieren und dynamische Skripterstellung für sie aktivieren. Das wird jedoch nicht empfohlen, da sie nicht vollständig auf ihre Sandboxed beschränkt werden können und als deprecated markiert sind.

Geskriptete Felder laufen nur für ein Elasticsearch-Dokument gleichzeitig, können aber auf mehrere Felder in diesem Dokument verweisen. Damit könnt ihr geskriptete Felder benutzen, um Felder in einem Dokument zu kombinieren oder umzuwandeln, aber keine Berechnungen aufgrund mehrerer Dokumente anstellen (z. B. Zeitreihenanalyse). Sowohl Painless als auch Lucene Expressions berücksichtigen Felder, die in doc_values gespeichert sind. Für String-Daten müsst ihr also den String im Datentyp keywords speichern. Mit Painless geskriptete Felder funktionieren außerdem nicht direkt mit _source.

Sobald die geskripteten Felder unter „Management“ gespeichert sind, können die Benutzer mit ihnen im Rest von Kibana auf dieselbe Weise interagieren wie mit anderen Feldern. Geskriptete Felder werden automatisch in der Feldliste „Discover“ angezeigt und stehen in „Visualize“ für die Erstellung von Visualisierungen zur Verfügung. Kibana reicht die Definitionen der geskripteten Felder einfach zum Query-Zeitpunkt zur Evaluierung an Elasticsearch weiter. Der daraus resultierende Datensatz wird mit anderen Ergebnissen aus Elasticsearch kombiniert und dem Benutzer in einer Tabelle oder einem Diagramm angezeigt.

Während wir diesen Artikel schreiben, sind uns einige Beschränkungen bei der Arbeit mit geskripteten Feldern bekannt. Du kannst die meisten im Kibana Visual Builder verfügbaren Elasticsearch-Aggregationen auf geskriptete Felder anwenden, wobei die wichtigste Ausnahme die Aggregation „significant terms“ ist. Du kannst geskriptete Felder auch über die Filterleiste in Discover, Visualize und im Dashboard filtern. Dafür musst du jedoch ordentliche Skripte schreiben, die gut definierte Werte ausgeben. Wie das geht, zeigen wir im Verlauf dieses Artikels. Außerdem solltest du dir im Folgenden den Abschnitt „Bewährte Methoden“ ansehen, damit du die Stabilität deiner Umgebung nicht mit geskripteten Feldern gefährdest.

Im folgenden Video seht ihr, wie ihr in Kibana geskriptete Felder erstellt.

Beispiele für geskriptete Felder

In diesem Abschnitt zeigen wir euch ein paar mit Lucene Expressions und Painless geskriptete Felder in Kibana in gängigen Szenarien. Wie oben erwähnt wurden diese Beispiele auf Grundlage eines Datensatzes aus dem „Erste Schritte mit Kibana“-Tutorial erarbeitet. Wir gehen auch davon aus, dass du Elasticsearch und Kibana 5.1.1 benutzt, da es einige bekannte Probleme mit dem Filtern und Sortieren bestimmter geskripteter Felder in früheren Versionen gibt.

Zum Großteil sollten geskriptete Felder ohne Zusatzarbeit funktionieren, da Lucene Expressions und Painless standardmäßig in Elasticsearch 5.0 aktiviert sind. Die einzige Ausnahme bilden Skripte, für die ein Regex-basiertes Parsing der Felder erforderlich ist. Dafür müsst ihr folgende Einstellung in elasticsearch.yml für Regex-Matching in Painless setzen: script.painless.regex.enabled: true

Eine Berechnung für ein einzelnes Feld durchführen

  • Beispiel: Kilobytes aus Bytes berechnen
  • Sprache: expressions
  • Rückgabetyp: number
 doc['bytes'].value / 1024

Hinweis: Beachtet, dass geskriptete Felder in Kibana nur mit einem Dokument gleichzeitig funktionieren. Ihr könnt also keine Zeitreihenanalysen mit geskripteten Feldern durchführen.

Datumsberechnung mit Ausgabe einer Zahl

  • Beispiel: Datum in Stunde des Tages umrechnen
  • Sprache: expressions
  • Rückgabetyp: number

Lucene Expressions bieten von vornherein eine ganze Menge an Funktionen zur Datumsbearbeitung out-of-the-box. Allerdings geben Lucene Expressions nur Zahlenwerte aus, weshalb wir Painless verwenden müssen, um einen String-basierten Wochentag ausgeben zu können (unten).

 doc['@timestamp'].date.hourOfDay

Hinweis: Das obige Skript gibt 1–24 aus.

doc['@timestamp'].date.dayOfWeek

Hinweis: Das obige Skript gibt 1–7 aus.

Zwei String-Werte kombinieren

  • Beispiel: Quelle und Ziel oder Vor- und Nachnamen kombinieren
  • Sprache: painless
  • Rückgabetyp: string
 doc['geo.dest.keyword'].value + ':' + doc['geo.src.keyword'].value

Hinweis: Da geskriptete Felder nur Felder in doc_values verarbeiten können, verwenden wir .keyword-Versionen der obigen Strings.

Logik hinzufügen

  • Beispiel: Die Kennzeichnung „großer Download“ für alle Dokumente mit mehr als 10.000 Bytes ausgeben
  • Sprache: painless
  • Rückgabetyp: string
 if (doc['bytes'].value > 10000) { 
    return "großer download";
}
return "";

Hinweis: Bei der Einführung von Logik musst du darauf achten, dass jeder Ausführungspfad über ein ordnungsgemäß definiertes Ausgabe-Statement und einen ordnungsgemäß definierten Ausgabewert (nicht null) verfügt. Das obige geskriptete Feld schlägt zum Beispiel mit einem Compile-Fehler fehl, wenn es in den Kibana-Filtern ohne das Return-Statement am Ende benutzt wird oder das Statement null ausgibt. Beachte auch, dass du bei geskripteten Kibana-Feldern die Logik nicht in mehrere Funktionen aufspalten kannst. 

Einen Teilstring ausgeben

  • Beispiel: Den Teil nach dem letzten Schrägstrich in der URL ausgeben
  • Sprache: painless
  • Rückgabetyp: string
 def path = doc['url.keyword'].value;
if (path != null) {
    int lastSlashIndex = path.lastIndexOf('/');
    if (lastSlashIndex > 0) {
    return path.substring(lastSlashIndex+1);
    }
}
return "";

Hinweis: Vermeide soweit als möglich Regex-Ausdrücken zur Extraktion von Teilstrings, da indexOf()-Operationen ressourcenschonender und weniger fehleranfällig sind. 

Einen String mit Regex abgleichen und den Treffer weiterverarbeiten

  • Beispiel: Einen String „error“ ausgeben, wenn der Substring „error“ im Feld „referer“ gefunden wird, ansonsten einen String „no error“ ausgeben.
  • Sprache: painless
  • Rückgabetyp: string
if (doc['referer.keyword'].value =~ /error/) { 
return "error"
} else {
return "no error"
}

Hinweis: Die vereinfachte Regex-Syntax eignet sich für Bedingungen auf Grundlage eines Regex-Treffers.  

Einen String abgleichen und den Treffer ausgeben

  • Beispiel: Domain, den String nach dem letzten Punkt im Feld „host“, ausgeben.
  • Sprache: painless
  • Rückgabetyp: string
def m = /^.*\.([a-z]+)$/.matcher(doc['host.keyword'].value);
if ( m.matches() ) {
   return m.group(1)
} else {
   return "no match"
}

Hinweis: Wenn du ein Objekt über die Regex-Funktion matcher() definierst, kannst du Zeichengruppen, die mit dem Regex übereinstimmen, extrahieren und ausgeben. 

Eine Zahl abgleichen und den Treffer ausgeben

  • Beispiel: Das erste Oktett einer IP-Adresse (als String gespeichert) ausgeben und als Zahl bearbeiten.
  • Sprache: painless
  • Rückgabetyp: number
 def m = /^([0-9]+)\..*$/.matcher(doc['clientip.keyword'].value);
if ( m.matches() ) {
   return Integer.parseInt(m.group(1))
} else {
   return 0
}

Hinweis: Es muss unbedingt der richtige Datentyp in einem Skript ausgegeben werden. Ein Regex-Treffer gibt einen String aus, auch wenn der Treffer eine Zahl ist. Du solltest ihn daher explizit bei der Ausgabe in eine Ganzzahl umwandeln. 

Datumsberechnung mit String als Ergebnis

  • Beispiel: Datum in Wochentag als String umwandeln
  • Sprache: painless
  • Rückgabetyp: string
LocalDateTime.ofInstant(Instant.ofEpochMilli(doc['@timestamp'].value), ZoneId.of('Z')).getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault())

Hinweis: Da Painless alle nativen Typen von Java unterstützt, bietet es auch Zugang zu nativen Funktionen für diese Typen – zum Beispiel LocalDateTime() – was für die erweiterte Datumsberechnung hilfreich ist.

Bewährte Methoden

Wie du siehst, bietet die Skriptsprache Painless vielfältige Möglichkeiten für die Extraktion nützlicher Informationen mithilfe von geskripteten Kibana-Feldern aus Elasticsearch. Allerdings bringt große Macht auch große Verantwortung mit sich. 

Im Folgenden führen wir einige bewährte Methoden zur Benutzung der geskripteten Kibana-Felder an.

  • Benutze immer eine Entwicklungsumgebung, wenn du mit geskripteten Feldern experimentierst. Geskriptete Felder sind sofort nach dem Speichern im Management-Abschnitt von Kibana aktiv. Beispielsweise werden sie in der Discover-Ansicht für alle Benutzer für dieses Indexmuster angezeigt. Deshalb solltest du sie nicht direkt in der Produktion erstellen. Wir empfehlen, deine Syntax zuerst in einer Entwicklungsumgebung zu testen, die Auswirkungen auf praxisnahe Datensätze und -volumina im Staging zu testen und sie erst dann in die Produktion zu übernehmen. 
  • Sobald du sicher bist, dass das geskriptete Feld deinen Benutzern einen Mehrwert bietet, kannst du deine Datenspeicherung so bearbeiten, dass das Feld zur Indexzeit extrahiert wird. So spart Elasticsearch Ressourcen zum Query-Zeitpunkt und liefert schneller Ergebnisse für Kibana-Benutzer. Außerdem kannst du mit der _reindex-API in Elasticsearch vorhandene Daten neu indizieren.