Real User Monitoring (RUM) mit Elastic APM | Elastic Blog
Engineering

Ein Schluck Elastic-RUM (Real User Monitoring)

Es tut mir leid, wenn ich Sie auf die Idee gebracht habe, einen wundervollen Cocktail mit Rum zu trinken, und Ihnen dann aufgegangen ist, dass ich mit RUM kein Getränk meine. Aber glauben Sie mir, Elastic RUM ist genau so toll! Lassen Sie uns einen Schluck nehmen! Ich möchte Sie darauf hinweisen, dass Sie einige Zeit brauchen werden, um alle Details durchzugehen, die ich in diesen Blogeintrag besprechen werde!

Was ist RUM?

Elastic Real User Monitoring erfasst die Benutzerinteraktionen mit dem Webbrowser und liefert eine ausführliche Ansicht des „tatsächlichen Benutzererlebnisses“ Ihrer Webanwendungen aus einer Leistungsperspektive. Der Elastic RUM Agent ist ein JavaScript-Agent und unterstützt daher beliebige JavaScript-basierte Anwendungen. RUM kann wertvolle Einblicke in Ihre Anwendungen liefern. Beispiele für wichtige Vorteile von RUM:

  • Mit den RUM-Leistungsdaten können Sie Engpässe identifizieren und herausfinden, wie sich Leistungsprobleme in Ihrer Site auf das Erlebnis Ihrer Besucher auswirken.
  • Mit den von RUM erfassten Informationen aus dem Benutzer-Agent können Sie die meistverwendeten Browser, Geräte und Plattformen Ihrer Kunden identifizieren, um informierte Entscheidungen für Ihre Anwendungen zu treffen.
  • Zusammen mit Standortinformationen helfen Ihnen die individuellen Benutzerleistungsdaten aus RUM dabei, die regionale Performance Ihrer Website weltweit zu verstehen.
  • RUM liefert Einblicke und Messungen für die Servicelevel-Vereinbarungen (SLA) Ihrer Anwendungen.

Erste Schritte mit RUM und Elastic APM

In diesem Blogeintrag besprechen wir den kompletten Prozess der Instrumentierung einer einfachen Webanwendung mit einem React-Frontend und einem Spring Boot-Backend, Schritt für Schritt. Sie werden sehen, wie einfach es ist, den RUM Agent zu verwenden. Außerdem erfahren Sie, wie Elastic APM die Leistungsinformationen aus Frontend und Backend in einer ganzheitlichen, verteilten Trace-Ansicht kombiniert. Lesen Sie meinen vorherigen Blogeintrag für eine Übersicht über Elastic APM und verteiltes Tracing, falls Sie weitere Details erfahren möchten.

Um Elastic APM Real User Monitoring verwenden zu können, müssen Sie den Elastic Stack mit APM Server installiert haben. Selbstverständlich können Sie den neuesten Elastic Stack mit APM Server herunterladen und lokal auf Ihrem Computer installieren. Zur Vereinfachung empfehle ich Ihnen jedoch, ein Elastic Cloud-Testkonto zu erstellen, mit dem Sie innerhalb von Minuten einen einsatzbereiten Cluster erhalten. APM ist für die standardmäßige E/A-optimierte Vorlage aktiviert. Ab diesem Punkt gehe ich davon aus, dass Sie einen einsatzbereiten Cluster haben.

Beispielanwendung

Die Beispielanwendung, die wir instrumentieren werden, ist eine einfache Autodatenbank mit einem React-Frontend und einem Spring Boot-Backend, das API-Zugriff auf eine speicherinterne Autodatenbank gewährt. Die Anwendung ist bewusst einfach gehalten. Ich möchte Ihnen die Instrumentierungsschritte von Anfang an ausführlich zeigen, damit Sie Ihre eigenen Anwendungen mit denselben Schritten instrumentieren können.

Eine einfache Beispielanwendung mit React-Frontend und Spring-Backend

Erstellen Sie ein Verzeichnis mit dem Namen CarApp irgendwo auf Ihrem Laptop. Klonen Sie anschließend die Frontend- und Backend-Anwendung in dieses Verzeichnis.

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

Wie Sie sehen, ist die Anwendung extrem einfach. Das React-Frontend enthält nur eine handvoll Komponenten, und das Spring Boot-Backend einige wenige Klassen. Folgen Sie den Anweisungen in GitHub für Frontend und Backend, um die Anwendung zu erstellen und auszuführen. Dabei sollte in etwa Folgendes angezeigt werden. Sie können Autos durchsuchen, filtern und CRUD-Vorgänge (create, read, update, delete) ausführen.

Die einfache React-Benutzeroberfläche

Unsere Anwendung ist also einsatzbereit, und wir können die Instrumentierung mit dem RUM Agent starten.

Umfassende vorkonfigurierte Instrumentierung mit RUM

Zunächst werden wir den RUM Agent installieren und konfigurieren. Dazu haben Sie zwei Möglichkeiten:

  1. Für serverseitige Anwendungen können Sie den RUM Agent als Abhängigkeit installieren und initialisieren.

    npm install @elastic/apm-rum --save
        
  2. Installieren Sie den RUM Agent mit HTML-Konfiguration.

    <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>
        

Da unser Frontend eine React-Anwendung ist, werden wir den ersten Ansatz verwenden. Im gleichen Verzeichnis wie die Datei index.js befindet sich auch die Datei rum.js, die den folgenden Code enthält:

import { init as initApm } from '@elastic/apm-rum'
var apm = initApm({
 // Erforderlichen Dienstnamen festlegen (zulässige Zeichen: a-z, A-Z, 0-9, -, _ und Leerzeichen)
 serviceName: 'carfront',
 // Version Ihrer Anwendung festlegen
 // Wird im APM Server verwendet, um die richtige Sourcemap zu finden
 serviceVersion: '0.90',
 // Benutzerdefinierte URL für den APM Server festlegen (Standard: http://localhost:8200)
 serverUrl: 'https://aba7c3d90b0b4820b05b0a9df44c096d.apm.us-central1.gcp.cloud.es.io:443',
 // distributedTracingOrigins: ['http://localhost:8080'],
})
export default apm;

Und damit haben Sie Ihren RUM Agent initialisiert! Ich werde einige der Konfigurationen hier kurz erklären:

  1. Dienstname: Der Dienstname muss festgelegt werden. Er dient als Bezeichner für Ihre Anwendung in der APM-Benutzeroberfläche. Geben Sie dem Dienst einen aussagekräftigen Namen.
  2. Dienstversion: Dies ist die Version Ihrer Anwendung. Der APM Server verwendet diese Versionsnummer, um die richtige Sourcemap zu finden. Wir werden die Sourcemap später noch ausführlich behandeln.
  3. Server-URL: Die URL des APM Servers. Die URL des APM Servers ist normalerweise aus dem öffentlichen Internet erreichbar, da Ihr RUM Agent Daten von den Browsern der Endbenutzer im Internet an diese URL meldet.
  4. Wir werden distributedTracingOrigins später besprechen.

Falls Sie mit Elastic APM Backend-Agents vertraut sind, haben Sie sich vielleicht gefragt, warum wir das APM-Token nicht übergeben haben. Dies liegt daran, dass der RUM Agent kein geheimes APM-Token verwendet. Das Token wird nur für Backend-Agents verwendet. Da der Frontend-Code öffentlich ist, liefert das geheime Token keine zusätzliche Sicherheit.

Wir laden diese JavaScript-Datei, wenn die Anwendung geladen wird, und binden Sie an den Orten ein, an denen wir unsere benutzerdefinierte Instrumentierung hinzufügen möchten. Wir sehen uns fürs Erste die vorkonfigurierten Funktionen an, ohne benutzerdefinierte Instrumentierung. Dazu reicht es aus, rum.js in index.js einzubinden. Die Datei index.js importiert rum.js und legt einen Ladenamen für die Seite fest. Ohne Ladenamen für die Seite wird die geladene Seite in der APM-Benutzeroberfläche als „Unknown“ angezeigt, was nicht besonders hilfreich ist. index.js enthält den folgenden Code.

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();

Generieren Sie etwas Traffic zu Ihrer Anwendung. Melden Sie sich bei Kibana an und klicken Sie auf die APM-Benutzeroberfläche. Dort sollte ein Dienst mit dem Namen „carfront“ aufgelistet werden. Klicken Sie auf den Dienstnamen, um zur Transaktionsseite zu gelangen. Dort sollte die Standardliste „page-load“ und eine geladene Seite „car list“ angezeigt werden. Falls nicht, legen Sie das Zeitintervall im Auswahlfeld auf die letzten 15 Minuten fest. Klicken Sie auf den Link „car list“, um die folgende Wasserfallansicht der Browserinteraktionen anzuzeigen:

Eine Beispieltransaktion in Elastic APM

Ist es nicht unglaublich, wie viele Informationen der RUM Agent standardmäßig erfasst? Achten Sie besonders auf die Markierungen wie etwa timeToFirstByte, domInteractive, domComplete und firstContentfulPaint. Fahren Sie mit der Maus über die schwarzen Punkte, um die Namen anzuzeigen. Hier finden Sie umfangreiche Details über abgerufene Inhalte und die Darstellung dieser Inhalte im Browser. Beachten Sie außerdem all die Leistungsdaten über geladene Ressourcen im Browser. Sie erhalten all diese detaillierten Leistungsmetriken vordefiniert und ohne benutzerdefinierte Instrumentierung, einfach indem Sie Ihren RUM Agent initialisieren! Bei einem Leistungsproblem können Sie mit diesen Metriken mühelos herausfinden, ob das Problem durch langsame Backend-Dienste, ein langsames Netzwerk oder einfach nur einen langsamen Clientbrowser verursacht wird. Das ist wirklich beeindruckend!

Falls Sie eine Auffrischung brauchen, werden wir kurz über Leistungsmetriken im Web sprechen. Für moderne Anwendungs-Frameworks wie React kann es aufgrund der asynchronen Natur der Frameworks vorkommen, dass sich die Metriken nur auf den statischen Teil der Webseite beziehen. Wie Sie später sehen werden, können dynamische Inhalte auch nach domInteractive weiterhin geladen werden.

  • timeToFirstByte gibt an, wie lange ein Browser nach der Anforderung gewartet hat, bis er das erste Byte an Informationen vom Webserver erhalten hat. Dieser Wert ist eine Kombination aus Netzwerk- und serverseitiger Verarbeitungsgeschwindigkeit.
  • domInteractive ist die Zeit direkt bevor der User Agent die Bereitschaft des aktuellen Dokuments auf „interactive“ festlegt, was bedeutet, dass der Browser das HTML analysiert und die DOM-Erstellung abgeschlossen hat.
  • domComplete ist die Zeit direkt bevor der User Agent die Bereitschaft des aktuellen Dokuments auf „complete“ festlegt, was bedeutet, dass die Seite und alle untergeordneten Ressourcen wie Bilder heruntergeladen wurden und bereit sind. Die Ladeanzeige hat aufgehört, sich zu drehen.
  • firstContentfulPaint ist der Moment, in dem der Browser die ersten Inhalte aus dem DOM darstellt. Dieser Meilenstein ist wichtig für die Benutzer, da sie sehen, dass sie Seite tatsächlich geladen wird.

Flexible benutzerdefinierte Instrumentierung

Wie Sie bereits gesehen haben, enthält der RUM Agent eine vorkonfigurierte ausführliche Instrumentierung für Ihre Browserinteraktionen. Bei Bedarf können Sie auch Ihre eigene Instrumentierung hinzufügen. Die React-Anwendung besteht nur aus einer Seite, und beim Löschen eines Autos wird kein „page load“ ausgelöst. Daher kann RUM die Leistung beim Löschen von Autos standardmäßig nicht erfassen. Zu diesem Zweck können wir benutzerdefinierte Transaktionen verwenden.

Mit unserer aktuellen Version (APM Real User Monitoring JavaScript Agent 4.x) müssen die Benutzer manuell Transaktionen für AJAX-Aufrufe und Aufrufe an Websites mit nur einer einzigen Seite (Single-Page-Application,SPA) erstellen, wenn kein „page load“ ausgelöst wird. In manchen Frameworks wie etwa JSF haben Sie nur sehr wenig Kontrolle über das JavaScript. Daher ist es nicht machbar, manuell Transaktionen für Schaltflächen-Klicks zu erstellen, bei denen AJAX-Anfragen ausgelöst werden. Selbst wenn ein Entwickler direkte Kontrolle über die AJAX-Anfragen hat, wäre es ein riesiger Aufwand, eine große Anwendung zu instrumentieren. Wir haben vor, den RUM Agent so zu erweitern, dass für diese Anforderungen automatisch eine Transaktion erstellt wird, wenn noch keine aktive Transaktion existiert. Auf diese Weise könnte die automatische Instrumentierung einen viel größeren Teil der Anwendung abdecken, ohne dass die Entwickler Tracing-Logik im Code ihrer Anwendungen hinzufügen müssen.

Mit der Schaltfläche „New Car“ in unserer Frontend-Anwendung können Sie ein neues Auto zur Datenbank hinzufügen. Wir werden den Code instrumentieren, um die Leistung beim Hinzufügen neuer Autos zu messen. Öffnen Sie die Datei Carlist.js im components-Verzeichnis. Dort sehen Sie den folgenden Code:

// Neues Auto hinzufügen
addCar(car) {
    // Benutzerdefinierte Transaktion erstellen
    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));
        // Aktuelle Transaktion am Ende des Antwort-Callbacks beenden
        var transaction = apm.getCurrentTransaction()
        if (transaction) transaction.end()
}

Der Code erstellt eine neue Transaktion mit dem Namen „Add Car“ vom Typ „Car“. Anschließend wird die Transaktion mit dem Auto markiert, um Kontextinformationen zu liefern. Die Transaktion endet am Ende des Antwort-Callbacks.

Fügen Sie ein neues Auto in der Web-Benutzeroberfläche der Anwendung hinzu. Klicken Sie auf die APM-Benutzeroberfläche in Kibana. Dort sollte jetzt eine „Add car“-Transaktion aufgelistet werden. Achten Sie darauf, „Car“ im Dropdownfeld „Filter by Type“ auszuwählen. Standardmäßig werden hier die „page-load“-Transaktionen angezeigt.

Filtern nach Typ in Elastic APM

Klicken Sie auf den Link der Transaktion „Add Car“. Daraufhin sollten Leistungsdaten für die benutzerdefinierte Transaktion „Add Car“ angezeigt werden:

Transaktionsbeispiel in Elastic APM gefiltert nach dem Typ „Car“

Klicken Sie auf die Registerkarte „Tags“. Dort sehen Sie die Tags, die wir hinzugefügt haben. Tags und Logs ergänzen Ihre APM-Traces um wertvolle Kontextinformationen.

Erkundung nach Type-Tag in Elastic APM

Und damit haben Sie auch schon eine benutzerdefinierte Instrumentierung eingerichtet, kinderleicht und doch leistungsstark! Weitere Details finden Sie in der API-Dokumentation.

Leistungsstarke benutzerdefinierte API-Visualisierungen mit Kibana

Elastic APM bietet eine kuratierte APM-Benutzeroberfläche und integrierte APM-Dashboards, mit denen Sie sämtliche von den Agents erfassten APM-Daten vorkonfiguriert visualisieren können. Außerdem können Sie Ihre eigenen Visualisierungen erstellen. Die vom RUM Agent erfassten Daten zu IP und User Agent der Benutzer enthalten beispielsweise umfassende Daten über Ihre Kunden. Mit den Informationen zu IP und User Agent können Sie die folgende Visualisierung erstellen, um auf einer Karte anzuzeigen, woher der Web-Traffic kommt und welche Betriebssysteme und Browser Ihre Kunden verwenden. Sie können Ingest-Knoten-Pipelines verwenden, um Ihre APM-Daten anzureichern und zu transformieren. Mit all diesen Informationen können Sie Ihre Anwendung noch intelligenter optimieren.

Visualisierung von Elastic APM-Daten in einem Kibana-Dashboard

Behalten Sie den Überblick mit verteiltem Tracing

Als Bonus werden wir außerdem unser Spring Boot-Backend instrumentieren, damit Sie einen kompletten Überblick über die gesamte Transaktion vom Webbrowser bis hin zur Backend-Datenbank in einer einzigen Ansicht erhalten. Dazu verwenden wir die verteilte Tracingfunktion von Elastic APM.

Konfigurieren von verteiltem Tracing in RUM Agents

Verteiltes Tracing ist im RUM Agent standardmäßig aktiviert. Dabei werden jedoch nur Anfragen an denselben Ursprung erfasst. Um Anfragen zwischen verschiedenen Ursprüngen zu erfassen, müssen Sie die Konfigurationsoption distributedTracingOrigins festlegen. Außerdem müssen Sie die CORS-Richtlinie in der Backend-Anwendung festlegen, wie Sie im nächsten Abschnitt sehen werden.

Das Frontend für unsere Anwendung wird unter http://localhost:3000 gehostet. Um Anfragen an „http://localhost:8080“ einzubinden, müssen wir die Konfiguration distributedTracingOrigins zu unserer React-Anwendung hinzufügen. Dazu verwenden wir die Datei rum.js. Der Code ist bereits vorhanden. Wir entfernen lediglich den Kommentar in der entsprechenden Zeile.

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

Damit weisen wir den Agent an, den HTTP-Header für verteiltes Tracing (elastic-apm-traceparent) zu den Anfragen an „http://localhost:8080“ hinzuzufügen.

Um die Standardinstrumentierung auf der Serverseite vorkonfiguriert verwenden zu können, müssen Sie den Java Agent herunterladen und verwenden, um Ihre Anwendung zu starten. Hier sehen Sie die Konfiguration meines Eclipse-Projekts für die Ausführung des Spring Boot-Backends. In dieser Konfiguration müssen Sie Ihre eigene APM-URL und Ihr APM-Token eingeben.

-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

Konfigurieren von Eclipse für den Versand von Backenddaten an Elastic APM

Aktualisieren Sie Ihre Autoliste im Browser, um eine neue Anfrage zu generieren. Öffnen Sie die APM-Benutzeroberfläche in Kibana und überprüfen Sie das letzte „Page Load“ für „car list“. Dort sollte in etwa Folgendes angezeigt werden. Wie Sie sehen, werden sämtliche clientseitigen Leistungsdaten aus dem Browser und serverseitige Leistungsdaten, inklusive JDBC-Zugriff, zusammen in einem einzigen verteilten Trace angezeigt! Beachten Sie die verschiedenen Farben für unterschiedliche Teile des verteilten Trace. Dies ist die Standardkonfiguration, die Sie ohne jegliche benutzerdefinierte Instrumentierung auf der Serverseite erhalten, einfach indem Sie Ihre Anwendung mit dem Agent starten. Spüren Sie die Leistung von Elastic APM und verteiltem Tracing!

Verteiltes Tracing (Backend und Frontend) in Elastic APM

Wenn Sie sich die Zeitleistenvisualisierung oben genau ansehen, fragen Sie sich vielleicht, warum die „Page Load“-Transaktion für „Car List“ bei 193 ms endet, der domInteractive-Zeit, während das Backend immer noch Daten überträgt. Das ist eine gute Frage! Dies liegt daran, dass die fetch-Aufrufe standardmäßig asynchron sind. Der Browser glaubt, dass er bei 193 ms das komplette HTML analysiert und die DOM-Erstellung abgeschlossen hat, weil er sämtliche statischen HTML-Inhalte vom Server geladen hat. React lädt dagegen weiterhin asynchron Daten vom Backend-Server.

Ressourcenfreigabe zwischen verschiedenen Ursprüngen (Cross-Origin Resource Sharing, CORS)

Der RUM Agent ist nur ein Puzzleteil in einem verteilten Trace. Um verteiltes Tracing nutzen zu können, müssen wir noch andere Komponenten korrekt konfigurieren. Normalerweise müssen Sie beispielsweise die Ressourcenfreigabe zwischen verschiedenen Ursprüngen konfigurieren, das berüchtigte CORS! Dies liegt daran, dass Frontend- und Backend-Dienste normalerweise separat bereitgestellt werden. Mit der Richtlinie same-origin treten bei Ihren Frontend-Anfragen von einem anderen Ursprung an das Backend Fehler auf, wenn CORS nicht korrekt konfiguriert ist. Mit CORS kann der Server überprüfen, ob Anfragen von einem anderen Ursprung zulässig sind. Weitere Informationen zu Anfragen zwischen verschiedenen Ursprüngen und zur Notwendigkeit dieses Verfahrens finden Sie auf der MDN-Seite unter Cross-Origin Resource Sharing.

Was bedeutet das für uns? Es bedeutet zwei Dinge:

  1. Wir müssen die Konfigurationsoption distributedTracingOrigins festlegen, was wir bereits erledigt haben.
  2. Mit dieser Konfiguration sendet der RUM Agent zusätzlich eine HTTP OPTIONS-Anfrage vor der eigentlichen HTTP-Anfrage, um sicherzustellen, dass alle Header und HTTP-Methoden unterstützt werden und dass der Ursprung erlaubt ist. „http://localhost:8080“ erhält eine OPTIONS-Anfrage mit den folgenden Headern:

    Access-Control-Request-Headers: elastic-apm-traceparent
    Access-Control-Request-Method: [request-method]
    Origin: [request-origin]
        
    Der APM Server sollte darauf mit den folgenden Headern und dem Antwortcode 200 antworten:

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

Dafür ist die Klasse MyCorsConfiguration in unserer Spring Boot-Anwendung zuständig. Wir können diese Konfiguration in Spring Boot auf verschieden Arten vornehmen, aber in diesem Fall verwenden wir einen filterbasierten Ansatz. Wir konfigurieren die Spring Boot-Anwendung auf der Serverseite so, dass Anfragen von jedem Ursprung mit beliebigen HTTP-Headern und allen HTTP-Methoden zulässig sind. In Ihren Produktionsanwendungen sollten Sie diese Option etwas weniger offen konfigurieren.

@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;
    }
}

Zum Abschluss dieses Blogeintrags möchte ich Ihnen ein besonders leistungsstarkes Feature des RUM Agent vorstellen: Sourcemaps. Sourcemaps erleichtern Ihnen die Fehlerbehebung in Ihren Anwendungen, indem sie Ihnen genau verraten, wo der Fehler in Ihrem ursprünglichen Quellcode aufgetreten ist, anstatt im kryptischen miniaturisierten Code.

Mühelose Fehlerbehebung mit Sourcemaps

JavaScript-Pakete werden aus Leistungsgründen in Produktionsbereitstellungen oft miniaturisiert. Es ist jedoch von Natur aus schwierig, Fehler in miniaturisiertem Code zu finden. Der folgende Screenshot zeigt eine vom RUM Agent in einem Produktions-Build erfasste Fehlermeldung. Wie Sie sehen, ergibt der Ausnahme-Stack nicht viel Sinn, da es sich um miniaturisierten Code handelt. Die Fehlerzeilen verweisen auf JavaScript-Dateien mit Namen wie 2.5e9f7401.chunk.js und zeigen aufgrund der Art der Miniaturisierung immer auf Zeile 1. Wäre es nicht toll, wenn Sie hier exakt den von Ihnen entwickelten Quellcode sehen könnten?

Miniaturisierter Code in Elastic APM kann mit einer Sourcemap aufgelöst werden

Sourcemaps sind die Lösung. Sie können Sourcemaps für Ihre Pakete generieren und auf den APM Server hochladen. Anschließend kann der APM Server die Fehler im miniaturisierten Code in Ihren ursprünglichen Quellcode übersetzen und Ihnen eine viel leichter verständliche Fehlermeldung liefern.

Ich werde Ihnen jetzt zeigen, wie wir dies in unserem React-Frontend erreichen. Wir werden einen Produktions-Build für unsere Anwendung generieren und die Sourcemaps hochladen. Führen Sie den folgenden Befehl aus, um einen Produktions-Build zu erstellen:

npm run build

Am Ende des Builds sollte die folgende Nachricht angezeigt werden:

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

Weitere Informationen zum Produktions-Build finden Sie hier: https://facebook.github.io/create-react-app/docs/production-build

Installieren Sie serve, falls noch nicht geschehen:

npm install -g serve

Versetzen Sie Ihre React-Anwendung mit dem folgenden Befehl in den Produktionsmodus:

serve -s build

Wenn sich Ihre React-Anwendung im Produktionsmodus befindet, wird das Symbol der React Developer Tools für Chrome mit dunklem Hintergrund angezeigt. Wenn sich Ihre React-Anwendung im Entwicklungsmodus befindet, wird das Symbol mit rotem Hintergrund angezeigt. Vergewissern Sie sich, dass Sie den Produktions-Build ausführen.

Wenn Sie jetzt auf die error-Schaltfläche klicken, einen Fehler generieren und in der APM-Benutzeroberfläche in Kibana öffnen, sehen Sie den miniaturisierten Fehler-Stack wie im vorherigen Screenshot.

Wir werden jetzt unsere Sourcemaps laden und die Magie wirken lassen! Die Sourcemaps werden im Verzeichnis $APP-PATH/carfront/build/static/js generiert. In diesem Verzeichnis sehen Sie drei Sourcemap-Dateien für die drei JavaScript-Dateien. Führen Sie den folgenden Befehl aus, um sie auf Ihren APM Server hochzuladen. Passen Sie dabei URL, Dateiname und sonstige Parameter an die Version, den Build und die Umgebung Ihrer Anwendung an. Achten Sie außerdem darauf, das Autorisierungstoken von Ihrem APM Server zu verwenden.

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"

Stellen Sie sicher, dass die Dienstversion eine Zeichenfolge ist und exakt mit der Dienstversion übereinstimmt, die Sie in Ihrer React-Anwendung konfiguriert haben. In diesem Fall verwenden wir service_version="0.90" für unseren Upload, und in der Anwendung ist die Dienstversion "0.90" festgelegt. Wenn Sie die Sourcemaps mit service_version="0.9" (ohne 0 am Ende) laden, funktioniert die Lösung nicht!

Sobald die Dienstkarte im APM Server geladen wurde, finden Sie alle Sourcemaps mit der folgenden Anfrage in den Entwickler-Tools (möglicherweise verwenden Sie eine andere Version):

GET apm-6.6.1-sourcemap/_search

Generieren Sie einen weiteren Fehler und öffnen Sie den Stacktrace auf der Registerkarte „error“ in der APM-Benutzeroberfläche. Dort sollte ein Stacktrace wie im folgenden Screenshot angezeigt werden, der Ihren ursprünglichen Quellcode wunderschön wiedergibt! Damit lassen sich Probleme viel leichter beheben und identifizieren!

Nach der Implementierung einer Sourcemap wird tatsächlicher Code in Elastic APM angezeigt

Zusammenfassung

Hoffentlich konnte ich Ihnen mit diesem Blogeintrag zeigen, wie einfach und doch leistungsstark die Instrumentierung Ihrer Anwendungen mit Elastic RUM ist. Zusammen mit anderen APM Agents für Backend-Dienste erhalten Sie mit RUM eine ganzheitliche Ansicht der Anwendungsleistung aus der Perspektive der Endbenutzer mit verteiltem Tracing.

Für Ihre ersten Schritte mit Elastic APM können Sie wie immer den Elastic APM Server herunterladen und lokal ausführen, oder Sie können ein Elastic Cloud-Testkonto erstellen, mit dem Sie innerhalb von Minuten einen einsatzbereiten Cluster erhalten.

Im Elastic APM Forum können Sie wie immer jederzeit eine Diskussion eröffnen oder Fragen stellen. Viel Spaß mit RUM!