WebAssemblyアプリケーションの監視には、その設計と実行環境に起因する固有の課題があります。監視ツールをJavaScriptやドキュメントオブジェクトモデル(DOM)に直接接続できる従来のウェブアプリケーションとは異なり、WebAssemblyではブラウザのサンドボックス内で実行されるバイナリコードとして実行されます。従来の監視ツールはWebAssemblyの低レベルの操作と対話するように設計されていないため、この抽象化レイヤーにより直接的な監視を困難にしています。Bytecode Allianceは、WebAssemblyのセキュリティと使いやすさを強化することを目的とした標準とツールを推進しており、オブザーバビリティのサポートの向上など、重要な役割を果たしています。さらに、WebAssemblyはネイティブの速度に近いパフォーマンス特性を保つため、ユーザー体験に影響を与えないように、非常に効率的で影響が最小限の監視ソリューションが必要です。これにより、パフォーマンスを犠牲にすることなくアプリケーションの動作を詳細に可視化する必要がある開発者にとって、複雑なシナリオが生まれます。

Blazor WebAssembly:ブラウザでの.NETの活用

.NET Blazor WebAssemblyは、開発者がJavaScriptではなく.NETを使用して、対話型のクライアント側Web UIを構築できるようにする最先端のフレームワークです。Blazor WebAssemblyは、C#コードをWebAssemblyにコンパイルすることで、開発者がサーバー側とクライアント側の両方で同じ言語とライブラリを利用して、.NETのフルスタック機能を活用できるようにします。この独自のアプローチにより、開発プロセスが合理化され、パフォーマンスが大幅に向上し、豊かで応答性の高いユーザー体験が実現します。.NET 8のリリースにより、Blazor WebAssemblyでは、さまざまなデプロイメントシナリオにわたって柔軟性とパフォーマンスを強化する新しいレンダリングモードが導入されました。これらのモードには次のものが含まれます。

  • 静的サーバーレンダリング(静的サーバーサイドレンダリングまたは静的SSRとも呼ばれる)は、サーバー上で静的HTMLを生成します。
  • インタラクティブサーバーレンダリング(インタラクティブなサーバーサイドレンダリングまたはインタラクティブSSRとも呼ばれる)は、サーバー上で事前レンダリングされたインタラクティブなコンポーネントを生成します。
  • インタラクティブWebAssemblyレンダリング(クライアント側レンダリングまたはCSRとも呼ばれ、常にインタラクティブであると想定)は、サーバー上で事前レンダリングを行い、クライアント上でインタラクティブなコンポーネントを生成します。
  • インタラクティブ自動(オート)レンダリング では、コンテンツのレンダリングとインタラクティブ機能のために最初にサーバー側のASP.NET Coreランタイムを使用します。Blazorバンドルがダウンロードされ、WebAssemblyランタイムがアクティブ化された後、クライアント上の.NET WebAssemblyランタイムは、後続のレンダリングとインタラクティブ機能に使用されます。インタラクティブな自動レンダリングは通常、最速のアプリ起動エクスペリエンスを実現します。

特に自動レンダリングモードにより、Blazor WebAssemblyは.NETテクノロジーを使用して最新のウェブアプリケーションを構築しようとしている開発者にとって、さらに魅力的な選択肢になります。

このブログ記事では、特にBlazor WebAssemblyに焦点を当て、その機能と最新のウェブ開発における実用的なアプリケーションについて説明します。

.NETのOpenTelemetryによるBlazor WebAssemblyオブザーバビリティの強化

この Blazor WebAssemblyの調査では、OpenTelemetry(OTelと呼ばれることもある)を.NETと統合して、これらのアプリケーションに包括的なオブザーバビリティを提供する方法について詳しく説明します。OpenTelemetryは、API、ライブラリ、エージェント、計装のセットであり、開発者はトレース、メトリクス、ログなどのテレメトリーデータを収集およびエクスポートして、アプリケーションのパフォーマンスと健全性を分析できます。.NET開発者にとって、OpenTelemetryとのインテグレーションは特にシームレスです。.NETのすべてのテレメトリーは安定していると考えられているため、さまざまなデプロイメントシナリオにわたって信頼性と堅牢なサポートが保証されます。

Blazor WebAssemblyのサンプルアプリケーションを探る

このブログ記事では、Blazor WebAssemblyの原則とOpenTelemetryを組み合わせたサンプルアプリケーションを使用します。このアプリケーションは、GitHubリポジトリにあり、独立したBlazor WebAssemblyアプリケーションの優れた例として機能します。この実用的な例は、.NET環境でのOpenTelemetryの実装と監視に関する説明の基礎となります。このアプリケーションを分析することで、ブラウザで実行されるBlazor WebAssemblyアプリケーションとしてのBlazorのクライアント側コンポーネントと、ウェブフロントエンドが通信するBlazor WebAssemblyバックエンドアプリケーション間のインタラクションをより深く理解できます。

バックエンドの自動および手動計装

OpenTelemetryをBlazor WebAssemblyアプリケーションにデプロイする場合、特に.NETバックエンドとインターフェースするときに、自動計装が重要なコンポーネントになります。OpenTelemetryの.NETライブラリは、ASP.NET Core用のすぐに使用できる計装を提供し、HTTPリクエスト、データベースクエリなどのテレメトリーデータを簡単にキャプチャします。この自動化されたプロセスでは、手動による設定とコーディングが最小限で済むため、包括的な監視を実装するタスクが簡素化されます。さらに、OpenTelemetryをプロジェクトに統合するのは、それぞれのNuGetパッケージをソリューションに追加するのと同じくらい簡単です。

Blazor WebAssemblyを使用する開発者にとって、これはアプリケーションを強化するバックエンド操作の可視性が向上することを意味します。自動化された計装により、クライアントとサーバー間のすべての関連データトランザクションが細部まで監視され、パフォーマンスメトリクスと潜在的なボトルネックに関するインサイトが提供されます。この機能を活用することで、オブザーバビリティインフラストラクチャの設定と維持の複雑さが軽減され、開発者が機能の構築に集中できるようになり、高パフォーマンスで信頼性の高いアプリケーションをより簡単に提供できるようになります。

.NET Blazor WebAssemblyバックエンドのプロジェクトファイルは次のようになります(完全なプロジェクトファイルは リポジトリ内にあります)。

'''dotnet '''

自動化されたOpenTelemetry計装を使用してBlazorバックエンドを実行し、テレメトリーをコンソールにエクスポートすると、コンソール出力(ここではVS Codeターミナル)の一部としてトレース、メトリクス、ログを簡単に確認できます。

しかし、コンソール出力でトレース、メトリクス、ログなどのテレメトリーを調べても、あまり役に立ちません。したがって、理想的には、そのデータをOpenTelemetryテレメトリーバックエンドにエクスポートしたいと考えています。私はもちろんバックエンドにNew Relicを使用しています。

OpenTelemetryの設定を行う際の一般的な方式として、アプリケーションの実行時にいくつかの環境変数を指定するだけで済みます。以下のスクリーンショットでは最も重要な設定を強調表示しました。完全な実行スクリプトはリポジトリにあります。

アプリケーションが起動して実行されると、APM & ServicesのOpenTelemetryセクションにアプリケーションエンティティが表示されます。以下のスクリーンショットは 概要 での表示を示しています。

分散トレーシング での表示

単一のトレースの表示( /roles エンドポイントへのバックエンドスパンの強調表示):

上記のBlazorWASMBackend サービスの GET /roles へのスパンを見ると、バックエンドスパンで費やされた合計時間が4.61秒であることがわかりますが、時間が費やされた場所を正確に知ることはできません。自動計装を使用する場合、そのメソッドで実際に何が起こっているかについての詳細はわかりません。

このメソッドについてさらに詳しい情報を得るには、OpenTelemetry SDK for .NETを活用して、ソースコードに手動の計装を追加する必要があります。

このケースでは、app.MapGet("/roles", … メソッドの元のコードを変更します。

元のコード:

           // システム提供の値をシードとして使用して乱数ジェネレーターをインスタンス化します。
           var rand = new Random();
           int waitTime = rand.Next(500, 5000);

                    // do some heavy lifting work
           Thread.Sleep(waitTime);

カスタムOpenTelemetryスパンの.NET固有の実装をいくつか追加:

           // システム提供の値をシードとして使用して乱数ジェネレーターをインスタンス化します。
           var rand = new Random();
           int waitTime = rand.Next(500, 5000);

           using (var sleepActivity = DiagnosticsConfig.ActivitySource.StartActivity("RolesHeavyLiftingSleep"))
           {
               // do some heavy lifting work
               Thread.Sleep(waitTime);


               string waitMsg = string.Format(@"ChildActivty simulated wait ({0}ms)", waitTime);
               sleepActivity?.SetTag("simulatedWaitMsg", waitMsg);
               sleepActivity?.SetTag("simulatedWaitTimeMs", waitTime);
               DiagnosticsConfig.logger.LogInformation(eventId: 123, waitMsg);
           }

usingブロックでは RolesHeavyLiftingSleep という名前で新しいスパンの作成をトリガーし、アクティビティを sleepActivity 変数に保存します。さらに、sleepActivity?.SetTag()を呼び出して、同じアクティビティにいくつかのカスタムタグも追加していることに気付くでしょう。

アプリケーションを再起動した場合、その変更の結果がどのようになるかは、以下のスクリーンショットで確認できます。

上記のスクリーンショットのトレースでは、進行中のスパンを有効にすることで、バックエンドスパンをさらに深く掘り下げていることが確認できます。ここでは、RolesParentActivity があり、その後に RolesChildActivity が続き、最後に手動計装からの RolesHeavyLiftingSleep スパンがあることがわかります。このスクリーンショットには、そのスパンに関連付けられている属性のセクションも表示されます。ご覧のとおり、カスタムタグ simulatedWaitTimeMssimulatedWaitMsg も表示されます。これらのカスタムタグは、根本原因の分析やトラブルシューティングを行うときに役立つ可能性があります。

フロントエンドの計装

OpenTelemetryを使ってバックエンドを計装する方法を確認したので、次はフロントエンド、つまりBlazor WebAssemblyコンポーネントに焦点を当てましょう。

OpenTelemetryの自動計装を使用してWebAssemblyコンポーネントを計装しようとすると、C#プロジェクトファイルがバックエンドと同様に見えます。これは実際にBlazow WebAssemblyの利点であり、バックエンドだけでなくWebAssemblyフロントエンドにもC#を使用できるということです。

OpenTelemetryテレメトリーのために、コンソールエクスポーターを再度設定してみましょう。当然のことながら、出力結果はそれぞれブラウザの開発者ツールに表示されます。

上のスクリーンショットでは、上部にBlazor WebAssemblyフロントエンドの実際のUIが表示されています。アプリケーションのユーザーが [Click me] ボタンをクリックすると、トレースが生成され、ブラウザのコンソールビューに出力されます。

繰り返しになりますが、これはテストには十分ですが、実際にテレメトリーを有意義に活用するには、すべてのテレメトリーをOpenTelemetryバックエンド(私の場合はNew Relic)にエクスポートする必要があります。これを実現するには、OpenTelemetryプロトコル(OTLP)エクスポーターを定義し、バックエンドで確認したものと同様のOTLPエンドポイントとOTLPヘッダー情報を設定する必要があります。

しかし、これらの変更を実装してアプリケーションを再実行するとすぐに、例外が生成されます。

例外は、このプラットフォームでは操作がサポートされていません と表示されています。これはWebAssemblyコンポーネントから外部エンドポイントにHTTPリクエストを送信しようとしているためで、理にかなっています。WebAssembly自体は組み込みの入出力(I/O) 機能が無いため、ホスト環境にアクセスすることができません。セキュリティ上の理由から、WebAssemblyコンポーネント内からHTTPリクエストを行うことはできないためです。

では、単純なOpenTelemetry計装が不可能な場合、フロントエンドで何ができるでしょうか?

OpenTelemetryの一環として、コミュニティはリアルユーザーモニタリングの機能にも取り組んでいます。

ただし、これはまだ初期段階であり、仕様が完全には決まっていません。現在のドラフトもNode.jsとTypeScriptのみに焦点を当てています。将来的には、これがフロントエンドコンポーネントに活用できるオプションになる可能性があります。

WebAssemblyコンポーネントの詳細を取得する1つの方法は、New Relic Browserを介してリアルユーザーモニタリングを活用することです。

新しく作成されたNew Relic BrowserアプリケーションからJavaScriptスニペットを取得し、Blazor WebAssemblyプロジェクトにコピーすると(こちらに配置する必要があります)、フロントエンドから高レベルのテレメトリーを確認できます。

分散トレーシング での表示

AJAX リクエスト:

フロントエンドで他に何ができるでしょうか?フロントエンドの一部では、バックエンドとのやり取りが行われます(ログインやログアウトの認証など)。その他の部分は、WebAssemblyコンポーネント内でコードを実行するだけです。一例として、カウンター セクションが挙げられます。

ページのページソースを見るとわかるように、実際のHTML表現はありません。

そこで何ができるか見てみましょう。

1つの方法は、実際の.NETコードからJavaScript関数を呼び出すことです。次のスクリーンショットは、これを実現する方法を示しています(リポジトリ内の それぞれのファイル へのリンクはこちら)。

New Relic Browser APIを使用すると、ユーザーはカスタムページアクションを追加できます。このようにして、カウンターのすべてのクリックを監視し、カウンターの現在の値をカスタム属性として取得することもできます。

これを実装してアプリケーションの新しいリリースをデプロイすると、たとえばカスタムページアクションで追加したデータをカスタムダッシュボードに表示して、アプリケーションのすべてのユーザーにわたる実際のカウンター値の分布を確認できます。

さらに、Instant Observabilityとも呼ばれるNew Relicクイックスタートにはサンプルダッシュボードが含まれており、事前に構築されたダッシュボードをデプロイすることで、でBlazor WebAssembly固有の追加テレメトリーを確認できます。