Hallo, ich bin Melanie Drake, Systemingenieurin im Bereich JMP Development Operations. Unsere Gruppe stellt automatisierte Prozesse bereit, die JMP-Programme und -Installationsprogramme erstellen, automatisierte Tests durchführen und viele weitere Aufgaben zur Unterstützung der JMP-Forschung und -Entwicklung übernehmen.
Heute zeige ich Ihnen, wie wir Informationen aus Prozessprotokollen sammeln und mit JMP Probleme finden und den allgemeinen Zustand eines dieser automatisierten Prozesse überwachen. Prozesse erzeugen große Datenmengen. Diese können von Messwerten von Instrumenten, aus einer Fertigungslinie oder aus Statusprotokollen stammen. Sie lassen sich als Klartext oder in strukturierten Formaten wie XML, JSON oder CSV speichern. All diese Daten bieten zahlreiche Möglichkeiten, Probleme zu finden und zu beheben.
Diese Daten werden jedoch häufig in viele separate Dateien geschrieben, die ständig erstellt und aktualisiert werden. Dadurch ist es sehr schwierig, Fehler zu erkennen und nachzuverfolgen. Ich werde über den nächtlichen JSL-Unit-Test-Stream sprechen. Dieser Prozess erzeugt täglich zahlreiche Protokolldateien. Wir stellten fest, dass es sehr schwierig war, spezifische Fehler zu verfolgen und zu beheben. Wir hatten zwar eine riesige Datenmenge, aber deren Nutzung war äußerst umständlich.
Was ist der JSL-Unit-Test-Stream? Es handelt sich um eine Reihe von JSL-Regressionstests, die wir täglich mit dem Build von JMP ausführen. Er läuft jede Nacht auf allen aktuell in Entwicklung befindlichen JMP-Versionen und auf verschiedenen Windows- und Macintosh-Betriebssystemen. Tester nutzen diesen nächtlichen Testlauf, um Fehler in JMP zu finden, damit diese schnell von den Entwicklern behoben werden können. Falls ein bestimmter Test-Stream nicht erfolgreich ausgeführt wird, stehen die entsprechenden Testergebnisse nicht zur Verfügung.
Einige Wochen lang im Sommer entwickelten wir parallel vier Versionen von JMP. Das bedeutete, dass wir jede Nacht 18 Testläufe mit verschiedenen JMP- und Betriebssystemversionen durchführten. Ein solcher Lauf konnte 8 bis 12 Stunden dauern. Um die Windows-Testergebnisse deutlich schneller zu erhalten, teilten wir jeden Windows-Testlauf in drei parallel laufende Läufe auf. So konnte unser Testteam in Peking die Ergebnisse noch am selben Tag nutzen.
Insgesamt analysierten wir täglich 36 separate Konsolenprotokolle. Wir verbrachten viel Zeit damit, herauszufinden, wo und wann ein bestimmter Fehler auftrat. Die Lösung für die Verwaltung dieser heterogenen Datensammlungen ist die Konsolidierung aller Daten an einem zentralen Ort. Eine JMP-Datentabelle, versteht sich.
In JMP können Sie Ihre Daten visualisieren und analysieren sowie Ihre Ergebnisse über JMP Live mit anderen teilen. Wir haben unsere Protokolle in JMP-Datentabellen gesammelt. Mithilfe von Diagrammen konnten wir alle Probleme verfolgen und beheben und den Systemzustand durch die Veröffentlichung in JMP Live kontinuierlich überwachen. Da sich unsere Protokollausgabe unter Macintosh und Windows unterschied, haben wir zwei verschiedene Datentabellen erstellt. Einige Fehler traten zwar häufig auf, viele andere waren jedoch spezifisch für ein Betriebssystem.
Ich werde zunächst den allgemeinen Arbeitsablauf skizzieren und anschließend das Skript erläutern und unseren Prozess demonstrieren. Im ersten Schritt gilt es herauszufinden, wo sich Ihre Daten befinden und wie Sie diese in JMP einlesen. Protokolldateien können auf der Festplatte oder einem Server gespeichert sein. Der Zugriff ist über das Web möglich. Sie können beispielsweise über eine API oder eine Datenbankabfrage abgerufen werden. In unserem Fall greifen wir über die webbasierte Benutzeroberfläche eines Jenkins-Servers, auf dem wir alle unsere automatisierten Prozesse ausführen, auf eine Textdatei zu. Wir haben die Informationen per HTTP-Anfrage abgerufen und anschließend mit Parse JSON in ein nutzbares Format gebracht.
Im zweiten Schritt importieren Sie Ihre Daten in JMP. Beginnen Sie mit Ihren Protokolldateien und fügen Sie gegebenenfalls weitere Metadaten hinzu. Sie können Ihre gesamten Protokolle in einer einzelnen Zelle darstellen oder sie auf mehrere Zellen, Spalten oder Zeilen verteilen. Das hängt von Ihren Daten und deren Darstellung ab. In unserem Fall haben wir ein Protokoll pro Stream-Lauf. Wir platzieren einen Stream-Lauf pro Zeile und ein gesamtes Protokoll in einer einzelnen Zelle. Zusätzlich erfassen wir viele weitere Metadaten.
Im dritten Schritt analysieren Sie Ihren Text. Verwenden Sie dazu beispielsweise Parse XML oder Parse JSON, um die Rohdaten zu erhalten. Anschließend können Sie mithilfe von regulären Ausdrücken, JSL-Mustervergleich oder JSL-Zeichenfunktionen die Informationen extrahieren. Sie können Formelspalten, Indikatorspalten oder Spalten mit mehreren Antworten verwenden, um die Informationen in Formaten zu sammeln, die Sie in Ihren Diagrammen verwenden können.
In unserem Fall haben wir fast alle oben genannten Methoden angewendet. Wir nutzten Formelspalten, um die spezifischen Informationen für Metadatenspalten zu erfassen, und anschließend reguläre Ausdrücke sowie JSL-Zeichenfunktionen, um die eigentlichen Protokolle zu analysieren. Zusätzlich verwendeten wir Indikatorspalten und Spalten mit mehreren Antwortvariablen, um Informationen zu sammeln und daraus ansprechende Diagramme zu erstellen.
Im vierten Schritt werden die Daten analysiert und grafisch dargestellt. JMP bietet den Graph Builder und viele weitere Grafiktools. Damit lassen sich Fehlermuster erkennen, Korrelationen zwischen Fehlern aufdecken und deren Verlauf über die Zeit verfolgen. Wir haben den Graph Builder intensiv genutzt. Mithilfe von Heatmaps visualisierten wir die Korrelationen. Balkendiagramme und Streudiagramme dienten uns zur Verfolgung von Fehlern über die Zeit, verschiedene JMP-Versionen und Betriebssystemversionen hinweg. Zusätzlich verwendeten wir gestapelte Balkendiagramme und Heatmaps, um den Stream-Status und fehlende Durchläufe im Zeitverlauf zu visualisieren. Beispiele hierfür folgen später.
Im fünften Schritt teilen Sie Ihre Diagramme mit Ihren Kollegen. Sie können spezifische Ergebnisse bestimmten Zielgruppen präsentieren. Die Verteilung dient der besseren Übersichtlichkeit, Transparenz und Teamarbeit. Sie können auch private, persönliche oder vertrauliche Daten ausblenden. In unserem Fall veröffentlichen wir eine Teilmenge der Daten auf einem internen JMP Live-Server. Wir veröffentlichen nur die für die Diagramme benötigten Informationen. Sobald die Diagramme veröffentlicht sind, lassen sie sich in Meetings leicht besprechen, und der allgemeine Status unseres gesamten Prozesses lässt sich problemlos überwachen.
JMP 19 bietet eine neue Funktion namens Spalten-Tags. Diese haben sich für dieses Projekt als äußerst nützlich erwiesen. Wir verwenden drei Spalten-Tags: einen für die Zeitstempel bestimmter Meilensteine, einen für gefundene Fehler und einen für die Spalten in den Diagrammen. Ich zeige Ihnen später, wie das alles funktioniert.
Warum haben wir das so gemacht? Zum einen vereinfacht es unseren JSL-Code erheblich. Wir können Spalten mit einem bestimmten Tag referenzieren, anstatt jeden Spaltennamen fest zu kodieren. Das bedeutet auch, dass wir Spaltennamen nicht aktualisieren müssen, wenn wir beispielsweise gefundene Fehler beheben. Wir taggen einfach die neue Spalte, und sie wird automatisch übernommen. Dadurch wird es sehr einfach, nur die benötigten Daten auszuwählen und zu veröffentlichen.
Jetzt sehen wir uns eine Demo an, und ich zeige Ihnen unser JSL-Skript. Bevor wir richtig loslegen, führen wir einige Zeilen Code aus, um alles einzurichten. Wir binden einige weitere Skripte mit Funktionen und Variablendefinitionen ein. Wir legen den Ordnerpfad fest, in dem unsere Haupttabellen gespeichert werden. Diese enthalten alle bisher gesammelten Daten. Anschließend öffnen wir eine CSV-Datei mit Konfigurationsinformationen.
Unsere eigentliche CSV-Datei enthält mehr Spalten, die jedoch interne Informationen enthalten, die ich für diese Präsentation entfernt habe. Die grundlegenden Informationen, die wir für jedes Projekt benötigen, sind der Name des jeweiligen Teststreams und die betriebssystemspezifische Haupttabelle, die alle bisher gesammelten Daten aller Teststreams für das jeweilige Betriebssystem enthält.
Jetzt müssen wir unsere Daten einlesen. Jede Kombination aus JMP-Version und Betriebssystemversion ist ein separates Jenkins-Projekt mit eigener Seite. Jeder Testlauf ist eine Unterseite der Hauptseite. Wir sehen uns das erste Projekt an: JMP 19 auf Sonoma. Ich setze die Iteration auf 1. Anschließend lese ich den Namen jedes Jobs und die Zieltabelle ein, in die die Daten importiert werden sollen.
Wir beginnen mit einer HTTP-Anfrage, um das JSON der Hauptseite abzurufen. Dieses enthält eine Liste der URLs für jeden Stream-Lauf. Wir senden diese Anfrage an Jenkins, empfangen das JSON und parsen es in unsere Build-Liste. Da ich das hier nicht live ausführen kann, erstelle ich einfach eine Liste. Sie ist sehr lang. Diese Liste enthält die Build-ID jedes einzelnen Test-Stream-Laufs für JMP 19 auf Macintosh Sonoma.
Da wir viele dieser Daten bereits in der Haupttabelle haben, möchten wir keine Zeit mit dem Lesen und Verarbeiten bereits vorhandener Daten verschwenden. Um dies zu umgehen, öffnen wir unsere Haupttabelle. Diese enthält alle Macintosh-Ergebnisse, nicht nur die Ergebnisse dieses Projekts. Anschließend rufen wir eine Liste der Build-IDs ab, die wir bereits für dieses Projekt haben. Genauer gesagt: die IDs der Zeilen in der Haupttabelle, die JMP 19 auf Sonoma entsprechen. Wir wandeln beide Listen von Build-IDs in assoziative Arrays um, um die aktuellen IDs von unserer neuen Liste subtrahieren zu können. Die Berechnung assoziativer Arrays liefert uns eine kurze Liste der noch nicht verarbeiteten Läufe. Es ist wesentlich einfacher und schneller, drei Zeilen anstatt 395 zu verarbeiten und die meisten davon anschließend zu verwerfen.
Im zweiten Schritt werden die Daten in JMP importiert. Ist die Liste nicht leer, verwenden wir eine von uns geschriebene Funktion, die die gewünschten Daten aus jedem Stream-Lauf in der Liste extrahiert und in eine Datentabelle einfügt. Diese Funktion nutzt erneut Parse JSON, um die benötigten Daten zu extrahieren und sie in Spalten und Zeilen für jeden Test-Stream in die neue Datentabelle einzufügen.
Ich öffne eine Beispieltabelle, damit Sie sehen können, wie sie aussieht. Sie sehen, dass wir bereits einige Metadaten haben. Wir erhalten die Dauer jedes einzelnen Streams, seinen Namen, seine ID-Nummer, den Status des Builds (Erfolg oder Misserfolg) und den Zeitstempel des Startzeitpunkts. Am wichtigsten ist die URL zur Textprotokolldatei, aus der wir alle Informationen beziehen. Als Nächstes fügen wir dieser Tabelle eine Spalte hinzu, die den Inhalt der Protokolldatei enthält. Wir verwenden die Funktion „Textdatei laden“, um die Datei von der URL zu lesen und in die Tabelle einzufügen.
Im dritten Schritt werden die Metadaten und die Konsolenausgaben dieser Zeilen analysiert. Wir fügen einige neue abgeleitete Spalten hinzu. Beispielsweise wandeln wir die Dauer in eine Laufzeit in Minuten um, was leichter verständlich ist. Außerdem fügen wir eine Datumsspalte hinzu, die nur das Datum ohne Zeitstempel enthält. Dies vereinfacht einige unserer Diagramme erheblich. Schließlich fügen wir noch eine Spalte für den Wochentag hinzu. Auch diese verwenden wir in einigen unserer Diagramme.
Als Nächstes bereiten wir die Protokollanalyse vor. Wir fügen Spalten hinzu, um die Startzeiten bestimmter Meilensteine zu erfassen. Wir definieren unterschiedliche Startspalten für Windows und Macintosh und kennzeichnen diese Spalten beim Hinzufügen als Startzeiten. Die grünen Markierungen zeigen an, dass alle diese Spalten mit demselben Tag versehen sind. Sie sehen sie hier unten.
Die nächste Spalte, die wir hinzufügen, zeigt den zuletzt gestarteten Vorgang an. Falls der Teststream aufgrund eines Fehlers abbricht, können wir den letzten erfolgreich abgeschlossenen Meilenstein ermitteln. Dies ist die letzte Spalte mit dem Tag „Starts“ in dieser Zeile und einem Zeitstempel. Wenn der Prozess die Verbindung zur VM oder zum Server verliert, werden JavaScript-Fehler im Protokoll protokolliert. Diese werden selbstverständlich erfasst.
Abschließend fügen wir eine Spalte mit mehreren Antwortmöglichkeiten hinzu, um Fehlerinformationen zu erfassen. Darauf komme ich gleich zurück. Im nächsten Schritt beginnen wir mit dem Parsen des Protokolls. Wir verwenden eine Schleife, um die gesamte Konsole für jede Zeile zu analysieren, die gewünschten Informationen zu extrahieren und sie den neuen Spalten dieser Zeile hinzuzufügen. Das Konsolenprotokoll befindet sich in einer einzelnen Zelle pro Zeile. Wir wandeln es in eine Liste von Zeichenketten um, wobei jede Zeile des Protokolls eine Zeichenkette darstellt.
Anschließend durchlaufen wir die Liste einmal und vergleichen jede Zeile mit den gesuchten Elementen wie Startzeiten, Fehlern usw. Dies ist wesentlich effizienter, als das Protokoll für jedes zu extrahierende Element einzeln zu analysieren. Zuerst suchen wir nach einem JavaScript-Fehler. Falls wir einen finden, erfassen wir ihn in der Datentabelle und fahren dann mit dem Konsolenprotokoll der nächsten Zeile fort. Sobald der Prozess auf diese Weise beendet wurde, sind keine weiteren Informationen mehr verfügbar.
In unseren Jenkins-Projekten suchen wir nach bestimmten Fehlern. Diese Suchvorgänge werden am Ende des Logs ausgegeben und als Textfinder bezeichnet. Sobald wir auf den ersten Textfinder stoßen, sammeln wir die Einträge bis zum Ende des Logs. Dort beenden wir die Schleife, da dies das Ende des Logs ist. Diesen Eintrag übergeben wir an eine Funktion, die ich Ihnen gleich zeigen werde.
Abgesehen von diesen beiden Punkten suchen wir nach den Meilensteinen und fügen die entsprechenden Datums- und Zeitstempel in die Tabelle ein. Am Ende der Schleife erfassen wir schließlich die gefundenen Textindikatoren. Jeder gefundene Textindikator wird der Tabelle als Indikatorspalte hinzugefügt: Null, wenn kein Fehler gefunden wurde, und Eins, wenn ein Fehler gefunden wurde.
Dadurch fehlen Einträge, wo wir nicht nach diesem Fehler gesucht haben. Zum Beispiel, wenn es sich um einen neuen Fehler handelt, nach dem wir zuvor noch nicht gesucht haben. Jede Spalte der Textsuche ist als „Textsuche“ gekennzeichnet. Sobald diese Indikatorspalten ausgefüllt sind, ermitteln wir die Namen aller Spalten, die als „Textsuche“ gekennzeichnet sind und in der jeweiligen Zeile eine Eins enthalten, und fügen sie der Spalte für Mehrfachantworten hinzu, die später für die grafische Darstellung verwendet wird.
Schließlich fügen wir den letzten Eintrag in die Tabelle für diese Zeile ein. Schleifen für die nachfolgenden Zeilen vergleichen die gefundenen Textmarkierungen mit den bereits vorhandenen Spalten und fügen nur die noch fehlenden hinzu. Hier zeige ich ein Beispiel, wie die Tabelle nach diesem Vorgang aussieht. Alle ursprünglich erfassten Metadaten sind weiterhin vorhanden. Wir haben unseren Protokolltext und alle abgeleiteten Daten. Nun enthalten alle Startspalten Datums- und Zeitstempel, sodass wir genau wissen, wann jeder Eintrag begonnen hat.
Der letzte gestartete Vorgang hat das letzte erfolgreich verarbeitete Element erfasst. Es traten keine Java-Verbindungsfehler auf; falls doch, wären diese jedoch protokolliert worden.
Schließlich haben wir alle Spalten unserer Textsuche hinzugefügt. Sie sind rot markiert. Alle Durchläufe waren erfolgreich, aber ich habe einige positive Ergebnisse als Fehler markiert, um Ihnen zu zeigen, wie sie in der Spalte für Mehrfachantworten aussehen. Sie sehen, dass dieser Durchlauf zwei Fehler aufwies und durch Semikolon getrennt ist. Dieser hier hatte nur einen Fehler.
Der letzte Schritt besteht darin, all diese neuen Informationen in unsere Hauptdatentabelle einzufügen. Es handelt sich lediglich um eine einfache Verkettung. Wir wollten also unsere drei neuen Zeilen in die Tabelle einfügen. Der letzte Schritt ist das Speichern dieser Tabelle. Wir komprimieren die Tabelle beim Speichern, da diese Protokolldateien manchmal Hunderte oder sogar Tausende von Zeilen umfassen. Bei 1.333 Zeilen handelt es sich um eine große Datenmenge, und diese Tabellen können sehr groß werden. Das ist der Kern des Prozesses und hat am längsten in der Entwicklung gedauert. Die Entscheidung, welche Informationen wir genau benötigten, war ein iterativer Prozess.
Der letzte Schritt besteht darin, Ihre Daten zu analysieren und grafisch darzustellen. Wir haben zahlreiche Diagramme gespeichert, die wir zur Fehlerdiagnose entwickelt haben. Ich zeige Ihnen einige davon. Dieses Diagramm zeigt die Laufzeit eines Datenstroms nach Datum und Betriebssystem.
Kurzzeitige Streams sind in der Regel solche, die schnell ablaufen und einfach beendet werden. Wir erhalten nur sehr selten Ergebnisse von ihnen. Ein sehr langer Stream hat sich wahrscheinlich irgendwo aufgehängt und wird vermutlich irgendwann abgebrochen. Das sind alles Probleme, die wir genauer untersuchen müssen. Dieser Streamlauf ist wirklich konstant. Das ist immer erfreulich.
Ein weiteres nützliches Diagramm zeigt Korrelationen zwischen JavaScript-Fehlern und dem letzten erfolgreich gestarteten Prozess. Das kann bei der Fehlerdiagnose sehr hilfreich sein. Mein absolutes Lieblingsdiagramm ist wahrscheinlich diese Heatmap, die den Build-Status bei fehlenden Durchläufen darstellt.
Sie sehen sofort, dass wir diese Testläufe fünf Tage die Woche durchführen. An den Wochenenden finden keine statt. In dieser Tabelle fehlen viele Läufe, da wir gar keine Testläufe durchgeführt haben. Diese Läufe wurden zwar ausgeführt, wiesen aber Fehler auf. Alle grünen Läufe waren einwandfrei. Hier sind die drei Zeilen, die wir dieser Tabelle gerade hinzugefügt haben.
Im fünften Schritt teilen Sie Ihre Diagramme. Da diese bereits in JMP Live veröffentlicht sind, müssen wir lediglich die Tabelle und die Diagramme in JMP Live aktualisieren. Wir stellen eine Verbindung zu unserer JMP Live-Instanz her. Diese wird unter „Datei“ > „Veröffentlichen“ > „Verbindungen verwalten“ definiert. Anschließend öffnen wir die Datentabelle und führen das Teilskript aus.
Sie fragen sich vielleicht, warum wir uns überhaupt die Mühe machen. Unsere ursprüngliche Haupttabelle hat 39 Spalten. Unsere Teiltabelle hingegen nur neun, und diese neun Spalten enthalten nicht die umfangreichen Konsolenprotokolle. Dadurch sind diese Teiltabellen deutlich kleiner, was die Leistung von JMP Live verbessert. Außerdem können wir private oder geschützte Daten ganz einfach nicht veröffentlichen. Wir veröffentlichen nur die Daten, die für die verwendeten Diagramme unbedingt erforderlich sind.
Wir senden diese Teiltabelle und weitere Informationen wie die JMP Live-Verbindung an eine von uns geschriebene Funktion, die alle Diagramme in der Tabelle durchläuft. Anschließend aktualisiert sie jede Tabelle. Danach aktualisiert sie die Teiltabelle in jedem Diagramm in JMP Live und fährt dann mit der nächsten Tabelle fort, in diesem Fall Windows. Damit ist der Vorgang abgeschlossen. Bei jeder Ausführung erhalten wir neue Ergebnisse.
Hier sehen Sie einige Beispiele der Diagramme, die wir auf JMP Live veröffentlicht haben. Über unser Dashboard bieten diese und weitere Diagramme einen schnellen Überblick, sodass wir allgemeine Trends und Problembereiche schnell erkennen können. Da es sich um JMP Live handelt, können wir jedes Diagramm einzeln betrachten und detaillierte Informationen abrufen. Alle unsere Diagramme werden mit lokalen Datenfiltern veröffentlicht. Dadurch können wir uns nur bestimmte Teststream-Läufe innerhalb eines bestimmten Zeitraums ansehen. Beispielsweise nur JMP 20-Teststream-Läufe der letzten Wochen.
Was haben wir letztendlich erreicht? Wir haben unsere Erkenntnisse automatisiert. Wir müssen nur noch die Diagramme betrachten, um zu sehen, was vor sich geht. Sie aktualisieren sich automatisch. Wir müssen uns nicht mehr um einzelne Fehler kümmern. Die Daten werden automatisch veröffentlicht. Da alles vordefiniert ist, funktioniert der Prozess einfach jeden Tag. Während wir unsere Daten analysierten, entschieden wir, welche Informationen wir erfassen wollten. Wir passten die Ausgaben in unseren Konsolenprotokollen an und fügten beispielsweise die Startzeiten für Meilensteine hinzu. Wir wählten die Diagramme aus, die die zu lösenden Probleme am besten darstellen.
Als wir das Skript fertiggestellt und zum Laufen gebracht hatten, war unser großer Erfolg perfekt. Wir hatten alle unsere bisherigen Probleme gelöst, und unsere Diagramme sind nun nicht mehr so aufregend, da die Testabläufe in der Regel erfolgreich verlaufen. Vielen Dank.