Exécution de Real User Monitoring (RUM) avec Elastic APM | Elastic Blog
Technique

À la découverte de RUM

Dire qu'à une lettre près, vous pourriez être en train de lire la recette d’un bon cocktail à base de rhum... Cet article de blog prendrait alors une orientation (et une saveur !) totalement différente. Mais non ! Aujourd’hui, nous allons nous intéresser à la fonction RUM d’Elastic. Et croyez-moi, elle est tout aussi délicieuse ! Voici maintenant l’heure d’y goûter. Avant de nous lancer, je voudrais vous prévenir que les informations que je vais aborder aujourd’hui sont assez vastes, et que cela prendra donc du temps.

RUM, qu’est-ce que c’est ?

La fonction Real User Monitoring d’Elastic capture les interactions des utilisateurs avec un navigateur web et fournit une vue détaillée de l’“expérience utilisateur réelle” de vos applications web sur le plan des performances. L’agent RUM d’Elastic est un agent JavaScript, ce qui signifie qu’il prend en charge n’importe quelle application basée sur JavaScript. RUM peut fournir des informations précieuses sur vos applications. Voici quelques avantages de RUM parmi les plus courants :

  • Les données que RUM fournit sur les performances peuvent vous aider à identifier les goulets d’étranglement, ainsi que l’incidence des problèmes de performances des sites sur l’expérience de vos visiteurs.
  • Les informations des agents utilisateur capturées par RUM vous permettent d’identifier les navigateurs, les appareils et les plateformes les plus utilisées par vos clients. Ainsi, vous pouvez apporter des améliorations optimales à votre application.
  • Combinées aux informations sur l’emplacement, les données sur les performances des utilisateurs individuels de RUM vous aident à comprendre les performances régionales de votre site web au niveau mondial.
  • RUM fournit des informations et des mesures pour les accords de niveau de service (SLA) de votre application.

Prise en main de RUM avec Elastic APM

Dans ce blog, nous allons étudier l’intégralité du processus pour instrumenter une application web simple, constituée d’un frontend React et d’un back-end Spring Boot, étape par étape. En ce qui concerne l’utilisation de l’agent RUM, c’est un vrai jeu d’enfants, vous allez voir ! En bonus, vous découvrirez comment Elastic APM met en conjonction les informations de frontend et de back-end sur les performances avec une vue de trace holistique et distribuée. Si vous souhaitez en savoir plus sur le traçage, consultez mon article de blog précédent : Traçage distribué, OpenTracing et Elastic APM.

Pour utiliser la fonction RUM d’Elastic APM, vous devez disposer de la Suite Elastic avec un serveur APM. Vous pouvez, bien sûr, télécharger et installer la dernière version de la Suite Elastic avec un serveur APM en local sur votre ordinateur. Toutefois, pour vous faciliter la tâche, je vous conseille de créer un compte d’essai Elastic Cloud pour que votre cluster soit prêt en quelques minutes. APM est activé pour le modèle optimisé d’E/S par défaut. À partir de maintenant, je partirai du principe que vous disposez d’un cluster fonctionnel.

Exemple d’application

L’application que nous allons instrumenter est une application de base de données automobile simple, constituée d’un frontend React et d’un back-end Spring Boot, qui fournit un accès API à une base de données automobile in-memory. L’application reste simple à dessein. L’idée, c’est de vous montrer les étapes détaillées de l’instrumentation en partant de zéro, pour que vous puissiez instrumenter vos propres applications en reproduisant cette procédure.

Une application simple avec un frontend React et un back-end Spring

Créez un répertoire appelé CarApp à l’emplacement que vous souhaitez sur votre ordinateur. Ensuite, clonez l’application frontend et back-end dans ce répertoire.

git clone https://github.com/adamquan/carfront.git
git clone https://github.com/adamquan/cardatabase.git

Comme vous pouvez le constater, l’application est extrêmement simple. Il y a seulement quelques composants dans le frontend React et quelques classes dans le back-end Spring Boot. Concevez et exécutez l’application en suivant les instructions dans GitHub concernant le frontend et le back-end. Vous devriez avoir une présentation similaire à celle ci-dessous. Vous pouvez parcourir les véhicules, les filtrer, ou encore les créer, les lire, les mettre à jour et les supprimer.

Interface utilisateur React simple

Maintenant que l’application est fonctionnelle et qu’elle est en cours d’exécution, nous pouvons étudier la procédure d’instrumentation à l’aide de l’agent RUM.

Instrumentation enrichie prête à l’emploi avec RUM

Tout d’abord, installons et configurons l’agent RUM. Pour cela, deux options s’offrent à nous :

  1. Pour les applications côté serveur, vous pouvez installer l’agent RUM en tant que dépendance et l’initialiser.

    npm install @elastic/apm-rum --save
        
  2. Ou vous pouvez installer l’agent RUM avec une configuration HTML.

    <script src="https://unpkg.com/@elastic/apm-rum@4.0.1/dist/bundles/elastic-apm-rum.umd.min.js">
    </script>
    <script>
      elasticApm.init({
        serviceName: 'carfront',
        serverUrl: 'http://localhost:8200',
        serviceVersion: '0.90'
      })
    </script>
        

Étant donné que notre frontend est une application React, nous opterons pour la première approche. Dans le même répertoire que votre fichier index.js, il y a le fichier rum.js qui contient le code suivant :

import { init as initApm } from '@elastic/apm-rum'
var apm = initApm({
 // Définissez le nom de service requis (caractères autorisés : a-z, A-Z, 0-9, -, _ et espace)
 serviceName: 'carfront',
 // Définissez la version de votre application
 // Utilisez le serveur APM pour déterminer la source map appropriée
 serviceVersion: '0.90',
 // Définissez l’URL personnalisée du serveur APM (par défaut : http://localhost:8200)
 serverUrl: 'https://aba7c3d90b0b4820b05b0a9df44c096d.apm.us-central1.gcp.cloud.es.io:443',
 // distributedTracingOrigins: ['http://localhost:8080'],
})
export default apm;

Et voilà ! Nous venons d’initialiser l’agent RUM ! Voici une explication rapide de certaines configurations :

  1. serviceName : Le nom de service doit être défini. C’est lui qui représente votre application dans l’interface utilisateur APM. Donnez-lui un nom représentatif.
  2. serviceVersion : Il s’agit de la version de votre application. Ce numéro de version est également utilisé par le serveur APM pour trouver la source map appropriée. Nous aborderons les source maps plus en détail ultérieurement.
  3. serverURL : Il s’agit de l’URL du serveur APM. Remarque : L’URL du serveur APM est normalement accessible depuis Internet en mode public, car votre agent RUM y rapporte les données à partir des navigateurs des utilisateurs finaux sur Internet.
  4. Nous verrons distributedTracingOrigins plus tard.

Les utilisateurs qui ont l’habitude des agents de back-end Elastic APM peuvent se demander pourquoi le token APM ne sert pas ici. La raison est toute simple : en réalité, l’agent RUM n’utilise pas de token APM secret. Le token sert uniquement pour les agents back-end. Étant donné que le code frontend est public, le token secret n’apportera pas de sécurité supplémentaire.

Nous allons charger ce fichier JavaScript lorsque l’application se chargera et nous le mettrons aux emplacements auxquels nous souhaitons réaliser une instrumentation personnalisée. Pour l’instant, étudions ce que ce fichier nous offre de base, sans instrumentation personnalisée. Pour cela, incluons tout simplement rum.js dans index.js. Le fichier index.js importe rum.js et définit un nom de chargement de page. Si aucun nom de chargement de page n’était défini, le chargement serait affiché comme “Unknown” (Inconnu) dans l’interface utilisateur APM, ce qui ne serait pas très intuitif. Voici à quoi ressemble index.js.

import apm from './rum'
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
apm.setInitialPageLoadName("Car List")
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();

Générez du trafic vers votre application. Connectez-vous à Kibana et cliquez sur l’interface utilisateur APM. Vous devriez voir un service répertorié avec le nom “carfront”. Si vous cliquez sur le nom de ce service, vous serez redirigé vers la page de la transaction. Vous devriez voir la liste de chargements de page par défaut, “page-load”, ainsi qu’un chargement de page “car list”. Si ce n’est pas le cas, vérifiez que l’intervalle de temps défini dans le sélecteur est “Last 15 minutes” (Les dernières 15 minutes). Cliquez sur le lien “car list”. Vous devriez obtenir une vue en cascade d’interactions de navigateur, comme dans l’exemple ci-dessous :

Exemple de transaction dans Elastic APM

Vous avez vu toutes ces informations que l’agent RUM propose par défaut ? Impressionnant, n’est-ce pas ? Prêtez une attention particulière aux marqueurs tels que timeToFirstByte, domInteractive, domComplete et firstContentfulPaint. Survolez les points noirs pour voir les noms. Ils vous donneront des informations précieuses concernant la récupération de contenu et le rendu de navigateur de ces contenus. Gardez également un œil attentif sur l’ensemble des données de performances relatives au chargement des ressources que fournit le navigateur. Lorsque vous initialisez votre agent RUM sans apporter la moindre instrumentation personnalisée, vous obtenez déjà tous ces indicateurs sur les performances ! En cas de problème de performances, ces indicateurs vous permettent de déterminer facilement si le problème vient de la lenteur des services back-end, de la lenteur du réseau ou tout simplement de la lenteur du navigateur client. Le moins qu’on puisse dire, c’est que ça en jette !

Pour ceux qui auraient besoin d’un petit rappel, voici une explication rapide sur la façon dont les performances web sont mesurées à l’aide d’indicateurs. Gardez toutefois à l’esprit que, pour les frameworks modernes d’applications web comme React, ces indicateurs peuvent représenter uniquement la partie “statique” de la page web, en raison de la nature asynchrone de React. Par exemple, les contenus dynamiques peuvent être toujours en cours de chargement après domInteractive, comme vous le verrez plus tard.

  • timeToFirstByte est la durée pendant laquelle un navigateur patiente en attendant de recevoir la première information du serveur web qu’il a demandée. Cette durée dépend à la fois de la vitesse de traitement côté réseau et côté serveur.
  • domInteractive représente le moment juste avant que l’agent utilisateur définisse l’état de préparation du document en cours sur “interactive” (interactif), ce qui signifie que le navigateur a fini d’analyser l’ensemble du contenu HTML et que la construction DOM est terminée.
  • domComplete correspond au moment juste avant que l’agent utilisateur définisse l’état de préparation du document sur “complete” (terminé), ce qui signifie que la page et toutes ses sous-ressources (p. ex. images) ont terminé de se télécharger et sont prêtes. L’indication de téléchargement s’arrête.
  • firstContentfulPaint est le moment où le navigateur effectue le rendu du premier bit de contenu à partir de DOM. Il s’agit d’un jalon important pour les utilisateurs car il indique que la page est en cours de chargement.

Instrumentation personnalisée flexible

L’agent RUM fournit une instrumentation détaillée prête à l’emploi pour que votre navigateur puisse interagir, comme vous venez de le voir. Si besoin, vous pouvez également mettre en place une instrumentation personnalisée. Par exemple, étant donné que l’application React est une application à page unique et que la suppression d’un véhicule n’entraînera pas de chargement de page, par défaut, RUM ne capturera pas les données de ladite suppression de véhicule. Dans un cas comme celui-ci, nous pouvons recourir aux transactions personnalisées.

Avec notre version actuelle (APM Real User Monitoring JavaScript Agent 4.x), les utilisateurs doivent créer manuellement les transactions pour les appels AJAX et les appels d’application à page unique (SPA) qui ne déclenchent aucun chargement de page. Dans certains frameworks, comme JSF, vous n’exercez qu’un contrôle très restreint sur JavaScript. De ce fait, la création manuelle de transactions concernant les clics de bouton qui entraînent le lancement de requêtes AJAX n’est pas viable. Même si le développeur a un contrôle direct sur les requêtes AJAX, l’instrumentation d’une application volumineuse impliquerait un gros travail. Nous prévoyons d’apporter des améliorations à l’agent RUM pour qu’il crée une transaction pour ces requêtes, dans le cas où il n’y en aurait aucune qui soit active. Ainsi, l’instrumentation automatique prendrait une plus grande partie de l’application en charge et les développeurs n’auraient pas à programmer l’ajout d’une logique de traçage dans leurs applications.

Le bouton "New Car" (Nouveau véhicule) de notre application frontend permet d’ajouter un nouveau véhicule à la base de données. Nous allons instrumenter le code de sorte à capturer les performances de l’ajout d’un nouveau véhicule. Ouvrez le fichier Carlist.js dans le répertoire des composants. Vous verrez le code suivant :

// Ajouter un nouveau véhicule
addCar(car) {
    // Créer une transaction personnalisée
    var transaction = apm.startTransaction("Add Car", "Car");
    apm.addTags(car);
    fetch(SERVER_URL + 'api/cars',
        {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(car)
        })
        .then(res => this.fetchCars())
        .catch(err => console.error(err))
}
fetchCars = () => {
    fetch(SERVER_URL + 'api/cars')
        .then((response) => response.json())
        .then((responseData) => {
            this.setState({
                cars: responseData._embedded.cars,
            });
        })
        .catch(err => console.error(err));
        // Terminer la transaction en cours à la fin du rappel de la réponse
        var transaction = apm.getCurrentTransaction()
        if (transaction) transaction.end()
}

Le code a créé une transaction appelée “Add Car” (Ajouter un véhicule) de type “Car” (Véhicule). Il a ensuite balisé la transaction à l’aide du véhicule afin de fournir des informations contextuelles. La transaction s’est terminée à la fin du rappel de la réponse.

Ajoutez un nouveau véhicule à partir de l’interface utilisateur web de l’application. Cliquez sur l’interface utilisateur APM dans Kibana. Une transaction “Add Car” (Ajouter un véhicule) devrait être répertoriée. Sélectionnez “Car” (Véhicule) dans la liste déroulante “Filter by type” (Filtrer par type). Par défaut, ce sont les transactions de chargement de page (“page-load”) qui sont affichées.

Filtrage par type dans Elastic APM

Cliquez sur le lien de transaction “Add Car” (Ajouter un véhicule). Des informations sur les performances de la transaction personnalisée “Add Car” (Ajouter un véhicule) devraient vous être présentées.

Exemple de transaction dans Elastic APM filtré par type 'car' (Véhicule)

Cliquez sur l’onglet “Tags” (Balises). C’est là que vous verrez les balises ajoutées. Les balises et les logs ajoutent des informations contextuelles précieuses à vos traces APM.

Exploration par balise de type dans Elastic APM

Voilà ! Pour réaliser une instrumentation personnalisée, c’est aussi simple que cela. Et en plus, vous optimisez votre puissance ! Pour en savoir plus, consultez la documentation sur l’API.

Visualisations APM personnalisées puissantes avec Kibana

Elastic APM propose une interface utilisateur APM optimisée et des tableaux de bord APM intégrés pour visualiser toutes les données APM capturées directement par les agents. Vous pouvez également créer vos propres visualisations personnalisées. Par exemple, l’IP utilisateur et les données capturées par l’agent RUM sont des informations extrêmement riches concernant vos clients. Avec ces informations, il est possible de créer une visualisation comme celle-ci pour montrer d’où vient le trafic web sur une carte et quels sont les systèmes d'exploitation et les navigateurs utilisés par vos clients. Vous pouvez vous servir de pipelines de nœud d’ingestion pour enrichir et transformer vos données APM. Toutes ces informations vous permettent d’optimiser votre application de façon plus intelligente.

Visualisation des données Elastic APM dans un tableau de bord Kibana

Obtenez une vue d’ensemble grâce au traçage distribué

En bonus, nous allons également instrumenter notre application Spring Boot back-end. Ainsi, vous aurez un aperçu complet de la transaction globale à partir du navigateur web jusqu’à la base de données back-end, le tout, en une seule vue. Pour y parvenir, nous nous servirons du traçage distribué APM.

Configuration du traçage distribué dans les agents RUM

Le traçage distribué est activé par défaut dans l’agent RUM. Toutefois, il inclut uniquement les requêtes effectuées d’une même origine. Pour prendre en charge les requêtes entre origines multiples, vous devez définir l’option de configuration distributedTracingOrigins. Vous devrez également configurer la politique CORS dans l’application back-end, ce que nous verrons dans la section suivante.

Pour notre application, le frontend est démarré à partir de http://localhost:3000. Pour inclure les requêtes effectuées vers http://localhost:8080, nous devons ajouter la configuration distributedTracingOrigins à notre application React. Pour cela, nous nous servons du fichier rum.js. Le code s’y trouve déjà. Supprimez tout simplement la ligne.

var apm = initApm({
  ...
  distributedTracingOrigins: ['http://localhost:8080']
})

En procédant ainsi, l’agent sera invité à ajouter l’entête HTTP (elastic-apm-traceparent) du traçage distribué aux requêtes envoyées vers http://localhost:8080.

Pour utiliser directement l’instrumentation par défaut côté serveur, vous devez télécharger l’agent Java et vous en servir pour démarrer l’application. Voici comment j’ai configuré mon projet Eclipse pour qu’il exécute l’application Spring Boot back-end. Pour réaliser votre configuration, vous devrez vous servir de votre propre URL APM et de votre propre token APM.

-javaagent:/Users/aquan/Downloads/elastic-apm-agent-1.4.0.jar 
-Delastic.apm.service_name=cardatabase 
-Delastic.apm.application_packages=com.packt.cardatabase
-Delastic.apm.server_urls=https://aba7c3d90b0b4820b05b0a9df44c096d.apm.us-central1.gcp.cloud.es.io:443 
-Delastic.apm.secret_token=jeUWQhFtU9e5Jv836F

Configuration d’Eclipse pour envoyer des données back-end à Elastic APM

Maintenant, actualisez votre liste de véhicules à partir du navigateur pour générer une autre requête. Accédez à l’interface utilisateur APM de Kibana et vérifiez le dernier chargement de page “car list”. Vous devriez voir un écran similaire à la capture ci-dessous s’afficher. Comme vous pouvez le voir, vos données de performances côté client et vos données de performances côté serveur, y compris l’accès JDBC, apparaissent toutes de façon bien organisée dans une trace distribuée unique ! Notez que différentes couleurs ont été employées pour différentes parties de la trace distribuée. Gardez à l’esprit qu’il s’agit là du traçage par défaut et qu’il n’y a besoin d’aucune instrumentation personnalisée côté serveur, à part démarrer votre application avec l’agent. Bénéficiez de la puissance d’Elastic APM et du traçage distribué !

Traçage distribué (back-end et frontend) dans Elastic APM

Aux lecteurs qui ont observé attentivement la visualisation chronologique ci-dessus : vous vous demandez peut-être pourquoi la transaction de chargement de page “Car List” prend fin à 193 ms, ce qui correspond à domInteractive, alors que les données sont toujours servies à partir du back-end. C’est une excellente question ! Cela s’explique par le fait que les appels d’extraction sont asynchrones par défaut. Le navigateur “pense” qu’il a fini d’analyser l’ensemble du contenu HTML et que la construction DOM est achevée à 193 ms du fait qu’il a chargé l’ensemble du contenu HTML “statique” servi à partir du serveur web. Par ailleurs, React continue à charger des données depuis le serveur back-end de façon asynchrone.

Partage des ressources entre origines multiples (CORS)

L’agent RUM n’est qu’un élément parmi d’autres constituant la trace distribuée. Pour pouvoir utiliser le traçage distribué, nous devons aussi configurer correctement les autres composants, notamment, le partage des ressources entre origines multiples, le “célèbre” CORS ! Pourquoi ? Car en général, les services de frontend et de back-end sont déployés séparément. Avec la politique concernant l’origine unique (same-origin), les requêtes frontend issues d’une origine différente de celle du back-end échoueront si le CORS n’est pas bien configuré. En quelques mots, le CORS est une méthode qui permet, côté serveur, de vérifier si les requêtes venant d’une autre origine sont autorisées. Pour en savoir plus sur les requêtes aux origines multiples et pour comprendre la nécessité de ce processus, consultez la page MDN sur le partage des ressources entre origines multiples.

Concrètement, qu’est-ce que cela signifie pour nous ? Deux choses, que voici :

  1. Nous devons configurer l’option de configuration distributedTracingOrigins, comme nous l’avons fait.
  2. Avec cette configuration, l’agent RUM envoie une requête HTTP OPTIONS avant la requête HTTP réelle afin de vérifier que tous les en-têtes et toutes les méthodes HTTP sont bien pris en charge et que l’origine est autorisée. Plus précisément, http://localhost:8080 recevra une requête OPTIONS avec les en-têtes suivants :

    Access-Control-Request-Headers: elastic-apm-traceparent
    Access-Control-Request-Method: [request-method]
    Origin: [request-origin]
        
    Et le serveur APM devrait y répondre avec les en-têtes ci-dessous et un état de code 200 :

    Access-Control-Allow-Headers: elastic-apm-traceparent
    Access-Control-Allow-Methods: [allowed-methods]
    Access-Control-Allow-Origin: [request-origin]
        

C’est exactement l’effet qu’a la classe MyCorsConfiguration dans notre application Spring Boot. Différentes méthodes sont possibles pour configurer l’application Spring Boot afin qu’elle agisse ainsi. Ici, nous utilisons une approche basée sur des filtres. Résultat : notre application Spring Boot côté serveur autorise les requêtes venant de n’importe quelle origine, quels que soient les en-têtes HTTP et les méthodes HTTP utilisés. Vous ne voudrez peut-être pas cependant avoir une telle flexibilité dans vos applications de production.

@Configuration
public class MyCorsConfiguration {
    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<CorsFilter>(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    }
}

Concluons cet article avec une autre fonctionnalité très puissante de l’agent RUM : les source maps. Les source maps facilitent la correction des erreurs dans votre application en vous indiquant précisément où celles-ci se sont produites dans votre code source d’origine, et non pas dans un code minifié énigmatique.

Un débogage en toute simplicité avec les source maps

Il est très courant de minifier les bundles JavaScript dans le cadre du déploiement de production pour des raisons de performances. Néanmoins, lorsqu’il s’agit de déboguer un code minifié, les choses deviennent tout de suite extrêmement complexes. Vous trouverez ci-dessous un message d’erreur capturé par l’agent RUM pour une build de production. Comme vous pouvez le voir, la trace de stack d’exception n’a pas beaucoup de sens, et pour cause, elle est en code minifié. Dans toutes les lignes d’erreur, des fichiers javascript tels que 2.5e9f7401.chunk.js sont indiqués. Ils renvoient tous vers la même ligne, “line 1”, du fait de la minification. Si vous pouviez voir votre code source tel que vous l’avez écrit, ne serait-ce pas merveilleux ?

Le code minifié dans Elastic APM peut être résolu avec une source map

Les source maps sont là pour vous aider. Vous pouvez générer des source maps pour vos bundles et les charger dans le serveur APM. Le serveur APM pourra convertir les erreurs du code minifié dans votre code source d’origine, ce qui vous permettra d’avoir une bien meilleure compréhension du message d’erreur.

Voyons comment nous pouvons procéder pour notre application React frontend. Nous allons créer une build de production pour notre application et charger les source maps. Pour cela, vous pouvez utiliser la commande suivante :

npm run build

Le message suivant devrait s’afficher à la fin de la build :

The build folder is ready to be deployed.
You may serve it with a static server:
  serve -s build

Pour en savoir plus sur la build de production, rendez-vous sur : https://facebook.github.io/create-react-app/docs/production-build

Si vous ne l’avez pas déjà fait, installez serve.

npm install -g serve

Configurez votre application React en tant que serveur en mode de production à l’aide de cette commande :

serve -s build

Lorsque votre application React est en mode de production, l’arrière-plan de l’icône de React Developer Tools pour Chrome est foncé. Lorsqu’elle est en mode de développement, l’arrière-plan de l’icône est rouge. Faites bien attention à exécuter la build de production !

Maintenant, si vous cliquez sur le bouton d’erreur pour générer une erreur et que vous vérifiez celle-ci dans l’interface utilisateur APM de Kibana, vous obtiendrez le stack d’erreur minifié, comme sur la capture ci-dessus.

Chargeons nos source maps... et laissons la magie opérer ! Les source maps sont générées dans le répertoire $APP-PATH/carfront/build/static/js. Accédez à ce répertoire. Vous y trouverez trois fichiers de source maps pour les trois fichiers JavaScript. Exécutez la commande suivante pour les charger dans votre serveur APM. Modifiez l’URL, le nom des fichiers et les autres paramètres afin qu’ils correspondent à la version, à la build et à l’environnement de votre application. Pensez également à utiliser le token d’autorisation de votre serveur APM.

curl https://aba7c3d90b0b4820b05b0a9df44c096d.apm.us-central1.gcp.cloud.es.io:443/v1/rum/sourcemaps -X POST \
  -F sourcemap="@./main.b81677b7.chunk.js.map" \
  -F service_version="0.90" \
  -F bundle_filepath="http://localhost:5000/static/js/main.b81677b7.chunk.js" \
  -F service_name="carfront" \
  -H "Authorization: Bearer jeUWQhFtU9e5Jv836F"
curl https://aba7c3d90b0b4820b05b0a9df44c096d.apm.us-central1.gcp.cloud.es.io:443/v1/rum/sourcemaps -X POST \
  -F sourcemap="@./runtime~main.fdfcfda2.js.map" \
  -F service_version="0.90" \
  -F bundle_filepath="http://localhost:5000/static/js/runtime~main.fdfcfda2.js" \
  -F service_name="carfront" \
  -H "Authorization: Bearer jeUWQhFtU9e5Jv836F"
curl https://aba7c3d90b0b4820b05b0a9df44c096d.apm.us-central1.gcp.cloud.es.io:443/v1/rum/sourcemaps -X POST \
  -F sourcemap="@./2.5e9f7401.chunk.js.map" \
  -F service_version="0.90" \
  -F bundle_filepath="http://localhost:5000/static/js/2.5e9f7401.chunk.js" \
  -F service_name="carfront" \
  -H "Authorization: Bearer jeUWQhFtU9e5Jv836F"

Attention : la version de service est une chaîne, qui est identique à la version de service que vous avez configurée dans votre application React. Dans le cas présent, nous effectuons un chargement avec service_version="0.90". La version de service définie dans l’application est bien "0.90". Mais si vous chargez les source maps avec service_version="0.9" (sans le 0 final), ça ne fonctionnera pas !

Une fois que la service map est chargée dans le serveur APM, vous pouvez trouver l’ensemble des source maps avec cette requête dans Dev Tools (attention : votre version peut être différente) :

GET apm-6.6.1-sourcemap/_search

Générez une autre erreur et consultez à nouveau la trace de stack dans l’onglet des erreurs de l’interface utilisateur APM. Tadam ! La trace de stack affiche votre magnifique code source d’origine, comme dans la capture suivante ! Désormais, déboguer et identifier les problèmes est un véritable jeu d’enfants.

Une fois la source map mise en œuvre, le code réel est désormais visible dans Elastic APM

Résumé

Comme nous l’avons vu, il est extrêmement simple de réaliser l’instrumentation de vos applications avec Elastic RUM. Et très puissant ! Combiné à d’autres agents APM pour les services back-end, RUM vous donne une vue holistique des performances d’une application du point de vue de l’utilisateur final grâce au traçage distribué.

Comme déjà mentionné, pour vous lancer sur Elastic APM, vous pouvez télécharger le serveur Elastic APM pour l’exécuter en local, ou créer un compte d’essai Elastic Cloud pour avoir un cluster prêt en quelques minutes.

Comme toujours, rendez-vous sur leforum Elastic APM si vous souhaitez ouvrir un fil de discussion ou si vous avez des questions. Profitez bien !