こんにちは。JMP開発オペレーションのシステムエンジニア、メラニー・ドレイクです。私たちのグループは、JMPの実行ファイルとインストーラーのビルド、自動テストの実行、その他JMPの研究開発を支援するための様々なタスクを自動化するプロセスを提供しています。
本日は、プロセスログから情報を収集し、JMPを使用して自動化プロセスの問題を特定し、全体的な健全性を監視する方法についてお話しします。プロセスは大量のデータを生成します。機器の測定値、製造ラインの測定値、ステータスログなどです。データはプレーンテキスト形式で保存される場合もあれば、XML、JSON、CSVなどの構造化テキスト形式で保存される場合もあります。これらのデータはすべて、問題の発見と修正に多くの機会をもたらします。
しかし、このデータは多くの場合、多数の個別のファイルに書き込まれ、常に作成・更新されています。そのため、エラーに気づいて追跡するのは非常に困難です。これから、夜間のJSLユニットテストストリームについてお話します。このプロセスでは、毎日多数のログファイルが生成されます。そのため、特定のエラーを追跡してトラブルシューティングするのは非常に困難であることがわかりました。データは山積みでしたが、それを活用するのは非常に困難でした。
JSLユニットテストストリームとは何でしょうか?これは、JMPの毎日のビルドを使用して実行するJSL回帰テストスイートです。これは、現在開発中のすべてのバージョンのJMPで毎晩実行され、WindowsおよびMacintoshのさまざまなオペレーティングシステムバージョンで実行されます。テスターはこの毎晩の実行を使用してJMPのエラーを検出し、開発者が迅速に対応できるようにします。特定のテストストリームが正常に実行されなかった場合、そのテスト結果は取得されません。
夏の数週間、JMPの4つのバージョンを同時に開発していました。つまり、JMPとOSの複数のバージョンを組み合わせて、毎晩18本のテストストリームを実行していたのです。ストリーム全体の実行には8~12時間かかります。そこで、Windowsのテストストリームを3本に分割し、同時に実行することで、すべてのWindowsテスト結果をより迅速に完了できるようにしました。これにより、北京のテストチームは、それらのテスト結果を当日中に利用できるようになりました。
これらすべてを合わせると、毎日36個のコンソールログを確認していました。特定のエラーがいつどこで発生したかを調べるのに、かなりの時間を費やしていました。分散したデータを管理するには、すべてを一箇所に統合する必要があります。もちろん、JMPのデータテーブルを使うのが正解です。
JMPでは、データを視覚化・分析し、JMP Liveを使って他のユーザーと結果を共有できます。私たちの場合、ログをJMPのデータテーブルに収集しました。グラフを使用してすべての問題を追跡・トラブルシューティングし、JMP Liveにパブリッシュすることで継続的に健全性を監視できます。ログ出力がMacintoshとWindowsで異なるため、最終的に2つの異なるデータテーブルが作成されました。一部のエラーは共通していましたが、多くのエラーは特定のオペレーティングシステムに固有のものでした。
まず、一般的なワークフローの概要を説明し、その後、スクリプトを順に解説し、プロセスのデモを行います。最初のステップは、データがどこに保存されているかを確認し、JMPにどのように読み込むかを決めることです。ログファイルはディスクまたはサーバーに保存できます。Webからアクセスすることも可能です。APIやデータベースクエリを使って取得することも可能です。今回のケースでは、すべての自動化プロセスを実行するJenkinsサーバーのWebベースUIからテキストファイルにアクセスします。HTTPリクエストを使用して情報を取得し、その後、Parse JSONを使用して、使用可能な形式で情報を取得しました。
ステップ2は、データをJMPにインポートすることです。ログファイルから開始し、そこに他のメタデータを追加することもできます。ログ全体を1つのセルにまとめることも、複数のセル、列、さらには行に分散させることもできます。すべては、データの内容と、それをどのように表示するかによって異なります。今回のケースでは、ストリーム実行ごとに1つのログがあります。1行ごとに1つのストリーム実行と、1つのログ全体を1つのセルにまとめます。さらに、他の多くのメタデータも収集します。
ステップ3はテキストを解析することです。Parse XMLやParse JSONを使って生の情報を取得することができます。生の情報を取得したら、正規表現、JSLパターンマッチング、JSL文字関数などを使って情報を解析します。数式列、インジケータ列、複数回答列などを使って、グラフで使用できる形式に情報を収集することもできます。
私たちのケースでは、上記のほぼすべてを活用しました。数式列を使用してメタデータ列の具体的な情報を収集し、その後、正規表現とJSL文字関数を使用して実際のログを解析しました。また、インジケーター列と複数レスポンス列を使用して情報を収集し、それらから見やすいグラフを作成しました。
ステップ4は、データを探索し、グラフ化することです。JMPにはグラフビルダーをはじめとする様々なグラフ作成プラットフォームが用意されています。これらを使って、エラーのパターンを見つけ、エラー同士の相関関係を調べ、経時的にエラーを追跡することができます。私たちのケースでは、グラフビルダーを多用しました。相関関係を確認するためにヒートマップを作成しました。また、経時変化、JMPのバージョン、オペレーティングシステムのバージョンをまたいでエラーを追跡するために、棒グラフと散布図を使用しました。さらに、積み上げ棒グラフとヒートマップを使用して、ストリームの状態と実行の欠落を経時的に確認しました。これらの例については後ほどいくつか紹介します。
最後に、ステップ5では、グラフを同僚と共有します。特定の調査結果を特定の対象者に提示できます。共有することで、可視性、透明性、そしてチームワークを高めることができます。また、個人情報や機密性の高いデータを非公開にすることもできます。私たちの場合、データのサブセットを社内のJMP Liveサーバーに公開しています。グラフに必要な情報のみを公開しています。公開されたグラフは、会議での議論が容易になり、プロセス全体の健全性も容易に監視できます。
JMP 19には「列タグ」という新機能が搭載されました。このプロジェクトでは非常に便利だと感じました。3つの列タグを使用しています。1つは特定のマイルストーンのタイムスタンプをタグ付けするため、1つは発見したエラーをタグ付けするため、そしてもう1つはグラフで使用する列をタグ付けするためです。これらの機能の使い方については、後ほど詳しく説明します。
なぜこれをやったのか?まず、JSLコードが大幅に効率化されるからです。列名を一つ一つハードコーディングする代わりに、特定のタグを使って列を参照できます。また、例えば発見したエラーを更新する際に列名を更新する必要もありません。新しい列にタグを付けるだけで、自然に反映されます。これにより、必要なデータだけをサブセット化して公開するのが非常に簡単になります。
ではデモを見ながら、JSLスクリプトをお見せします。作業を始める前に、いくつかの行を実行して設定を行います。関数と変数定義を含むスクリプトをいくつか追加しています。メインテーブルが保存されているフォルダパスを設定します。これらのテーブルには、これまでに収集したすべてのデータが保存されています。次に、設定情報を含むCSVファイルを開きます。
実際のCSVファイルにはこれよりも多くの列がありますが、これらの列には内部情報が含まれており、このプレゼンテーションでは削除しました。各プロジェクトに必要な基本情報は、各テストストリームの名前と、このストリームのオペレーティングシステムについて、これまですべてのテストストリームから収集したすべてのデータを保持するオペレーティングシステム固有のメインテーブルです。
次に、データを読み込みます。JMPのバージョンとOSのバージョンの組み合わせはそれぞれ独立したJenkinsプロジェクトであり、それぞれにページがあります。各テストストリームの実行は、メインページのサブページになります。最初のプロジェクトであるSonoma上のJMP 19を例に、手順を見ていきましょう。反復回数は1に設定します。次に、各ジョブの名前と、ジョブを取り込むターゲットテーブルを読み込みます。
まず、HTTPリクエストを使ってメインページのJSONを取得します。このJSONには、各ストリーム実行のURLリストが含まれています。このリクエストをJenkinsに送信し、JSONを取得してビルドリストに解析します。このテストをライブで実行することはできないので、リストを作成します。非常に長いリストであることがわかります。このリストには、Macintosh Sonoma上のJMP 19でこれまでに実行されたすべてのテストストリームのビルドIDが含まれています。
しかし、このデータの多くは既にメイン テーブルに保存されており、既存のデータを読み込んで処理するのに多くの時間を無駄にしたくはありません。この問題を解決するために、メイン テーブルを開きます。このテーブルには、このプロジェクトの結果だけでなく、Macintosh の結果がすべて保存されています。次に、このプロジェクトの既存のビルド ID のリストを取得します。つまり、メイン テーブル内の行のうち、Sonoma の JMP 19 に一致する ID です。これらの 2 つのビルド ID リストを連想配列に変換し、新しいリストから現在の ID を減算できるようにします。連想配列の計算マジックにより、まだ処理していない実行だけの短いリストが作成されます。395 行を処理してそのほとんどを破棄するよりも、3 行を処理する方がはるかに簡単かつ高速になります。
ステップ2は、実際にそのデータをJMPにインポートすることです。リストが空でない場合は、リスト内の各ストリーム実行から必要なデータを取得し、データテーブルにインポートする関数を使用します。この関数は、再度「Parse JSON」を使用して必要なデータを抽出し、新しいデータテーブルに各テストストリームの列と行として追加します。
サンプルテーブルを開いて、どのようなものか確認してみましょう。すでにメタデータの一部を取得しているのがお分かりいただけると思います。ストリームの所要時間、名前、ID番号、ビルドのステータス(成功かどうか)、開始日時などが取得できます。最も重要なのは、すべての情報を取得するテキストログへのURLです。次に、このテーブルにログファイルの内容を格納する列を追加し、「load text file」コマンドを使ってURLからログファイルを読み込み、テーブルに挿入します。
ステップ3は、これらの行のメタデータとコンソールログを解析することです。いくつかの新しい派生列を追加します。例えば、期間を取得し、分単位で測定された実行時間に変換して、より分かりやすくします。また、タイムスタンプのない日付のみを表示する日付列も追加します。これにより、一部のグラフの作成がはるかに簡単になります。最後に、曜日列を追加します。これも一部のグラフで使用します。
次に、ログを解析するための準備を行います。特定のマイルストーンの開始時刻を収集するための列を追加します。WindowsとMacintoshで異なる開始列を定義し、これらの列を追加する際に開始としてタグ付けします。ここに緑色のタグが表示されていますが、これはすべての列に同じタグが付けられていることを示しています。タグは下の方にあります。
次に追加する列は「最後に開始したもの」です。テストストリームがエラーのために実行を停止した場合、最後に成功したマイルストーンを取得できます。これは、この行のstartsタグが付いた最後の列で、日付とタイムスタンプが記録されます。プロセスがVMまたはサーバーへの接続を失った場合、ログにJavaScriptエラーが記録されます。これらは確実に収集します。
最後に、エラー情報を収集するための複数レスポンス列を追加します。これについては後ほど詳しく説明します。次のステップは、実際にログの解析を開始することです。1つのループを使用して、コンソール全体を各行ごとに解析し、必要な情報を取得して、その行のすべての新しい列に追加します。コンソールログは各行ごとに1つのセルに格納されています。これを文字列のリストに変換し、ログの各行が文字列になるようにします。
次に、リストを1回ループし、各行を、開始時刻やエラーなど、探している特定の項目と比較します。これは、抽出したい項目ごとにログを1回ずつ解析するよりもはるかに効率的です。まずJavaScriptエラーを探します。見つかった場合は、それを取得してデータテーブルに格納し、次の行のコンソールログに進みます。このようにジョブが停止されると、取得する情報はなくなります。
Jenkinsプロジェクトでは、特定のエラーを検索し、その検索結果はログの末尾に出力されます。これはテキストファインダーと呼ばれます。テキストファインダーの最初のインスタンスに到達すると、ログの末尾まで収集します。ログの末尾なので、ループ処理はそこで停止します。この結果を関数に渡します。関数については後ほど説明します。
これら2つの項目以外にも、マイルストーンを探し、それぞれの日付とタイムスタンプをテーブルに挿入します。最後に、ループの最後に、テキストファインダーを収集します。見つかったテキストファインダーはそれぞれ、エラーが見つからなかった場合に0、見つかった場合に1が入るインジケーター列としてテーブルに追加されます。
そのため、そのエラーを探さなかった箇所が欠落したままになります。例えば、新しいエラーなので、以前に探したことがないという場合などです。各テキストファインダー列にはテキストファインダーのタグが付けられています。これらのインジケータ列が入力されると、その行に1が含まれているテキストファインダーのタグが付けられた各列の名前を取得し、複数回答列に追加します。この列は後でグラフ作成に使用されます。
最後に、最後の処理を開始し、この行のテーブルに追加します。後続の行のループでは、見つかったテキストファインダーを既存の列と比較し、まだ存在しないものだけを追加します。ここで、この処理をすべて実行した後のテーブルの例を示します。最初に収集したメタデータはすべて残っています。ログテキストも、派生データもすべて揃っています。これで、すべての開始列に日付とタイムスタンプが保存されたので、それぞれの開始時刻が正確にわかります。
最後に開始した処理で、最後に成功した項目が収集されました。Javaの切断エラーは発生しませんでしたが、もし発生していたら、そこに記録されていたはずです。
最後に、テキスト検索の列をすべて追加しました。赤いタグが付いているのがわかると思います。これらの実行はすべて成功しましたが、エラーの肯定的な結果もいくつか追加しました。複数回答の列でどのように表示されるかお見せできるようにしました。この実行には2つの失敗があり、セミコロンで区切られています。こちらには1つしかありませんでした。
最後のステップは、この新しい情報をすべてメインのデータテーブルに追加することです。単純な連結です。新しい3行をテーブルに追加するつもりでした。最後のステップは、このテーブルを保存することです。ログは数百行、あるいは数千行に及ぶこともあるため、保存時にテーブルを圧縮します。1,333行と膨大なデータ量になり、テーブルが非常に大きくなる可能性があります。これがプロセスの核心であり、開発に最も時間がかかった部分です。必要な情報を正確に特定することは、反復的なプロセスでした。
最後のステップは、データを調査してグラフ化することです。問題の診断に役立つように、作成したグラフを多数保存しています。いくつかご紹介します。このグラフは、日付とオペレーティングシステム別に、ストリームの実行時間を示しています。
短時間の配信は、たいていすぐに終わってしまい、すぐに終わってしまうものです。そういった配信から何か成果が得られることはほとんどありません。非常に長い時間がかかった配信は、おそらくどこかで行き詰まり、最終的にはタイムアウトしてしまうでしょう。これらはすべて、さらに調査する必要がある問題です。今回の配信は非常に安定しています。いつも素晴らしいですね。
もう一つ便利なグラフは、JavaScriptエラーと最後に正常に開始されたものとの相関関係を示すグラフです。これは問題の診断に非常に役立ちます。最後のグラフはおそらく私のお気に入りです。これは、実行されなかったビルドのステータスを示すヒートマップです。
これらのテストストリームは週5日実行されていることがすぐにわかります。週末には実行しません。この表には、テストストリームを全く実行しなかったために実行されなかった部分が多数あるのがわかります。これらは実行されましたが、エラーが発生しました。緑色で表示されているものはすべて正常に実行されました。これが、この表に追加した3つの行です。
ステップ5はグラフを共有することです。これらはすべてJMP Liveで既に公開されているので、JMP Liveのテーブルとグラフを更新するだけです。JMP Liveインスタンスへの接続を作成します。これは、「ファイル」→「公開」→「接続の管理」で定義されています。データテーブルを開き、サブセットスクリプトを実行します。
なぜわざわざそんなことをするのか不思議に思うかもしれません。元のメインテーブルには39列ありましたが、サブセットテーブルには9列しかなく、しかもその9列には巨大なコンソールログは含まれていません。そのため、サブセットテーブルのサイズは大幅に小さくなり、JMP Liveのパフォーマンスが向上します。また、プライベートデータや独占データを容易に公開しないことができるのも利点です。公開するのは、使用しているグラフに本当に必要なデータだけです。
このサブセットテーブルと、JMP Live接続などのその他の情報を、テーブル内のすべてのグラフをループ処理する関数に送ります。この関数はすべてのテーブルを更新します。さらに、JMP Live内のすべてのグラフのサブセットテーブルを更新し、次のテーブル(この場合はWindows)に進みます。これで処理は完了です。この関数を毎日実行するたびに、最新の結果が得られます。
JMP Liveで公開したグラフの一部をご紹介します。ダッシュボードからこれらのグラフやその他のグラフを一目で確認できるので、全体的な傾向や問題点を簡単に把握できます。JMP Liveなので、各グラフを個別に確認し、さらに詳細な情報にドリルダウンすることも可能です。すべてのグラフはローカルデータフィルターを使用して公開しています。これにより、特定の期間における特定のテストストリーム実行のみを確認できます。例えば、過去数週間のJMP 20テストストリーム実行のみを表示できます。
最終的に、私たちは何を達成したのでしょうか?それは、洞察を自動化したということです。何が起こっているかを知るには、グラフを見るだけで済みます。グラフは自動的に更新されます。特定のエラーを細かく管理する必要はありません。まるで自動で公開されるかのようです。すべてが事前に定義されているため、プロセスは毎日自動的に機能します。データを分析する中で、何を追跡する必要があるかを判断しました。マイルストーンの開始時刻を追加するなど、コンソールログに出力する内容を変更しました。解決すべき問題を示すのに最適なグラフを決定しました。
このスクリプトが完成して実行できるようになった頃には、大きな勝利を収めていました。これまでの問題はすべて解決し、テストストリームが通常通り実行されるようになったため、グラフもそれほど目立ったものではなくなりました。ありがとうございます。