Comment intégrer des tableaux de bord Kibana

Les ingénieurs frontend, comme moi, demandent souvent d’intégrer des tableaux de bord existants provenant de sources telles que Kibana® dans une application web JavaScript. C’est une tâche que j’ai dû effectuer à plusieurs reprises car nous voulions déployer rapidement des vues générées par les utilisateurs ou permettre aux utilisateurs de contrôler une vue donnée. À en juger par les questions que nous recevons régulièrement de la merveilleuse communauté des développeurs, je ne suis pas le seul.

Les outils de visualisation de données tels que les tableaux de bord Kibana permettent même à l’utilisateur le moins doué en conception ou en technique de créer rapidement et facilement des vues à partir des données Elasticsearch® et des vues de prototype. En effet, cela signifie que l’intégration d’un tableau de bord dans une application web existante est la partie la plus difficile, surtout si nous voulons intégrer des contrôles web personnalisés pour orienter la vue des données afin d’offrir un style et une expérience cohérents aux utilisateurs.

Je vais passer en revue des exemples de code pour intégrer des tableaux de bord Kibana dans une application web à l’aide de iframes HTML. Je vais aussi aborder l’authentification Kibana pour ces vues et comment connecter des contrôles personnalisés aux vues intégrées en JavaScript.

Qu’est-ce qu’une iframe ?

Les deux exemples présentés dans cet article utilisent une iframe pour intégrer notre tableau de bord. Une iframe, identifiée par la balise HTML<iframe>, permet d’intégrer une autre page web dans le document actuel. Plus précisément, nous allons inclure le tableau de bord Global Flight chargé à partir du jeu de données de « Sample flight data », dans notre propre déploiement Elastic®.

Lors de l’intégration d’autres sources dans votre application, il est important de s’assurer qu’il s’agit d’une source de données fiable à laquelle les utilisateurs devraient avoir accès. Nous devons utiliser des politiques de sécurité du contenu appropriées, appliquer des restrictions avec l’attribut sandbox, ainsi que des autorisations pour limiter les actions du contenu intégré. En ne spécifiant pas l’attribut sandbox dans notre iframe, nous incluons toutes les restrictions par défaut.

Les performances doivent également être prises en compte lorsque du contenu tiers est inclus dans l’application. Comme les iframes peuvent consommer plus de bande passante que les autres ressources, leur utilisation dans une seule application peut ralentir l’ensemble de l’application. Si vous souhaitez intégrer plusieurs tableaux de bord Kibana à votre application, essayez d’en limiter le nombre au maximum et effectuez des tests de performance sur l’application. Bien qu’il soit facile d’ajouter des composants et des tableaux de bord, en tant que développeurs, nous devons nous assurer de fournir les données dont les utilisateurs ont besoin plutôt que tous les contrôles superflus qu’ils souhaitent. Ainsi, au moment de choisir entre tableaux de bord et visualisations, vous devez travailler avec les consommateurs pour identifier leurs véritables besoins.

Intégration basique avec une iframe HTML

Le code pour inclure le Global Flight Dashboard dans votre application web, tel que décrit dans cet exemple de base, peut être facilement généré depuis Kibana via l’option Partager :

Diagramme de code d’intégration de Kibana

Un extrait de code d’iframe ajoutant les options pertinentes que vous avez sélectionnées, ainsi que les filtres actuels, est généré pour que vous puissiez les coller dans votre HTML :

<iframe src="https://my-deployment:9243/app/dashboards#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?embed=true&_g=(refreshInterval%3A(pause%3A!t%2Cvalue%3A0)%2Ctime%3A(from%3Anow-1y%2Fd%2Cto%3Anow))&show-top-menu=true&show-query-input=true&show-time-filter=true" height="600" width="800"></iframe>

L’extrait généré utilise des mesures en pixels pour la largeur et la hauteur de l’iframe. La taille a souvent été un défi pour s’assurer que la taille de l’iframe reflète le contenu. La meilleure pratique consiste à envisager de dimensionner l’iframe par rapport à la fenêtre d’affichage en utilisant les attributs de taille viewport vw et vh, ou les media queries pour gérer plusieurs tailles d’appareils dans le cadre de la conception réactive moderne.

Compte tenu du nombre de réglages disponibles, il peut être difficile de déterminer ce dont vous avez besoin. Les options permettent de configurer l’état du tableau de bord et les contrôles visibles dans l’iframe.

Le type d’URL à générer peut être l’une des deux options distinctes suivantes :

  1. Snapshot : URL encodage l’état actuel complet du tableau de bord, ce qui signifie que les modifications apportées au tableau de bord ne sont pas présentes dans la version intégrée. 
  2. Objet sauvegardé : Utilisez une URL référençant l’ID de l’objet enregistré du tableau de bord, ce qui signifie que toute modification apportée au tableau de bord après la génération de l’URL sera visible pour les utilisateurs de l’application JavaScript.

L’expérience de l’auteur est que ces tableaux de bord sont sujets à des changements. Par conséquent, l’option Objet sauvegardé serait la plus appropriée pour l’intégration afin de garantir que les modifications du tableau de bord effectuées après la génération de l’URL soient visibles.

Les paramètres d’inclusion désignent les contrôles supplémentaires à inclure en haut du tableau de bord intégré :

Éléments du tableau de bord Kibana
  1. Menu principal : paramètres contenant les fonctions du tableau de bord telles que l’édition et le mode plein écran, contrôlés en incluant show-top-menu=true dans l’URL de Kibana. 
  2. Requête : la barre de requête KQL vous permet de filtrer les données visibles dans le tableau de bord, représentées par le paramètre URL show-query-input=true
  3. Filtre temporel : sélecteur de dates permettant de sélectionner la plage de dates des données du tableau de bord, activé à l’aide de show-time-filter=true dans l’URL. 
  4. Barre de filtrage : masquer les paramètres pour ajouter le filtrage des données, ce qui nécessite de définir la valeur de paramètre UR hide-filter-bar sur true.

Sans utiliser l’URL publique, il nous sera demandé de nous connecter pour accéder au tableau de bord. À ce stade, l’expérience n’est pas fluide, mais le tableau de bord est accessible à ceux qui ont des identifiants.

Tableau de bord intégré sans authentification anonyme

Connexion automatique

Pour garantir que le tableau de bord s’affiche automatiquement, l’authentification doit être intégrée au tableau de bord de Kibana afin d’éviter que les utilisateurs aient besoin de saisir leurs identifiants à la fois pour l’application JavaScript et pour le tableau de bord. L’expérience est ainsi fluide. Cela peut se faire de deux manières :

  1. Activez l’authentification anonyme pour fournir un ensemble par défaut d’identifiants et de droits à toute requête entrante où aucun jeton d’authentification ne peut être extrait (disponible dans l’offre gratuite).

  2. Ajout de la prise en charge d’un fournisseur d’authentification unique SAML (SSO) pour rediriger les utilisateurs non authentifiés vers le portail SSO, et diriger les utilisateurs authentifiés directement vers le tableau de bord. Cette fonctionnalité est soumise à licence.

Nous abordons ici l’option anonyme. Tout d’abord, il faut ajouter un fournisseur d’authentification anonyme à kibana.yml :

xpack.security.authc.providers:
  anonymous.anonymous1:
    order: 0
    credentials:
      username: "my_anonymous_user"
      password: "password"

L’URL de l’iframe doit également être régénérée pour spécifier le paramètre auth_provider_hint afin de lier les identifiants configurés pour le fournisseur anonyme1 au contenu intégré :

<iframe src="https://my-deployment-9f9945.kb.eu-west-2.aws.cloud.es.io:9243/app/dashboards?auth_provider_hint=anonymous1#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?embed=true&_g=(refreshInterval%3A(pause%3A!f%2Cvalue%3A120000)%2Ctime%3A(from%3Anow-1y%2Cto%3Anow))&show-time-filter=true" height="600" width="800"></iframe>

Si auth_provider_hint=anonymous1 n’est pas inclus, il en résultera l’impossibilité de continuer vers le tableau de bord en tant qu’invité. De même, sans un rôle utilisateur correspondant enregistré dans Kibana avec le bon nom d’utilisateur et mot de passe, des erreurs d’authentification se produiront :

Identifiants non valides pour l’authentification intégrée

Pour remédier à ce problème, assurez-vous qu’un utilisateur est inscrit avec le mot de passe correct correspondant à celui de la configuration du fournisseur dans kibana.yml. Il est recommandé de limiter les privilèges de ce compte au minimum requis, étant donné que l’accès sera accordé aux utilisateurs non authentifiés.

Créer un utilisateur d’authentification dans Kibana

À ce stade, vous pourriez penser que tout est prêt. Cependant, lorsque vous vous connectez à votre tableau de bord, vous verrez se produire’ d’étranges événements d’actualisation répétés :

Bloc de politique de contenu du tableau de bord intégré

Ce problème est dû au blocage du tableau de bord Kibana par le navigateur. Les navigateurs web modernes appliquent la politique d’origine identique (Same-Origin Policy) afin de restreindre l’affichage du contenu intégré. Deux URL partagent la même origine si elles utilisent le même protocole, le même port et le même hôte. Autrement dit, tout contenu provenant d’une origine différente sera bloqué par défaut, sauf autorisation expresse de la politique de contenu.

Pour permettre au navigateur de transmettre les cookies de session au serveur Kibana de votre stack ELK avec les fonctionnalités de sécurité activées (configuration par défaut depuis Elastic v8.x), vous devez configurer l’option sameSiteCookies dans kibana.yml:

xpack.security.sameSiteCookies: "None"

À cette dernière étape, nous pouvons voir le tableau de bord Kibana intégré à l’application JavaScript :

tableau de bord Kibana intégré de base

Utilisation de commandes personnalisées

Vous avez peut-être remarqué que ce tableau de bord utilise des contrôles pour filtrer les données. Il est important de permettre aux utilisateurs d’analyser les données et de réduire leur sélection pour trouver des informations intéressantes.

Dans certaines situations, utiliser les commandes intégrées au tableau de bord peut ne pas être la bonne décision. Vous pouvez utiliser vos propres commandes personnalisées pour assurer la cohérence du design au sein d’une application existante. Vous pouvez également placer le tableau de bord à côté de sources de données et de visualisations supplémentaires que vous souhaitez filtrer pour créer une expérience cohérente.

Dans cet exemple avancé, nous montrons comment transmettre les paramètres de plage de dates d’un sélecteur de dates et d’une sélection déroulante vers le tableau de bord pour forcer une mise à jour du tableau de bord :

Tableau de bord Kibana intégré avancé

L’utilisation de contrôles personnalisés implique de comprendre la structure de l’URL du tableau de bord. Prenons l’exemple suivant :

https://elastic-deployment-9f9945.kb.eu-west-2.aws.cloud.es.io:9243/app/dashboards?auth_provider_hint=anonymous1#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?embed=true&_g=(filters:!(),refreshInterval:(pause:!f,value:0),time:(from:'${selectedStartDate}',to:'${selectedEndDate}'))&_a=(query:(language:kuery,query:'${carrierQuery}'))&hide-time-filter=true

Outre les paramètres discutés dans l’exemple de base, il faut manipuler les filtres. Comme évoqué précédemment par la communauté, il existe deux niveaux de filtres dans Kibana :

  1. L’état global, noté par le paramètre _g, désigne l’état qui se déplace entre les applications individuelles de Kibana. Les filtres épinglés, notamment les dates de début et de fin sélectionnées, en constituent un exemple emblématique.

  2. L’état est limité à des applications individuelles telles que le tableau de bord actuel. Cela est représenté par le paramètre URL _a.

Pour transmettre la plage de dates depuis un sélecteur de dates, l’iframe de l’URL doit être mise à jour avec les dates de début et de fin sélectionnées lorsqu’une nouvelle plage de dates est appliquée au contrôle. Initialement, ces valeurs sont définies sur une plage relative de l’année précédente. Prenons l’exemple d’Easepick, les nouvelles dates sont capturées lors de l’événement de sélection, enregistré à la configuration, et converties au format ISO requis avant que l’attribut src de l’iframe ne soit mis à jour avec la nouvelle URL.

let selectedStartDate = 'now-1y';
let selectedEndDate = 'now';

const picker = new easepick.create({
    element: '#datepicker',
    css: [
        'https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css'
    ],
    zIndex: 10,
    firstDay: 0,
    autoApply: false,
    format: 'MMM DD, YYYY @ HH:MM:00',
    plugins: [
        'RangePlugin',
        'TimePlugin'
    ],
    setup(picker) {
        picker.on('select', (e) => {
            const dateFormat = 'YYYY-MM-DDTHH:MM:00.000Z';
            selectedStartDate =  picker.getStartDate().format(dateFormat);
            selectedEndDate =  picker.getEndDate().format(dateFormat);
            
            dashboardUri=getDashboardUri();
            iframe.setAttribute('src', dashboardUri);
        });
     }
});

En ce qui concerne l’URL elle-même, le paramètre global de filtre _gest alors mis à jour avec la plage sélectionnée, comme on le voit dans la méthode helper getDashboardUri() :

function getDashboardUri() {
    return `https://my-deployment-9f9945.kb.eu-west-2.aws.cloud.es.io:9243/app/dashboards?auth_provider_hint=anonymous1#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?embed=true&_g=(filters:!(),refreshInterval:(pause:!f,value:0),time:(from:'${selectedStartDate}',to:'${selectedEndDate}'))&hide-time-filter=true`;
}

Pour les champs de données que vous souhaitez filtrer dans les contrôles tels que les menus déroulants, nous devons transmettre ces valeurs en utilisant l’option de requête dans le paramètre _a. Prenons le contrôle HTML select suivant comme exemple :

<div class="carrier-select-container">
  <label for="carrier-select">Carrier</label>
  <select name="carrier-select" id="carrier-select" onchange="updateWithCarrier()">
    <option value="ES-Air">ES-Air</option>
    <option value="JetBeats">JetBeats</option>
    <option value="Kibana Airlines">Kibana Airlines</option>
    <option value="Logstash Airways">Logstash Airways</option>
  </select>
</div>

Il est possible d’extraire la valeur sélectionnée lorsqu’elle est modifiée à partir de la méthode updateWithCarrier()qui est reliée à l’événement onchange. L’événement est extrait du contrôle « select » dans le gestionnaire d’événements :

function updateWithCarrier() {
    const carrierSelect = document.getElementById('carrier-select');
    selectedCarrier = carrierSelect.value || '';

    dashboardUri=getDashboardUri();
    iframe.setAttribute('src', dashboardUri);
}

Notez que nous utilisons toujours la fonction d’assistance getDashboardUri(), qui doit être mise à jour pour générer une requête KQL à transmettre à l’URL du tableau de bord via l’option de requête dans le filtre d’application :

function getDashboardUri() {
  const carrierQuery = rison.encode_object({Carrier : encodeURIComponent(selectedCarrier)});
  return `https://my-deployment-9f9945.kb.eu-west-2.aws.cloud.es.io:9243/app/dashboards?auth_provider_hint=anonymous1#/view/7adfa750-4c81-11e8-b3d7-01146121b73d?embed=true&_g=(filters:!(),refreshInterval:(pause:!f,value:0),time:(from:'${selectedStartDate}',to:'${selectedEndDate}'))&_a=(query:(language:kuery,query:'${carrierQuery}'))&hide-time-filter=true`;
}

Kibana utilise Rison et l’encodage URI, qui doit être appliqué à la requête avant son inclusion. Ceci est indiqué dans la définition de carrierQuery ci-dessus, où nous utilisons rison.js et échappons la valeur sélectionnée à l’aide de la méthode encodeURIComponent habituelle.

Une fois la connexion établie, le tableau de bord s’actualisera automatiquement à chaque nouvelle sélection. Soyez attentif aux erreurs indiquant un fichier Rison malformé,comme celle signalée sur nos forums, qui peut s’avérer difficile à débugger.

Notez que les URL sont toujours susceptibles de changer, et que vous risquez donc que votre fonctionnalité cesse de fonctionner avec les nouvelles versions de tout outil tiers que vous choisissez d’intégrer. Veillez à vérifier les changements importants pour chaque version de Kibana et à effectuer des tests de régression minutieux sur votre application.

Créer plus de tableaux de bord Kibana

Nous nous sommes plongés ici dans l’univers des tableaux de bord intégrés Kibana. Nous avons couvert un exemple simple utilisant un seul iframe HTML, ainsi qu’un exemple complexe utilisant nos propres composants JavaScript pour transmettre les paramètres au tableau de bord. Tout le code est disponible dans ce dépôt GitHub et peut être facilement adapté pour utiliser votre technologie web préférée, un framework JavaScript, ou pour une utilisation avec TypeScript.

N’hésitez pas à partager toute question ou problème rencontré lors de l’intégration de tableaux de bord sur nos forums communautaires. Nous sommes toujours heureux de vous aider. Bon usage des tableaux de bord !