Ciao, sono Melanie Drake, Systems Engineer presso JMP Development Operations. Il nostro gruppo fornisce processi automatizzati che creano eseguibili e programmi di installazione JMP, eseguono test automatizzati e molte altre attività a supporto della ricerca e sviluppo di JMP.
Oggi parlerò di come raccogliamo informazioni dai log di processo e utilizziamo JMP per individuare problemi e monitorare lo stato generale di uno di questi processi automatizzati. I processi generano molti dati. Possono provenire da letture di strumenti, misurazioni di una linea di produzione o log di stato. Possono essere salvati in formato testo normale o talvolta in formato testo strutturato, come XML, JSON o CSV. Tutti questi dati creano numerose opportunità per individuare e risolvere i problemi.
Tuttavia, questi dati vengono spesso scritti in molti file separati che vengono creati e aggiornati costantemente. Può essere molto difficile individuare e tracciare gli errori. Parlerò del flusso di test unitari JSL notturno. Questo processo produce molti file di log ogni giorno. Abbiamo scoperto che era molto difficile tracciare e risolvere errori specifici. Eravamo sommersi dai dati, ma utilizzarli era molto difficile.
Cos'è il flusso di test unitari JSL? Si tratta di una suite di test di regressione JSL che eseguiamo utilizzando la build giornaliera di JMP. Viene eseguito ogni notte su tutte le versioni di JMP attualmente in fase di sviluppo e su diverse versioni del sistema operativo Windows e Macintosh. I tester utilizzano questa esecuzione notturna per individuare errori in JMP, in modo che gli sviluppatori possano risolverli rapidamente. Se un determinato flusso di test non viene eseguito correttamente, non vengono forniti i relativi risultati.
Per alcune settimane d'estate, abbiamo avuto quattro versioni di JMP in fase di sviluppo contemporaneamente. Questo significava che eseguivamo 18 flussi di test ogni notte su una combinazione di versioni di JMP e del sistema operativo. L'esecuzione dell'intero flusso può richiedere dalle 8 alle 12 ore. Abbiamo suddiviso ogni flusso di test Windows in tre flussi che vengono eseguiti contemporaneamente per consentire a tutti i risultati dei test Windows di concludersi molto più rapidamente. Questo consente al nostro team di test a Pechino di utilizzare i risultati dei test nello stesso giorno.
Mettendo insieme il tutto, esaminavamo 36 log di console separati ogni giorno. Potevamo, e lo facevamo, dedicare molto tempo a cercare di scoprire dove e quando si verificava un errore specifico. La soluzione per gestire raccolte di dati disparate è consolidarle tutte in un unico posto. Una tabella dati JMP, ovviamente.
Una volta in JMP, è possibile visualizzare e analizzare i dati e condividere i risultati con altri tramite JMP Live. Nel nostro caso, abbiamo raccolto i log in tabelle dati JMP. Abbiamo utilizzato grafici per tracciare e risolvere tutti i problemi e possiamo monitorarne costantemente lo stato pubblicandoli su JMP Live. Abbiamo finito per avere due tabelle dati diverse per i nostri dati perché l'output del log era diverso tra Macintosh e Windows. Sebbene alcuni errori fossero comuni, molti altri erano specifici di un sistema operativo.
Per prima cosa, descriverò il flusso di lavoro generale, poi illustrerò lo script e illustrerò il nostro processo. Il primo passo è scoprire dove si trovano i dati e come verranno letti in JMP. I file di log possono essere salvati su disco o sul server. Sono accessibili dal web. È possibile recuperarli tramite un'API o persino una query di database. Nel nostro caso, accediamo al file di testo tramite l'interfaccia utente basata sul web da un server Jenkins, dove eseguiamo tutti i nostri processi automatizzati. Abbiamo utilizzato una richiesta HTTP per ottenere le informazioni, quindi abbiamo utilizzato Parse JSON per ottenere le informazioni in un formato utilizzabile.
Il secondo passaggio consiste nell'importare i dati in JMP. Si può iniziare con i file di log, a cui si potrebbero aggiungere altri metadati. È possibile inserire l'intero log in una singola cella, oppure distribuirlo su più celle, colonne o persino righe. Tutto dipende dal tipo di dati e da come si desidera visualizzarli. Nel nostro caso, abbiamo un log per ogni esecuzione di flusso. Inseriamo un'esecuzione di flusso per riga e un log intero in una singola cella. Inoltre, raccogliamo anche molti altri metadati.
Il terzo passaggio consiste nell'analizzare il testo. Potresti usare Parse XML o Parse JSON per ottenere le informazioni grezze. Una volta ottenute, potresti usare Regex, JSL pattern matching o funzioni JSL per analizzare le informazioni. Potresti usare colonne formula, colonne indicatore o colonne di risposta multiple per raccogliere queste informazioni in formati utilizzabili nei tuoi grafici.
Nel nostro caso, abbiamo utilizzato quasi tutti gli strumenti sopra menzionati. Abbiamo utilizzato colonne formula per raccogliere informazioni specifiche per le colonne di metadati, e poi abbiamo utilizzato funzioni Regex e JSL per analizzare i log effettivi. Abbiamo anche utilizzato colonne di indicatori e colonne a risposta multipla per raccogliere informazioni in modo da poterne ricavare grafici accattivanti.
Il quarto passaggio consiste nell'esplorare e rappresentare graficamente i dati. JMP fornisce Graph Builder e molte altre piattaforme grafiche. È possibile utilizzarle per individuare pattern negli errori, scoprire dove i dati sono correlati tra loro e monitorarli nel tempo. Nel nostro caso, abbiamo utilizzato ampiamente Graph Builder. Abbiamo creato mappe termiche per visualizzare le correlazioni. Abbiamo utilizzato grafici a barre e diagrammi a dispersione per monitorare gli errori nel tempo, nelle diverse versioni di JMP e del sistema operativo. Abbiamo anche utilizzato grafici a barre impilati e mappe termiche per visualizzare lo stato del flusso e le esecuzioni mancanti nel tempo. Mostrerò alcuni esempi più avanti.
Infine, nel quinto passaggio, vorrai condividere i tuoi grafici con i tuoi colleghi. Puoi presentare risultati specifici a un pubblico specifico. Puoi condividere per visibilità, trasparenza e lavoro di squadra. Puoi anche nascondere dati privati, personali o riservati. Nel nostro caso, pubblichiamo un sottoinsieme dei dati su un server JMP Live interno. Pubblichiamo solo ciò che è necessario per i grafici. Una volta pubblicati, i grafici sono molto facili da discutere durante le riunioni ed è molto facile monitorare lo stato generale dell'intero processo.
JMP 19 ha una nuova funzionalità chiamata Column Tag. Li abbiamo trovati estremamente utili per questo progetto. Stiamo utilizzando tre column tag: uno per taggare i timestamp di determinate milestone, uno per taggare gli errori che troviamo e uno per taggare le colonne che utilizziamo nei grafici. Vi mostrerò come funzionano più avanti.
Perché lo abbiamo fatto? Innanzitutto, semplifica notevolmente il nostro codice JSL. Possiamo fare riferimento alle colonne con un tag specifico invece di dover codificare ogni nome di colonna. Questo significa anche che non dobbiamo aggiornare i nomi delle colonne quando, ad esempio, aggiorniamo gli errori che troviamo. Ci basta taggare la nuova colonna, che verrà aggiunta in modo naturale. Questo semplifica notevolmente la creazione di sottoinsiemi e la pubblicazione dei soli dati necessari.
Ora faremo una demo e vi mostrerò il nostro script JSL. Prima ancora di iniziare a lavorare, eseguiamo alcune righe per impostare tutto. Includiamo un paio di altri script con funzioni e definizioni di variabili. Impostiamo il percorso della cartella in cui vengono salvate le nostre tabelle principali. Queste contengono tutti i dati che abbiamo raccolto finora. Quindi apriamo un file CSV con le informazioni di configurazione.
Il nostro file CSV reale ha più colonne di queste, ma contengono informazioni interne che ho eliminato per questa presentazione. Le informazioni di base di cui abbiamo bisogno per ogni progetto sono il nome di ogni flusso di test e la tabella principale specifica del sistema operativo che contiene tutti i dati che abbiamo raccolto finora da tutti i flussi di test per il sistema operativo di questo flusso.
Ora dobbiamo leggere i nostri dati. Ogni combinazione di versione di JMP e versione del sistema operativo è un progetto Jenkins separato con una propria pagina. Ogni esecuzione del flusso di test è una sottopagina della pagina principale. Esamineremo questo primo progetto, JMP 19 su Sonoma. Imposterò la mia iterazione a 1. Quindi leggerò il nome di ogni job e la tabella di destinazione in cui vogliamo importarlo.
Iniziamo utilizzando una richiesta HTTP per ottenere il JSON della pagina principale, che contiene un elenco degli URL per ogni stream eseguito. Inviamo la richiesta a Jenkins, otteniamo il JSON e lo analizziamo nella nostra build list. Dato che non posso eseguirlo live qui, creerò semplicemente una lista. Come potete vedere, è molto lunga. Questa lista contiene l'ID build di ogni singolo stream di test eseguito finora per JMP 19 su Macintosh Sonoma.
Ma abbiamo già molti di questi dati nella tabella principale e non vogliamo perdere troppo tempo a leggere ed elaborare i dati che già abbiamo. Per aggirare questo problema, apriamo la nostra tabella principale. Questa contiene tutti i risultati Macintosh, non solo i risultati per questo progetto. Quindi otteniamo un elenco degli ID build che abbiamo già per questo progetto. In altre parole, gli ID delle righe nella tabella principale che corrispondono a JMP 19 su Sonoma. Trasformiamo entrambi gli elenchi di ID build in array associativi in modo da poter sottrarre gli ID correnti dal nostro nuovo elenco. La magia matematica degli array associativi ci fornisce un breve elenco delle sole esecuzioni che non abbiamo ancora elaborato. Sarà molto più facile e molto più veloce elaborare 3 righe invece di 395 e poi scartarne la maggior parte.
Il secondo passaggio consiste nell'importare effettivamente i dati in JMP. Se l'elenco non è vuoto, utilizziamo una funzione da noi scritta che estrae i dati desiderati da ciascuna esecuzione del flusso nel nostro elenco e li importa in una tabella dati. Questa funzione utilizza nuovamente Parse JSON per estrarre i dati desiderati e aggiungerli alla nuova tabella dati, suddividendoli in colonne e righe per ogni flusso di test.
Aprirò una tabella di esempio così potrete vedere come appare. Come potete vedere, abbiamo già alcuni metadati. Otteniamo la durata di ogni flusso specifico, il suo nome, il suo numero ID, lo stato della build, se è riuscita o meno e la data e l'ora di inizio. Ancora più importante, abbiamo l'URL che porta al log di testo da cui otterremo tutte le nostre informazioni. Il passo successivo è aggiungere una colonna a questa tabella per contenere il contenuto del file di log e usare il comando "load text file" per leggerlo da quell'URL e inserirlo nella tabella.
Il terzo passaggio consiste nell'analizzare i metadati e i log della console per queste righe. Aggiungiamo alcune nuove colonne derivate. Ad esempio, prendiamo quella durata e la trasformiamo in un tempo di esecuzione misurato in minuti, che è più facile da comprendere. Aggiungiamo anche una colonna data che contiene solo la data senza timestamp. Questo semplifica notevolmente alcuni dei nostri grafici. Infine, un'altra colonna che aggiungiamo è quella del giorno della settimana. La utilizziamo anche in alcuni dei nostri grafici.
Il passo successivo è preparare il terreno per l'analisi del log. Aggiungiamo colonne per raccogliere gli orari di inizio per determinate milestone. Definiamo colonne di inizio diverse per Windows e Macintosh e, quando aggiungiamo queste colonne, le tagghiamo come inizi. Potete vedere questi tag verdi qui, che indicano che tutte queste colonne sono contrassegnate con lo stesso tag. Potete vederli qui sotto.
La colonna successiva che aggiungiamo è l'ultima cosa avviata. Se il flusso di test si interrompe a causa di un errore, possiamo ottenere l'ultima milestone che ha avuto successo. Sarà l'ultima colonna con un tag di avvio per questa riga che ha un timestamp di data e ora. Se il processo perde la connessione alla VM o al server, nel log verranno visualizzati errori JavaScript. Questi vengono sicuramente raccolti.
Infine, aggiungiamo una colonna a risposta multipla per raccogliere informazioni sugli errori. Torneremo su questo argomento tra poco. Il passo successivo è iniziare effettivamente ad analizzare il log. Utilizziamo un ciclo per analizzare l'intera console per ogni riga, estraiamo le informazioni desiderate e le aggiungiamo a tutte le nuove colonne per quella riga. Il log della console è in una singola cella per ogni riga. Lo trasformiamo in un elenco di stringhe in cui ogni riga del log è una stringa.
Quindi eseguiamo un ciclo sull'elenco una volta e confrontiamo ogni riga con gli elementi specifici che stiamo cercando, orari di inizio, errori, ecc. Questo è molto più efficiente che analizzare il log una volta per ogni elemento che vogliamo estrarre. La prima cosa che cerchiamo è un errore JavaScript. Se lo troviamo, lo recuperiamo e lo inseriamo nella tabella dati, per poi passare al log della console della riga successiva. Una volta interrotto il processo in questo modo, non ci saranno più informazioni da recuperare.
Nei nostri progetti Jenkins, cerchiamo errori specifici e queste ricerche vengono visualizzate alla fine del log e sono chiamate "text finder". Quando raggiungiamo la prima istanza di un text finder, raccogliamo i dati fino alla fine del log. Interrompiamo il ciclo proprio lì, perché questa è la fine del log. Inseriamo i dati in una funzione, che vi mostrerò tra poco.
Oltre a queste due cose, cerchiamo le milestone e inseriamo le relative date e ore nella tabella per ciascuna di esse. Infine, alla fine del ciclo, raccogliamo i risultati di ricerca. Ogni risultato di ricerca trovato viene aggiunto alla tabella come colonna indicatrice, con uno zero se l'errore non viene trovato o uno se viene trovato.
Ciò lascia mancanti i punti in cui non abbiamo cercato quell'errore. Ad esempio, se è nuovo, quindi non lo abbiamo cercato in precedenza. Ogni colonna del Finder di testo è etichettata come Finder di testo. Una volta compilate le colonne degli indicatori, otteniamo i nomi di ogni colonna etichettata come Finder di testo che ha un 1 per quella riga e la aggiungiamo alla colonna a risposta multipla, che verrà utilizzata in seguito per la rappresentazione grafica.
Infine, avviamo l'ultima cosa e la aggiungiamo alla tabella per questa riga. I cicli per le righe successive confronteranno i finder di testo trovati con le colonne che già abbiamo e aggiungeranno solo quelle che non abbiamo ancora. Qui aprirò un esempio di come appare questa tabella dopo aver eseguito tutto questo. Abbiamo ancora tutti i metadati che abbiamo raccolto inizialmente. Abbiamo il nostro testo di log. Abbiamo tutti i nostri dati derivati. Ora tutte le nostre colonne di inizio contengono i timestamp di data e ora, quindi sappiamo esattamente quando è iniziata ciascuna.
L'ultima cosa che abbiamo avviato ha raccolto l'ultimo elemento che ha avuto successo. Non abbiamo riscontrato errori di disconnessione Java, ma se li avessimo avuti, sarebbero stati presenti.
Infine, abbiamo aggiunto tutte le nostre colonne di ricerca del testo. Vedrete che sono contrassegnate in rosso. Tutte queste esecuzioni hanno avuto esito positivo, ma ho inserito alcuni risultati positivi di errori, così da potervi mostrare come appaiono nella nostra colonna a risposta multipla. Potete vedere che questa ha avuto due errori ed è delimitata da punto e virgola. Questa ne ha avuto solo uno.
Il passaggio finale consiste nell'aggiungere tutte queste nuove informazioni alla nostra tabella dati principale. Si tratta semplicemente di una concatenazione. Stavamo per aggiungere le nostre nuove tre righe alla tabella. Il passaggio finale consiste nel salvare questa tabella. Comprimiamo la tabella quando la salviamo, perché quei log a volte sono lunghi centinaia o addirittura migliaia di righe. Con 1.333 righe, si tratta di una grande quantità di dati e queste tabelle possono diventare molto grandi. Questo è il nocciolo del processo e ciò che ha richiesto più tempo per essere sviluppato. Decidere esattamente quali informazioni ci servissero è stato un processo iterativo.
Il passaggio finale consiste nell'esplorare e rappresentare graficamente i dati. Abbiamo salvato molti grafici che abbiamo progettato per aiutarci a diagnosticare i problemi. Ve ne mostrerò alcuni. Questo mostra la durata dell'esecuzione di un flusso in base alla data e al sistema operativo.
Gli streaming di breve durata sono solitamente quelli che vanno in onda rapidamente e si interrompono. Raramente riceviamo risultati. Uno streaming che ha richiesto molto tempo probabilmente si è bloccato da qualche parte e probabilmente prima o poi andrà in timeout. Sono tutti problemi che dobbiamo approfondire. Questo streaming è davvero coerente. È sempre bello vederlo.
Un altro grafico utile è questo, che mostra le correlazioni tra gli errori JavaScript e l'ultima cosa avviata correttamente. Può essere molto utile per diagnosticare i problemi. Quest'ultimo è probabilmente il mio preferito. È una mappa termica che mostra lo stato di compilazione nelle esecuzioni mancanti.
Potete vedere immediatamente che eseguiamo questi flussi di test cinque giorni a settimana. Non li eseguiamo nei fine settimana. Potete vedere che questo ha molte esecuzioni mancanti in cui semplicemente non abbiamo eseguito alcun flusso di test. Questi sono stati eseguiti ma hanno presentato errori. Tutte queste esecuzioni verdi erano perfettamente a posto. Ecco le tre righe che abbiamo appena aggiunto a questa tabella.
Il quinto passaggio consiste nel condividere i grafici. Poiché sono già tutti pubblicati in JMP Live, non dobbiamo far altro che aggiornare la tabella e i grafici in JMP Live. Creiamo una connessione alla nostra istanza di JMP Live. Questa è definita in File, Pubblica, Gestisci connessioni. Apriamo la tabella dati ed eseguiamo lo script del sottoinsieme.
Potreste chiedervi perché ci prendiamo la briga di farlo. Noterete che la nostra tabella principale originale ha 39 colonne. La nostra tabella di sottoinsieme ne ha solo nove, e queste nove colonne non includono i giganteschi log della console. Questo rende queste tabelle di sottoinsieme molto più piccole, il che migliora le prestazioni di JMP Live. È anche positivo il fatto che possiamo facilmente non pubblicare dati privati o proprietari. Pubblichiamo solo i dati strettamente necessari per i grafici che utilizziamo.
Inviamo quella tabella di sottoinsieme e altre informazioni, come la connessione a JMP Live, a una funzione che abbiamo scritto e che esegue un ciclo su ogni singolo grafico nella tabella. Quindi aggiorna ogni tabella. Quindi aggiorna la tabella di sottoinsieme in ogni grafico in JMP Live e poi passa alla tabella successiva, in questo caso Windows. Questo completa il processo. Ogni giorno che eseguiamo questa funzione, otteniamo risultati completamente nuovi.
Ecco un esempio di alcuni dei grafici che abbiamo pubblicato su JMP Live. Dalla nostra dashboard, questi e altri grafici ci offrono una rapida panoramica, così possiamo individuare facilmente tendenze generali e punti critici. Dato che si tratta di JMP Live, possiamo esaminare ogni grafico singolarmente e approfondire la ricerca di informazioni aggiuntive. Tutti i nostri grafici vengono pubblicati con filtri dati locali. Questo ci consente di esaminare solo determinati flussi di test eseguiti in un determinato intervallo di tempo. Ad esempio, nelle ultime settimane sono stati eseguiti solo flussi di test JMP 20.
Alla fine, cosa abbiamo ottenuto? Abbiamo automatizzato la nostra analisi. Tutto ciò che dobbiamo fare è guardare i grafici per vedere cosa sta succedendo. Si aggiornano automaticamente. Non dobbiamo gestire errori specifici. È un'autopubblicazione. Con tutto predefinito, il processo funziona ogni giorno. Mentre elaboravamo i nostri dati, abbiamo preso decisioni su cosa dovevamo monitorare. Abbiamo modificato ciò che visualizzavamo nei log della console, ad esempio aggiungendo gli orari di inizio per le milestone. Abbiamo deciso quali grafici funzionavano meglio per mostrare i problemi che dovevamo risolvere.
Una volta completato e avviato lo script, abbiamo ottenuto la grande vittoria. Finora avevamo risolto tutti i nostri problemi e i nostri grafici non sono più così entusiasmanti, dato che i flussi di test di solito ora funzionano correttamente. Grazie.