大家好,我是 Melanie Drake,JMP 開發維運部門的系統工程師。我們團隊負責提供自動化流程,包括建立 JMP 可執行檔和安裝程序、執行自動化測試以及執行許多其他任務,以支援 JMP 的研發工作。
今天,我將講解我們如何從流程日誌中收集信息,並使用 JMP 來查找問題並監控其中一個自動化流程的整體運作狀況。流程會產生大量數據,這些數據可能來自儀器讀數、生產線測量數據或狀態日誌。它們可以儲存為純文字格式,有時也可以儲存為結構化文字格式,例如 XML、JSON 或 CSV。所有這些數據都為尋找和解決問題提供了大量機會。
然而,這些數據通常被寫入許多不斷創建和更新的獨立文件中。這使得發現和追蹤錯誤變得非常困難。我將以每日 JSL 單元測試流程為例進行說明。這個流程每天都會產生大量日誌檔案。我們發現,追蹤和排查特定錯誤非常困難。我們擁有大量數據,但使用起來卻異常困難。
什麼是 JSL 單元測試流程?它是一套 JSL 回歸測試,我們使用每天建立的 JMP 版本來運行它。它每晚都會在所有正在開發的 JMP 版本上運行,並且支援多種 Windows 和 Macintosh 作業系統版本。測試人員利用這個夜間運行來尋找 JMP 中的錯誤,以便開發人員能夠快速解決這些問題。如果某個測試流運行失敗,他們就無法獲得對應的測試結果。
在夏季的幾週時間裡,我們同時開發了四個版本的 JMP。這意味著我們每晚都要在不同的 JMP 版本和作業系統組合上執行 18 個測試流程。整個測試流程可能需要 8 到 12 個小時才能運行。我們將每個 Windows 測試流程拆分成三個並發運行的測試流,以便更快完成所有 Windows 測試。這樣,我們在北京的測試團隊就可以在當天使用這些測試結果。
總的來說,我們每天要查看 36 個獨立的控制台日誌。我們花了大量時間試圖找出特定錯誤發生的位置和時間。管理分散資料的解決方案是將所有資料整合到一個地方。當然,就是 JMP 資料表。
在 JMP 中,您可以視覺化和分析數據,並使用 JMP Live 與他人分享您的發現。在本例中,我們將日誌收集到 JMP 資料表中。我們使用圖表來追蹤和排查所有問題,並透過發佈到 JMP Live 來持續監控系統運作狀況。由於 Macintosh 和 Windows 的日誌輸出不同,我們最終創建了兩個不同的資料表。雖然有些錯誤是通用的,但許多其他錯誤則是特定於某個作業系統。
我先概述整體工作流程,然後會逐步講解腳本並示範我們的流程。第一步是確定資料的位置以及如何將其匯入 JMP。日誌檔案可以儲存到磁碟或伺服器,也可以透過 Web 存取。或許您可以透過 API 甚至資料庫查詢來取得它們。在我們的案例中,我們透過 Jenkins 伺服器的 Web 使用者介面存取文字文件,我們所有的自動化流程都運行在該伺服器上。我們使用 HTTP 請求獲取信息,然後使用 Parse JSON 將信息轉換成我們可以使用的格式。
第二步是將資料導入 JMP。這可以從日誌檔案開始,您可能還需要新增其他元資料。您可以將整個日誌放在一個儲存格中,也可以將日誌分散到多個儲存格、列甚至行中。這完全取決於您的資料類型以及您需要如何查看資料。在我們的範例中,每個流運行對應一個日誌。我們將每個流運行放在一行,並將整個日誌放在一個單元格中。此外,我們還收集了許多其他元資料。
第三步是解析文字。您可以使用 Parse XML 或 Parse JSON 來取得原始資訊。取得原始資訊後,您可以使用正規表示式、JSL 模式匹配或 JSL 字元函數來解析資訊。您可以使用公式列、指示符列或多個回應列將資訊收集成可用於圖表的格式。
在我們的案例中,我們幾乎使用了以上所有方法。我們使用公式列來收集元資料列的特定信息,然後使用正則表達式和 JSL 字元函數來解析實際日誌。我們還使用了指示器列和多個響應列來收集信息,以便從中生成漂亮的圖表。
第四步是探索和繪製數據圖表。 JMP 提供了 Graph Builder 和許多其他繪圖平台。您可以使用這些工具來尋找錯誤模式,找出錯誤之間的相關性,並追蹤它們隨時間的變化。在我們的案例中,我們大量使用了 Graph Builder。我們創建了熱圖來查看相關性。我們使用長條圖和散佈圖來追蹤錯誤隨時間、JMP 版本和作業系統版本的變化。我們還使用了堆疊條形圖和熱圖來查看流狀態和缺失運行隨時間的變化。稍後我會展示一些範例。
最後,在第五步,您需要與同事分享圖表。您可以向特定受眾展示特定的發現,也可以分享圖表以提高透明度、增強團隊合作。此外,您還可以隱藏私人、個人或機密資料。在我們的案例中,我們將部分資料發佈到內部 JMP Live 伺服器。我們只發布圖表所需的數據。圖表發布後,在會議中討論起來非常方便,也方便監控整個流程的運作狀況。
JMP 19 新增了一項名為「列標籤」的功能。我們發現它對本項目非常有用。我們使用了三個列標籤:一個用於標記特定里程碑的時間戳,一個用於標記我們發現的錯誤,還有一個用於標記我們在圖表中使用的列。稍後我會詳細講解它們的使用方法。
我們為什麼要這樣做?首先,這大大簡化了我們的 JSL 程式碼。我們可以使用特定的標籤來引用列,而無需硬編碼每個列名。這也意味著,例如,當我們更新發現的錯誤時,無需更新列名。我們只需標記新列,它就會自動生效。這使得篩選和發布必要的數據變得非常容易。
現在我們來示範一下,我會展示我們的 JSL 腳本。在正式開始之前,我們先運行幾行程式碼進行設定。我們引入了一些包含函數和變數定義的其他腳本。我們設定了保存主表的資料夾路徑。這些表包含了我們目前收集的所有數據。然後,我們打開一個包含配置資訊的 CSV 檔案。
我們實際的 CSV 檔案包含的列比這多,但其中包含一些內部信息,為了演示,我已將其刪除。每個項目所需的基本資訊包括:每個測試流程的名稱,以及與作業系統相關的主表,該主表保存了我們目前為止從該測試流的所有作業系統收集到的所有資料。
現在我們需要讀取資料。每個 JMP 版本和作業系統版本的組合都是一個獨立的 Jenkins 項目,擁有自己的頁面。每個測試流程運行都是主頁的一個子頁面。我們將以第一個專案為例,即在 Sonoma 上運行的 JMP 19。我將迭代次數設為 1。然後,我將讀取每個作業的名稱以及要匯入的目標表。
我們首先使用 HTTP 請求獲取主頁的 JSON 數據,其中包含每次流運行的 URL 清單。我們將該請求發送到 Jenkins,獲取 JSON 數據,並將其解析到我們的建置列表中。由於我無法在此處即時運行,因此我將建立一個清單。您可以看到它非常長。此清單包含迄今為止在 Macintosh Sonoma 上使用 JMP 19 運行的每個測試流的建置 ID。
但是,主表中已經包含了大部分此類數據,我們不想浪費大量時間讀取和處理現有的數據。為了解決這個問題,我們打開主表。主表包含了所有 Macintosh 的結果,而不僅僅是本專案的結果。然後,我們取得了本項目已有的建置 ID 清單。換句話說,就是主表中與 Sonoma 上的 JMP 19 相符的行的 ID。我們將這兩個建置 ID 清單轉換為關聯數組,以便從新清單中減去目前的 ID。關聯數組的運算功能可以讓我們得到一個僅包含尚未處理的運行記錄的簡短清單。處理 3 行資料比處理 395 行資料然後丟棄大部分資料要容易得多,也快得多。
第二步是將這些資料實際導入 JMP。如果列表不為空,我們會使用先前編寫的函數,該函數從列表中每個流運行中獲取所需數據,並將其匯入到數據表中。該函數再次使用 Parse JSON 來提取所需的追蹤數據,並將其添加到新的數據表中,每個測試流對應一列和一行。
我打開一個範例表,方便您查看其結構。可以看到,我們已經取得了一些元資料。這些元資料包括:特定流的持續時間、名稱、ID 號碼、建置狀態(成功或失敗)以及開始日期時間戳記。最重要的是,我們獲得了指向文字日誌的 URL,所有資訊都將從中取得。接下來,我們將向此表中新增一列,用於儲存日誌檔案的內容,並使用「載入文字檔案」函數從該 URL 讀取日誌檔案並將其插入到表中。
第三步是解析這些行的元資料和控制台日誌。我們新增了一些新的派生列。例如,我們將持續時間轉換為以分鐘為單位的運行時間,這樣更容易理解。我們還新增了一個日期列,其中只包含日期,不包含時間戳記。這使得我們的一些圖表更加簡潔。最後,我們新增的另一個欄位是星期幾。我們也在一些圖表中用到了它。
接下來,我們需要為解析日誌做好準備。我們會新增列來收集特定里程碑的開始時間。我們為 Windows 和 Macintosh 定義了不同的開始時間列,並在新增這些列時,將它們標記為「開始時間」。您可以看到這裡的綠色標籤,它們表示所有這些列都標記了相同的標籤。您可以在下方看到這些標籤。
我們新增的下一列是「最後啟動項目」。如果測試流程因錯誤而停止運行,我們可以獲得最後一個成功的里程碑。它將是該行中帶有“starts”標籤且包含日期時間戳記的最後一列。如果進程與虛擬機器或伺服器的連線斷開,則日誌中將出現 JavaScript 錯誤。我們一定會收集這些錯誤。
最後,我們新增了一個多重回應列來收集錯誤訊息。稍後我會詳細介紹。下一步是開始解析日誌。我們使用一個循環來解析每一行的整個控制台輸出,提取所需資訊並將其添加到該行的所有新列中。控制台日誌在每一行的單一儲存格中。我們將其轉換為字串列表,其中日誌的每一行都是字串。
然後我們遍歷該清單一次,並將每一行與我們正在尋找的特定項目(例如開始時間、錯誤等)進行比較。這比為要提取的每個項目解析一次日誌要高效得多。我們首先要查找的是 JavaScript 錯誤。如果找到錯誤,我們就將其取得並放入資料表中,然後繼續處理下一行的控制台日誌。一旦作業以這種方式停止,就不會再有任何資訊需要取得。
在我們的 Jenkins 專案中,我們會尋找特定的錯誤,這些查找結果會印在日誌末尾,稱為文字查找器。當我們找到第一個文字查找器時,我們會收集日誌直到末尾。因日誌到此結束,所以我們會停止循環。我們會將收集到的信息傳遞給一個函數,稍後我會示範這個函數。
除了以上兩個項目之外,我們還會尋找里程碑事件,並將每個里程碑事件的日期時間戳記插入表格中。最後,在循環結束時,我們會收集這些文字查找器。找到的每個文字查找器都會作為指示列新增到表格中,如果未找到錯誤則為 0,如果找到錯誤則為 1。
這樣就遺漏了我們沒有查找到的錯誤。例如,如果這是一個新錯誤,我們之前就沒有查找過。每個文字查找列都被標記為「文字查找」。一旦這些指示列被填充,我們就會取得每個標記為「文字尋找」且在該行中值為 1 的列的名稱,並將其新增至多選回應列中,該列稍後用於繪製圖表。
最後,我們啟動最後一個任務,並將其新增到該行的表格中。後續行的循環會將找到的文字查找器與我們已有的列進行比較,並僅新增尚未包含的列。這裡我將開啟一個範例,展示執行所有操作後表格的樣子。我們仍然保留了最初收集的所有元資料。我們有日誌文字。我們有所有派生資料。現在,所有起始列都包含日期時間戳,因此我們可以準確地知道每個任務的開始時間。
我們最後啟動的進程收集到了最後一個成功收集的項目。我們沒有遇到任何 Java 斷開連接的錯誤,但如果有的話,錯誤訊息應該會顯示出來。
最後,我們新增了所有文字查找列。您會看到它們都被標記為紅色。所有這些運行實際上都成功了,但我添加了一些錯誤結果,以便向您展示它們在多重回應列中的顯示方式。您可以看到,這個運行有兩個失敗項,它們之間用分號分隔。而這個運行只有一個失敗項。
最後一步是將所有新資訊新增到我們的主資料表中。這只是一個簡單的連接操作。我們要將新增的三行資料加入表中。最後一步是儲存此表。保存時我們會壓縮表格,因為這些日誌有時長達數百甚至數千行。 1333 行資料量非常大,這些表格可能會變得非常龐大。這部分是整個流程的核心,也是我們開發過程中耗時最長的部分。確定我們需要哪些資訊是一個反覆迭代的過程。
最後一步是探索數據並繪製圖表。我們保存了很多自己設計的圖表,這些圖表可以幫助我們診斷問題。我將向您展示其中幾個。這個圖表顯示了按日期和作業系統劃分的資料流運行時長。
持續時間短的直播通常很快就會結束,我們很少能從中獲得任何結果。持續時間很長的直播可能卡在某個地方,最後也會超時。這些都是我們需要進一步調查的問題。這次的直播運行非常穩定,這總是令人欣喜的。
另一個很有用的圖表是這張,它顯示了 JavaScript 錯誤與最後一次成功啟動的操作之間的相關性。這對於診斷問題非常有用。最後這張圖表可能是我最喜歡的。它是一張熱圖,顯示了缺失運行中的建置狀態。
您可以立即看到,我們每週運行五天這些測試流。週末不運行。您可以看到,此表中有很多缺少的運行記錄,這些記錄表明我們根本沒有運行任何測試流程。這些測試流程雖然運行了,但出現了錯誤。所有這些綠色的運行記錄都完全正常。以下是我們剛剛新增到此表中的三行資料。
第五步是共享圖表。由於所有這些圖表都已發佈到 JMP Live 中,我們只需在 JMP Live 中更新表格和圖表即可。我們建立與 JMP Live 實例的連線。這可以在“檔案”>“發布”>“管理連線”中進行設定。我們打開資料表,然後執行子集腳本。
你可能會好奇我們為什麼要這麼做。你會注意到,我們原來的主表有 39 列,而子表只有 9 列,而且這 9 列還不包括龐大的控制台日誌。這使得子表體積小得多,從而提升了 JMP Live 的效能。此外,我們還可以輕鬆地選擇不發布私有或專有數據,這一點也很棒。我們只發布用於生成圖表所必需的數據。
我們將該子集表以及其他資訊(例如 JMP Live 連接)傳送到我們編寫的一個函數,該函數會遍歷表中的每個圖表。然後,它會更新每個表。接著,它會更新 JMP Live 中每個圖表的子集表,然後繼續處理下一個表,在本例中是 Windows 表。這樣就完成了。每天運行此程序,我們都會得到全新的結果。
以下是我們發佈在 JMP Live 上的一些圖表範例。透過我們的儀表板,這些圖表以及其他圖表可以讓我們快速概覽,輕鬆查看整體趨勢和問題所在。由於這是 JMP Live,我們可以單獨查看每個圖表並深入挖掘更多資訊。我們發布的所有圖表都帶有本地資料篩選器。這使我們能夠僅查看特定時間段內的特定測試流程運行情況。例如,僅查看最近幾週的 JMP 20 測試流程運行情況。
最終,我們取得了什麼成果?我們實現了洞察的自動化。我們只需查看圖表即可了解情況。圖表會自動更新。我們無需手動處理特定錯誤。它會自動發布。由於所有設定都已預先定義,因此該流程每天都能正常運作。在處理資料的過程中,我們確定了需要追蹤的內容。我們修改了控制台日誌的列印內容,例如新增了里程碑的起始時間。我們也確定了哪些圖表最能有效地展示我們需要解決的問題。
當我們完成並運行這個腳本時,我們取得了重大勝利。我們解決了之前遇到的所有問題,而且由於測試流程現在通常都能成功運行,我們的圖表也不再那麼令人興奮了。謝謝。