Technique

Utiliser Painless dans des champs scriptés Kibana

Grâce à Kibana, les utilisateurs peuvent effectuer des recherches et visualiser les données stockées dans Elasticsearch de façon efficace. À des fins de visualisation, Kibana recherche des champs définis dans les mappings Elasticsearch et les propose à l'utilisateur pour créer son graphique. Mais que se passe-t-il si vous avez oublié de définir une valeur importante en tant que champ séparé dans votre schéma ? Et si vous souhaitez associer deux champs et les traiter comme un champ unique ? C'est là que les champs scriptés Kibana entrent en scène.

Les champs scriptés existent en fait depuis les débuts de Kibana 4. À leurs débuts, le seul moyen de les définir reposait sur Lucene Expressions, un langage de script dans Elasticsearch qui traite exclusivement les valeurs numériques. Par conséquent, les capacités des champs scriptés se limitaient à un sous-ensemble de cas d'utilisation. Dans sa version 5.0, Elasticsearch a introduit Painless, un langage de script puissant permettant d'utiliser plusieurs types de données. Les champs scriptés dans Kibana 5.0 sont par conséquent bien plus puissants.

Dans la suite de ce blog, nous vous montrerons comment créer des champs scriptés pour des cas d'utilisation courants. Nous le ferons à l'aide d'un ensemble de données provenant du tutoriel Premiers pas avec Kibana et utiliserons une instance d'Elasticsearch et de Kibana fonctionnant sous Elastic Cloud, que vous pouvez créer gratuitement.

La vidéo suivante vous montre comment créer une instance personnelle d'Elasticsearch et de Kibana dans Elastic Cloud et y charger un exemple d' ensembles de données . 

Fonctionnement des champs scriptés

Elasticsearch vous permet d'indiquer des champs scriptés à chaque demande. Grâce à Kibana, vous pouvez maintenant définir une seule fois un champ scripté dans la section Gestion, pour pouvoir l'utiliser à plusieurs endroits de l'interface utilisateur à l'avenir. Remarque : Kibana stocke les champs scriptés à côté de ses autres configurations dans l'index .kibana, cependant, cette configuration est propre à Kibana, et les champs scriptés Kibana ne sont pas exposés aux utilisateurs d'API d'Elasticsearch.

Lorsque vous définissez un champ scripté dans Kibana, vous avez le choix entre plusieurs langages de script, ce qui vous permet de choisir parmi tous les langages installés sur les nœuds Elasticsearch sur lesquels les scripts dynamiques sont activés. Par défaut, il s'agit de "expression" et "painless" dans la version 5.0 et uniquement de "expression" dans la version 2.x. Vous pouvez installer d'autres langages de script et activer les scripts dynamiques pour ceux-ci. Cependant, ce n'est pas recommandé, car ils ne peuvent pas être suffisamment utilisés dans des sandbox  et sont désormais obsolètes.

Les champs scriptés fonctionnent sur un seul document Elasticsearch à la fois, mais peuvent référencer plusieurs champs dans ce document. Par conséquent, il convient d'utiliser les champs scriptés pour associer ou transformer les champs au sein d'un même document, mais pas pour effectuer des calculs basés sur plusieurs documents (calculs temporels par exemple). Les expressions Painless et Lucene fonctionnent toutes les deux sur des champs stockés dans doc_values. Par conséquent, pour les données de chaîne, vous devrez stocker la chaîne dans le mot de passe de types de données. Les champs scriptés basés sur Painless ne peuvent pas non plus fonctionner directement sur _source.

Une fois les champs scriptés définis dans "Gestion", l'utilisateur peut interagir avec eux de la même façon qu'avec les autres champs de Kibana. Les champs scriptés apparaissent automatiquement dans la liste de champs Discover et sont disponibles dans Visualize pour créer des visualisations. Kibana transmet simplement les définitions des champs scriptés à Elasticsearch au moment de la requête pour qu'elles soient évaluées. L'ensemble de données qui en découle est associé à d'autres résultats provenant d'Elasticsearch et est présenté à l'utilisateur sous forme de tableau ou de graphique.

À l'heure où nous rédigeons ce blog, il existe quelques limites connues lorsque vous travaillez avec des champs scriptés. Vous pouvez appliquer la plupart des agrégations Elasticsearch disponibles dans le Visual Builder de Kibana à des champs scriptés, à l'exception notable de l'agrégation de termes importants. Vous pouvez également filtrer les champs scriptés grâce à la barre de filtre présente dans Discover, Visualize et le tableau de bord. Cependant, faites attention à rédiger les scripts qui renverront bien des valeurs bien définies, comme montré ci-dessous. Il est également important de consulter la section "Bonnes pratiques" ci-dessous pour vous assurer de ne pas déstabiliser votre environnement lorsque vous utilisez les champs scriptés.

La vidéo suivante vous montre comment utiliser Kibana pour créer des champs scriptés.

Exemples de champ scripté

Cette section présente quelques exemples de scénarios courants de champs cryptés Lucene expressions et Painless dans Kibana. Comme indiqué précédemment, ces exemples ont été développés sur un ensemble de données provenant du tutoriel Premiers pas avec Kibana et suppose que vous utilisez Elasticsearch et Kibana 5.1.1, car il existe quelques problèmes connus concernant le filtrage et le tri sur certains types de champs scriptés dans les versions précédentes.

La plupart des champs scriptés devraient être immédiatement opérationnels, car Lucene expressions et Painless sont activés par défaut dans Elasticsearch 5.0. La seule exception concerne les scripts qui nécessitent une analyse des champs basée sur regex. Dans ce cas, vous devrez définir les paramètres suivants dans elasticsearch.yml pour activer la correspondance regex pour Painless : script.painless.regex.enabled: true

Réaliser un calcul sur un champ unique

  • Exemple : calculez des kilooctets à partir d'octets
  • Langage : expressions
  • Type de retour : nombre
 doc['bytes'].value / 1024

Remarque : n'oubliez pas que les champs scriptés Kibana fonctionnent uniquement sur un seul document à la fois, il n'y a donc aucun moyen de réaliser des calculs temporels dans un champ scripté.

Calcul de date donnant lieu un nombre

  • Exemple : analysez des dates en heure de la journée
  • Langage : expressions
  • Type de retour : nombre

Lucene expressions fournit un grand nombre de fonctions de manipulation de date qui sont immédiatement opérationnelles. Cependant, puisque Lucene expressions ne renvoie que des valeurs numériques, nous devrons utiliser Painless pour renvoyer un jour de la semaine basé sur une chaîne (ci-dessous).

 doc['@timestamp'].date.hourOfDay

Remarque : le script ci-dessus renverra les chiffres 1 à 24

doc['@timestamp'].date.dayOfWeek

Remarque : le script ci-dessus renverra les chiffres 1 à 7

Combiner deux valeurs de chaîne

  • Exemple : combinez la source et la destination, ou le prénom et le nom de famille
  • Langage : painless
  • Type de retour : chaîne
 doc['geo.dest.keyword'].value + ':' + doc['geo.src.keyword'].value

Remarque : étant donné que les champs scriptés ont besoin de fonctionner sur des champs dans doc_values, nous utilisons les versions .keyword des chaînes ci-dessus.

Présentation de la logique

  • Exemple : renvoyez le terme "téléchargement lourd" pour tout document dépassant 10 000 octets
  • Langage : painless
  • Type de retour : chaîne
 if (doc['bytes'].value > 10000) { 
return "big download";
}
return "";

Remarque : lors de la présentation de la logique, assurez-vous que chaque chemin d'exécution possède un énoncé de retour et une valeur de retour bien définis (non nuls). Par exemple, le champ scripté ci-dessus échouera en indiquant une erreur de compilation s'il est utilisé dans des filtres Kibana sans énoncé de retour à la fin ou si l'énoncé est renvoyé comme nul. Souvenez-vous également que le fait de décomposer la logique en fonctions n'est pas compatible avec les champs scriptés Kibana. 

Renvoyer une sous-chaîne

  • Exemple : renvoyez la partie qui suit le dernier slash dans l'URL
  • Langage : painless
  • Type de retour : chaîne
 def path = doc['url.keyword'].value;
if (path != null) {
int lastSlashIndex = path.lastIndexOf('/');
if (lastSlashIndex > 0) {
return path.substring(lastSlashIndex+1);
}
}
return "";

Remarque : si possible, évitez d'utiliser des expressions regex pour extraire des sous-chaînes, car les opérations indexOf() utilisent moins de ressources et sont moins enclines aux erreurs. 

Faire correspondre une chaîne à l'aide de regex, et agir sur une correspondance

  • Exemple : renvoyez une "erreur" de chaîne si vous trouvez une "erreur" de sous chaîne dans le champ "référent". Dans le cas contraire, renvoyez une chaîne "aucune erreur".
  • Langage : painless
  • Type de retour : chaîne
if (doc['referer.keyword'].value =~ /fr/error/) { 
return "error"
} else {
return "no error"
}

Remarque : une syntaxe regex est utile pour les conditions basées sur une correspondance regex. 

Faire correspondre une chaîne et renvoyer cette correspondance

  • Exemple : renvoyez le domaine, la chaîne après le dernier point dans le champ "hébergeur".
  • Langage : painless
  • Type de retour : chaîne
def m = /^.*\.([a-z]+)$/.matcher(doc['host.keyword'].value);
if ( m.matches() ) {
return m.group(1)
} else {
return "no match"
}

Remarque : définir un objet à l'aide des fonctions de correspondance regex vous permet d'extraire des groupes de caractères correspondants au regex et de les renvoyer. 

Faire correspondre un nombre et renvoyer cette correspondance

  • Exemple : renvoyez le premier octet de l'adresse IP (stocké en tant que chaîne) et traitez-le comme un nombre.
  • Langage : painless
  • Type de retour : nombre
 def m = /^([0-9]+)\..*$/.matcher(doc['clientip.keyword'].value);
if ( m.matches() ) {
return Integer.parseInt(m.group(1))
} else {
return 0
}

Remarque : Il est important de renvoyer les bons types de données dans un script. La correspondance regex renvoie une chaîne, même si un nombre correspond, vous devez donc explicitement le convertir en nombre entier lors du renvoi. 

Calcul de date donnant lieu à des chaînes

  • Exemple : analysez les données en jours de la semaine en chaîne
  • Langage : painless
  • Type de retour : chaîne
LocalDateTime.ofInstant(Instant.ofEpochMilli(doc['@timestamp'].value), ZoneId.of('Z')).getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault())

Remarque : puisque Painless est compatible avec tous les types natifs de Java, il fournit un accès à des fonctions natives de ces types, telles que LocalDateTime(), qui peuvent vous servir lorsque vous effectuez des calculs de dates avancés.

Bonnes pratiques

Comme vous pouvez le voir, le langage scripté Painless vous permet d'extraire de façon efficace des informations utiles des champs arbitraires stockés dans Elasticsearch via les champs scriptés Kibana. Toutefois, un grand pouvoir implique de grandes responsabilités. 

Voici quelques bonnes pratiques concernant l'utilisation des champs scriptés Kibana.

  • Utilisez toujours un environnement de développement pour expérimenter avec les champs scriptés. Les champs scriptés sont immédiatement actifs après que vous les avez enregistrés dans la section Gestion de Kibana (ils apparaîtront par exemple sur l'écran Discover pour ce modèle d'indexation pour tous les utilisateurs). Par conséquent, vous ne devriez pas développer de champs scriptés directement en production. Nous vous recommandons de commencer à tester votre syntaxe dans un environnement de développement, d'évaluer l'impact des champs scriptés sur des ensembles et des volumes de données réalistes lors de la mise en service, et de les faire passer en production uniquement à ce moment-là. 
  • Une fois que vous avez l'assurance que le champ scripté apporte de la valeur à vos utilisateurs, pensez à modifier votre ingestion de sorte à extraire le champ au moment de l'indexation pour les nouvelles données. Elasticsearch mettra ainsi moins de temps à traiter les requêtes, et les utilisateurs de Kibana bénéficieront donc de temps de réponse plus rapides. Vous pouvez également utiliser l'API _reindex dans Elasticsearch pour réindexer les données existantes.