paint-brush
WebRTC アプリケーションのパフォーマンスを監視すると、ユーザー エクスペリエンスが大幅に向上します@loadero
774 測定値
774 測定値

WebRTC アプリケーションのパフォーマンスを監視すると、ユーザー エクスペリエンスが大幅に向上します

Loadero19m2023/02/16
Read on Terminal Reader

長すぎる; 読むには

ビジネスが望んでいないことは、信頼性が低く、パフォーマンスの低いサービスとして知られることです。特に、数回クリックするだけで同様のソリューションがある場合はなおさらです。したがって、WebRTC アプリケーションまたはその他のソフトウェア ソリューションのパフォーマンスを認識することは、将来の問題を回避するために必須です。経験豊富な人がソリューションを開発し、リリース前にテストすることはできますが、それでもパフォーマンスの低下がまったく発生しないわけではありません。問題に早期に気づき、それに応じて行動を起こすことは、ユーザー エクスペリエンスを向上させるのに大いに役立ちます。
featured image - WebRTC アプリケーションのパフォーマンスを監視すると、ユーザー エクスペリエンスが大幅に向上します
Loadero HackerNoon profile picture
0-item

ビジネスが望んでいないことは、信頼性が低く、パフォーマンスの低いサービスとして知られることです。特に、数回クリックするだけで同様のソリューションがある場合はなおさらです。したがって、WebRTC アプリケーションまたはその他のソフトウェア ソリューションのパフォーマンスを認識することは、将来の問題を回避するために必須です。経験豊富な人がソリューションを開発し、リリース前にテストすることはできますが、それでもパフォーマンスの低下がまったく発生しないわけではありません。早期に問題に気づき、それに応じて行動を起こすことは、ユーザー エクスペリエンスを向上させるのに大いに役立ちます。


パケット損失が発生したときに、ビデオ通話アプリケーションに問題があるとします。パケット損失により、オーディオが途切れたり、ビデオがフリーズしたり、ビデオ フレームが欠落したりする可能性があり、ユーザーが何を話しているかを理解したり、通話で何が起こっているかを確認したりすることが難しくなります。また、パケット損失が多いと、アプリが接続を確立して維持することが難しくなり、通話やビデオ チャットが切断される可能性があります。あなたとあなたのチームがアプリケーションの開発に取り組んでいる間にパケット損失を経験しなかった場合、そこに問題があることを発見できる 1 つの方法があります。失望した顧客が不快な経験についてあなたに知らせることができます。もちろん、ユーザーが経験する前に問題を見つけて修正する方がはるかに良いでしょう.


これが、監視が非常に有益なプラクティスである理由です。製品のパフォーマンスと動作を長期間にわたって定期的に観察することで、発生した問題に迅速に対応し、信頼性の高い高性能サービスを維持することができます。これにより、ユーザー エクスペリエンスが向上し、顧客満足度とロイヤルティが向上するだけでなく、市場での競争力も高まります。


定期的なアプリケーション監視のメリット:


  • 問題に関する通知を早期に取得します。監視とテスト失敗の通知を設定することで、問題がリアルタイムで警告され、ユーザーのエクスペリエンスが低下する前にタイムリーにアクションを実行できます。
  • Monitoring は、障害の根本原因を特定し、改善が必要なアプリの領域を特定するのに役立つ詳細なレポートを提供します。
  • 多くの時間を節約します。監視は自動化されたプロセスです。つまり、ソリューションを一度作成すれば、メンテナンスをほとんどまたはまったく必要とせずに機能させることができます。
  • ソリューションが正常に機能するという確信。監視を実施すると、アプリケーションが定期的にチェックされているという安心感が得られ、最近通知を受け取っていなくても問題なく動作します。
  • パフォーマンスを経時的に比較する機能。監視設定で同じテストを定期的に実行し、測定を行うと、後でデータを集計して、時間の経過とともにパフォーマンスがどのように変化したかを確認できます。


WebRTC アプリの監視が Loadero でどのように機能するか

Loadero は、「Javascript + Nightwatch」、「Java + TestUI」、または「Python + Py-TestUI」などの Web 自動化テクノロジを使用して、実際のユーザーの動作と Web サイトとの対話をシミュレートできる負荷およびパフォーマンス テスト ツールです。それに加えて、テスト実行中に収集され、それらをアサートできる、FPS、ビットレート、ジッター、ラウンドトリップ時間など、必要なすべての WebRTC およびマシン統計を提供します。


監視には継続的な観察が必要なため (例: この場合は一定のテスト実行)、定期的にテスト実行をトリガーする必要もあります。そのためには、CI/CD パイプラインが最適です。柔軟性があり、タスクの構成が複雑ではないからです。テストを定期的に実行するだけでなく、パイプラインを使用して、各展開後にテストを自動化し、パフォーマンスが良好であることを確認することもできます。


Loadero で WebRTC ソリューションの監視を開始するには、3 つの部分からなるセットアップが必要です。


  1. WebRTC アプリのパフォーマンスを評価する Loadero テスト。 ここで例を見つけることができます。
  2. Loadero APIを使用してテストを開始し、結果を取得して失敗を通知するスクリプト。
  3. スクリプトを自動的に実行する CI/CD パイプライン。

監視設定のテストの設定

監視セットアップ例の Loadero テストをセットアップすることから始めましょう。いくつかのパフォーマンス メトリクスを確認するために起動できる Loadero のテストが既にある場合は、この部分をスキップして、パイプラインを介したテストの起動に関する部分にジャンプできます。 Loadero でテストを行うのが初めての場合は、Loadero でテストを作成する方法に関する完全なステップバイステップ ガイドがこのブログ投稿にあります。すでに別のサービスで WebRTC パフォーマンス テストを行っている場合は、テストを Loadero に移行する方法については、こちら を参照してください。私たちの場合、「Javascript + Nightwatch」テストを行います。そのシナリオは、 Jitsiでの 1 分間の 1 対 1 通話です。


テストでは、2 人の参加者が通話に参加して 1 分間留まり、追加の接続確認のために途中でいくつかのスクリーンショットを撮ります。


参加者は米国オレゴン州から接続し、最新の Google Chrome バージョン (このブログの執筆時点では 109v) を使用し、デフォルトのビデオ + オーディオを使用して出力信号をシミュレートします。


Loadero の実行後のアサーションも使用します。これにより、 「平均 FPS ≥ 10 の場合は合格」など、テストで WebRTC および/またはマシン メトリックの「合格」基準を指定できます。実行が完了すると、アサート結果が参加者ごとに自動的に計算され、指定された値が合格基準を満たしているかどうかが確認されます。参加者のアサーションが失敗した場合、結果レポートのこの参加者のステータスも「失敗」になります。


アサートを使用して、オーディオとビデオ、および FPS の着信および発信ビットレートとパケットを評価します。


テストの構成は次のようになります。


  • テストモード:「パフォーマンステスト」
  • 増分戦略: 「線形参加者」
  • 開始間隔: 1 秒
  • 参加者のタイムアウト: 5 分



Loadero テスト構成


この例では、Javascript + Nightwatch で記述された Loadero テスト スクリプトがありますが、Java + TestUI または Python + Py-TestUI でも同じことができます。


 client => { client // Open the page .url(`https://meet.jit.si/LoaderoTests`) // Wait until the username field is visible // And enter the username .waitForElementVisible('[placeholder]', 30 * 1000) .sendKeys('[placeholder]', 'User') // Wait until the "Join" button is visible // And join the call by pressing the button .waitForElementVisible('[aria-label="Join meeting"]', 10 * 1000) .click('[aria-label="Join meeting"]') // Another thing you can do is to take screenshots during the test // Which could help you to identify the cause of a failure // And give visual feeback about the test run .takeScreenshot('pre_call_screenshot.png') // Stay in the call for half a minute .pause(30 * 1000) // Take a mid-call screenshot .takeScreenshot('mid_call_screenshot.png') // Stay in the call for another half a minute .pause(30 * 1000) // Take a post-call screenshot .takeScreenshot('post_call_screenshot.png'); }


WebRTC パフォーマンスの期待値の設定

各 WebRTC アプリケーションは異なり、パフォーマンスもわずかに異なります。ここでは、たとえそれが夜間に発生したとしても、パフォーマンスの期待が満たされない場合にすぐに通知されるようにするしきい値を定義することを目指しています。これを行うには、WebRTC メトリクスに対する一連の Loadero の実行後のアサーションを使用します。これにより、WebRTC パフォーマンス メトリクスの値が期待するほど良くない場合、テスト実行全体が失敗します。したがって、目標値を狭いマージンに設定するべきではありませんが、テストが理想よりもわずかに悪い場合がありますが、妥当なパフォーマンスの範囲内であることを許容する必要があります。ここで設定した値は例であり、アプリケーションの仕様に基づいて調整する必要がある場合があります (たとえば、ネットワーク帯域幅よりも高いフレーム レートを優先する場合は、表示する最小フレーム レートを上げて下げることができます)。ビットレート制限)。開始点として、以下のアサート リストを使用できます。


実行後のアサーションのセットアップ


以下は、このサンプル テスト用に設定したアサーションとその値のリストです。


  • 着信ビデオ ストリームと発信ビデオ ストリームの両方で、通話時間の 75% を超える時間で、ビデオに 1 秒あたり少なくとも 10 フレームがあることをアサートします。さらに、標準偏差にアサーションを設定して、フレームレートの変動が小さく、毎秒 2 フレーム以下であることを確認します。
    • webrtc/video/fps/out/25th >= 10

    • webrtc/video/fps/in/25th >= 10

    • webrtc/video/fps/out/stddev < 2

    • webrtc/video/fps/in/stddev < 2


  • パフォーマンスが最適であることを確認するには、1 秒あたりに送信されるパケットの値を確認することが重要です。 1 秒あたりの送信パケット数が多すぎると、オーバーヘッドが増えますが、より多くのパケットを送信することで、パケット間の遅延が小さくなるため、ジッターを下げることができます。適切なバランスを見つける必要があり、アプリケーションごとに異なる場合があります。この例では、1 秒あたりの送信パケット数が 40 から 100 の間であるかどうかを確認します。
    • webrtc/audio/packets/out/avg > 40/sec

    • webrtc/video/packets/out/avg > 100/sec

    • webrtc/audio/packets/in/avg > 40/sec

    • webrtc/video/packets/in/avg > 100/sec


  • ビットレートは、1 秒間に送信されるデータの量を示します。一般に、高品質のメディアが送信されるほど、ビットレートが高くなります。ただし、一部のアプリケーションは、ビットレートを最小限に抑えて、悪いネットワークでより適切に動作し、データの消費を抑えようとします。これらのアサートを使用して、テスト期間の 95% で、データ消費量が着信および発信オーディオで 25 kbit/秒、着信および発信ビデオで 1000 kbit/秒の設定値を超えないことを確認します。
    • webrtc/audio/bitrate/out/95th <= 25 kbit/sec
    • webrtc/video/bitrate/out/95th <= 1000 kbit/sec
    • webrtc/audio/bitrate/in/95th <= 25 kbit/sec
    • webrtc/video/bitrate/in/95th <= 1000 kbit/sec

テスト参加者の構成とテストの実行

最後に、テスト参加者を構成する必要があります。通話に参加する同じ構成の 2 人の参加者を持つ参加者の 1 つのグループがあります。参加者の設定は次のとおりです。

  • タイトル – 「参加者」
  • カウント – 2
  • 計算ユニット: G2
  • ブラウザ – 「最新の Google Chrome」
  • 場所 – 「米国西部 – オレゴン」
  • ネットワーク – 「デフォルトのネットワーク設定」
  • 音声フィード – 「デフォルトの音声フィード」
  • 動画フィード – 「デフォルトの動画フィード」


ヒント:まったく同じ構成の多数の参加者が必要な場合は、構成メニューでCountの値を増やします。時間を節約するために、異なる構成が必要な場合にのみ参加者を個別に作成することをお勧めします。


テスト参加者のセットアップ


それになるテスト構成の場合。ただし、テストを定期的に実行する予定であるため、監視セットアップの構成に進む前に、テストを実行し、テスト実行中に参加者が取った Selenium ログとスクリーンショットをチェックして、スクリプトに問題がないことを確認します。 Loadero でテストを初めて起動する場合、または正しく構成されているかどうかわからない場合は、このブログ投稿を使用して、テストを起動する準備ができているかどうかを確認してください


Selenium ログのテスト実行結果

テスト参加者が撮影したスクリーンショット


前述のように、WebRTC メトリクス アサーションが失敗し、成功率が 100% ではない場合、参加者は失敗する可能性があります。これは私たちのテストでも起こりました。


試運転終了の様子


これは必ずしもテストに問題があることを意味するわけではなく、アプリが設定したアサーション基準を満たしていないことを意味するだけです。ただし、アサーション セットではなく、別の理由でテストが失敗した場合は、このブログ投稿でテストをデバッグするいくつかの方法について説明します。


設定したアサートは、テストするアプリケーションに合わせたものではなく、単なる一般的なベースラインです。リストを使用してこれらの値から開始することもできますが、テストするアプリが異なる可能性があり、メトリックの結果も大きく異なる可能性があります。


ヒント:独自のアサーションを設定する 1 つの良い方法は、5 回のテストを実行し、参加者の指標を見て、妥当な目標を評価することです。


これらのアサーションを設定し、結果を分析してアサーションが失敗した理由を突き止めることは、それ自体が複雑なタスクであるため、このブログ投稿では詳しく説明しません。また、アサーションの失敗が原因でテストが失敗したという事実は、テストが失敗し、失敗に関する通知が送信される場合に必要なケースを正確にシミュレートするため、そのままにしておきます。 Selenium ログにエラーが発生していないことが示され、必要なすべてのアクションが実行されたことがスクリーンショットで確認された場合は、テストを続行できます。

パイプラインから定期的にテストを起動する

私たちのチームは、 JSLoadero の Python クライアントを使用して、開発パイプラインにテストを統合する方法に関するいくつかのブログ投稿を既に準備しています。したがって、ここではそれらに依存します。私たちのセットアップでは、Python、GitHub、およびその CI/CD 実装を使用するブログ投稿で提案されているものを使用します – ワークフローと Loadero Python クライアント。


注: 一部の情報はスキップされる可能性があります。詳細な手順については、元のLoadero Python ブログ投稿を参照してください。


ワークフローには、次のものが必要です。

  • GitHub リポジトリ
  • ワークフローを定義する YAML ファイル
  • Loadero テストを実行して結果を報告する Python スクリプト


GitHub で新しいリポジトリを作成するか、既存のリポジトリを使用します。


.github/workflowsディレクトリのリポジトリで、 notify-on-fail.ymlファイルを作成します。これは、環境をセットアップして Loadero テストを起動する方法に関する指示が含まれるファイルです。


notify-on-fail.ymlでトリガーを指定して、ワークフローの定義を始めましょう。


 on: schedule: - cron: '0 9-18 * * *' workflow_dispatch:


scheduleスケジュールされた時間にワークフローをトリガーできます。したがって、テスト実行の頻度を定義する場所です。この例では、テストを午前 9 時から午後 6 時まで 1 時間ごとに実行するようにスケジュールを設定しました。これは、チームが障害に対応できる可能性が最も高い時間だからです。昼夜サイクル全体でテストを実行する必要がある場合は、4 時間ごとに実行することをお勧めします。したがって、誰も起きていなくてもアプリを監視できます。そのscheduleここで学習できる特定の構文を使用することに注意してください。


ヒント: テストの頻度を設定するときは、テストが互いに干渉する可能性があるため、テストの実行間隔をテストの期間よりも長くする必要があることを考慮してください。


2 番目のトリガー ( workflow_dispatch ) では、必要に応じて GitHub Web アプリケーションを介してパイプラインを手動でトリガーできます。


スクリプトのjobsセクションで、使用する環境とすべての依存関係を指定します。私たちの目的には、 Loadero Python ブログ投稿の構成が完全に適合するため、ためらわずにコピーして貼り付けてください。


 jobs: notify-on-fail: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: "3.10" - run: pip install loadero-python - run: python run.py env: LOADERO_ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} LOADERO_PROJECT_ID: ${{ secrets.PROJECT_ID }} LOADERO_TEST_ID: ${{ secrets.TEST_ID }}


重要:行にあることに注意してください- run: python run.py 、 python の後に、リポジトリのルートに相対的なrun.pyファイルへのパスがあります。私の場合、ファイル構造は次のようになります。

パイプライン ファイル構造

もう 1 つ注目すべき点は、資格情報です。まず、Loadero でテスト ID とプロジェクト ID を見つけることができますが、プロジェクト API アクセス トークンも必要になります。現在、プロジェクト アクセス トークンは、サポート チームにリクエストすることで取得されます。次に、資格情報は GitHub Actions シークレットとして使用されます。これにより、Loadero アクセス トークンが非公開になります。シークレットは、リポジトリ設定 -> セキュリティ -> シークレットと変数 -> アクション -> 新しいリポジトリ シークレットで構成できます。


シークレットに関する詳細な手順については、前述のブログ投稿を参照してください。


したがって、現時点でのワークフロー構成は次のようになります。


 name: Notify about failing tests in Loadero on: schedule: - cron: '0 9-18 * * *' workflow_dispatch: jobs: notify-on-fail: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: "3.10" - run: pip install loadero-python - run: python run.py env: LOADERO_ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} LOADERO_PROJECT_ID: ${{ secrets.PROJECT_ID }} LOADERO_TEST_ID: ${{ secrets.TEST_ID }}


それでは、Loadero と対話する Python スクリプトをセットアップに追加しましょう。繰り返しになりますが、 Loadero Python ブログのスクリプトは出発点として優れているため、これを使用して後で必要に応じて変更します。


 import os from loadero_python.api_client import APIClient from loadero_python.resources.test import Test project_id = os.environ.get("LOADERO_PROJECT_ID", None) access_token = os.environ.get("LOADERO_ACCESS_TOKEN", None) test_id = os.environ.get("LOADERO_TEST_ID", None) if project_id is None or access_token is None or test_id is None: raise Exception( "Please set the " "LOADERO_PROJECT_ID and LOADERO_ACCESS_TOKEN AND LOADERO_TEST_ID " "environment variables." ) APIClient( project_id=project_id, access_token=access_token, ) run = Test(test_id=test_id).launch().poll() print(run) for result in run.results()[0]: print(result) if run.params.success_rate != 1: raise Exception("Test failed")

失敗したテストに関する通知の送信

ここで、通知を実装するために Python スクリプトを変更します。この例では、アラートは Discord チャンネルに送信されます。通知を実装する方法は完全にオプションですが、個人の好みに依存するためです。


requests ライブラリ、 ResultStatusおよびAssertStatusクラスをインポートして、Python ファイルを更新しましょう。


 import requests import os from loadero_python.api_client import APIClient from loadero_python.resources.test import Test from loadero_python.resources.classificator import ResultStatus, AssertStatus


また、YAML ファイルを更新して要求ライブラリをインストールします。


 - run: pip install loadero-python - run: pip install requests - run: python run.py


パイプラインが正しく構成されておらず、いずれかの資格情報の値が None である場合は、エラー メッセージを含む POST 要求を送信して、Discord チャネルに通知する必要があります。


 missing_credentials_message = ( "Please set the " "LOADERO_PROJECT_ID and LOADERO_ACCESS_TOKEN AND LOADERO_TEST_ID " "environment variables." ) def send_notification(message): requests.post( "https://discordapp.com/api/webhooks/{id}", data={"content": message}, ) if project_id is None or access_token is None or test_id is None: send_notification(missing_credentials_message) raise Exception(missing_credentials_message)


テストが失敗した場合は、その理由を知りたいです。ここで、参加者の確認を追加します。参加者が失敗した場合、次の構造のエラー メッセージを送信します。

  • 参加者名
  • セレンの結果
  • スターテス
  • 失敗した assert のパス
  • 実行状況


さらに、ここでは過剰なので、最初のスクリプトにあった例外を取り除く必要があります。


 run_failure_message = "" for result in run.results()[0]: result.params.run_id = run.params.run_id result.read() if ( result.params.selenium_result.value != ResultStatus.RS_PASS or result.params.status.value != ResultStatus.RS_PASS ): run_failure_message += ( f"{result.params.participant_details.participant_name}:\n" f"-Selenium result: {result.params.selenium_result.value}\n" f"-Participant status: {result.params.status.value}\n" ) if result.params.asserts: run_failure_message += "-Failing asserts:\n" for assertion in result.params.asserts: if assertion.status != AssertStatus.AS_PASS: run_failure_message += f"--{assertion.path.value}\n" run_failure_message += "\n" run_failure_message += f"Run status: {run.params.status.value}" if run.params.success_rate != 1: send_notification(run_failure_message)


また、テストの実行が成功したことを示すメッセージを送信しましょう。


 if run.params.success_rate != 1: send_notification(run_failure_message) else: send_notification(f"The {run.params.test_name} test has been finished successfully")


最終的な検証として、Loadero API との接続に問題がある場合に備えて、API 呼び出し全体をtry-exceptにラップする必要があります。最終的な python スクリプトは、全体として次のようになります。


 import os import requests from loadero_python.api_client import APIClient from loadero_python.resources.test import Test from loadero_python.resources.classificator import ResultStatus, AssertStatus project_id = os.environ.get("LOADERO_PROJECT_ID", None) access_token = os.environ.get("LOADERO_ACCESS_TOKEN", None) test_id = os.environ.get("LOADERO_TEST_ID", None) missing_credentials_message = ( "Please set the " "LOADERO_PROJECT_ID and LOADERO_ACCESS_TOKEN AND LOADERO_TEST_ID " "environment variables." ) def send_notification(message): requests.post( "https://discordapp.com/api/webhooks/{id}", data={"content": message}, ) if project_id is None or access_token is None or test_id is None: send_notification(missing_credentials_message) raise Exception(missing_credentials_message) try: APIClient( project_id=project_id, access_token=access_token, ) run = Test(test_id=test_id).launch().poll() print(run) run_failure_message = "" for result in run.results()[0]: result.params.run_id = run.params.run_id result.read() if ( result.params.selenium_result.value != ResultStatus.RS_PASS or result.params.status.value != ResultStatus.RS_PASS ): run_failure_message += ( f"{result.params.participant_details.participant_name}:\n" f"-Selenium result: {result.params.selenium_result.value}\n" f"-Participant status: {result.params.status.value}\n" ) if result.params.asserts: run_failure_message += "-Failing asserts:\n" for assertion in result.params.asserts: if assertion.status != AssertStatus.AS_PASS: run_failure_message += f"--{assertion.path.value}\n" run_failure_message += "\n" run_failure_message += f"Run status: {run.params.status.value}" if run.params.success_rate != 1: send_notification(run_failure_message) else: send_notification( f"The {run.params.test_name} test has been finished successfully" ) except Exception as err: send_notification(f"Error while running Loadero test: {err}")


WebRTC モニタリング結果の検査

これで、テストは午前 9 時から午後 6 時まで 1 時間ごとに自動的に実行され、アプリが期待どおりに動作するかどうかが評価されます。


テストの実行が終了した後、テストに失敗した場合は、失敗について通知されます。


このケースでは、FPS とパケットが基準を満たしていないために Jitsi テストが失敗しました。これは、テスト結果の [Asserts] タブで確認できます。アサートの結果には、すべての参加者が個別にアクセスできるため、問題がすべての参加者に発生したのか、一部のみに発生したのかを確認できます。


テスト実行結果で実行結果をアサートします

上記の assert の値は、最初の参照として使用できますが、私たちの場合、それらは Jitsi の目標パフォーマンスと一致していないようです。そのため、監視プロセスが最適であることを確認するために、アプリのパフォーマンスとアプリに最適なアサーションを調べることを躊躇しないでください。


注: テストのアサートを見るだけで、テストの実行中にゼロを出力するセクション全体があり、最終的にテスト結果に影響を与えることがわかります。


ヒント:テストが失敗した場合は、WebRTC 統計タブに移動して、データを含むさまざまなグラフを見つけたり、失敗の原因となったメトリックに関する詳細情報を取得したりできます。


このブログ投稿では、Loadero と GitHub ワークフローを利用して WebRTC アプリケーションを監視する方法の例を提供しました。監視を使用すると、アプリケーションで発生する可能性のある問題に対処するための準備が整います。アプリケーションのパフォーマンスをより全体的に把握するために、さまざまな条件で他のシナリオを作成することも賢明です。このような監視の設定は、非常に複雑な作業になる可能性があります。 Loadero チームに同様の監視設定を作成してもらいたいですか? [email protected]までお気軽にお問い合わせください。WebRTC ソリューションを定期的かつ自動的に監視する方法について話し合いましょう。


ここにも掲載されています。