ケース スタディ: Web サイトに Application Insights を追加する

このポストは、10 月 5 日に投稿された Case Study: Adding Application Insights to a website の翻訳です。

 

マイクロソフトはこのたび、GetGlimpse.com (英語) Web サイトへの Application Insights の追加作業をサポートしました。このオープン ソース プロジェクトの作業過程の一部を皆さんと共有したいと思います。今回は、Application Insights を導入する一般的な手順の概要と、ドキュメント化されていないいくつかの手法をご紹介します。

Glimpse の開発者にとっては、信頼性が高く高速で、ユーザーのニーズを満たす Web サイトの状態を常に維持することが重要です。そのため、Application Insights を追加してサイトのパフォーマンスや利用状況を監視し、次回リリース準備の意思決定に役立てようと考えています。

GetGlimpse.com (英語) は ASP.NET MVC アプリケーションです。サイトのソース コードは GitHub に格納されており、Azure Web アプリとしてデプロイされます。

 

プロセス

まず Application Insights へ Web アプリを追加する標準的な手順からご説明しましょう。Glimpse のサイトの場合、その後にいくつか追加手順が必要になります。

  1. Azure Web アプリの設定からのインストルメンテーション キーの読み込み
  2. JavaScript の読み込みの最適化
  3. 不要なモジュールの削除
  4. テレメトリへのビルド バージョンのタグ付け
  5. すべての例外レポートの追加

これらの手順を解説し、その後、まとめと作業から得た知識についてお伝えします。

 

Azure Web アプリの設定からのインストルメンテーション キーの読み込み

Application Insights のインストルメンテーション キーは、テレメトリに対して適切なリソースを指定して、テレメトリを表示できるようにするものです。通常、ApplicationInsights.config ファイル内に記述され、アプリケーションがその内容を取得します。

しかし、Azure Web アプリの場合は、インストルメンテーション キーを Azure Web アプリの環境変数から読み込むことができます。今回は、デプロイメントが異なる場合にインストルメンテーション キーを上書きできるよう許可しています。

string ikeyValue = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY");

if (!string.IsNullOrEmpty(ikeyValue))
{
   TelemetryConfiguration.Active.InstrumentationKey = ikeyValue;
}

この機能を有効にするには、Azure Application Insights 拡張機能を Web アプリに追加するか、または Web サイトの設定に手動でインストルメンテーション キーを設定します。

 

JavaScript の読み込みの最適化

クライアント側でデータを収集するには、JavaScript コードをページに挿入する必要があります。一般的には、下記のようにコードをサイト内のすべての Web ページの冒頭 (通常はマスター ページ) に挿入します。

 Views/Shared/_Layout.cshtml.

しかし、このコードはサイズが大きく (約 1 KB)、各ページをダウンロードする際のオーバーヘッドを減少させるために、JavaScript ファイルを独立させ、ブラウザーでキャッシュして各ページで利用できるようにしました。これにより、ページの読み込み時間が短縮されます。

 

新しい JavaScript ファイルを作成して Application Insights のスニペットに格納する

スクリプト ファイル (英語) には、Application Insights の [Quick Start] の下にある [Get code to monitor my web page] ブレードから取得できるコード スニペットが含まれていますが、後半部分に 1 か所変更されているところがあります。

 }({
    instrumentationKey: window.instrumentationKey
});

インストルメンテーション キーをハードコーディングする代わりに、これを window.instrumentationKey で置換し、コーディングを 1 か所のみにします。

 

サーバーからインストルメンテーション キーを設定する

ASP.NET Web ページは実行時に生成されるため、インストルメンテーション キーはサーバー SDK から設定できます。Razor 構文を使用して (他のフレームワークでもこれを提供する必要があります)、_Layout (英語) (マスター) ページでキーを設定します。

  <script language="javascript">
        window.instrumentationKey = "@Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.Active.InstrumentationKey";
    </script>
    @Scripts.Render("~/bundles/headsitejs")

ASP.NET では Scripts.Render 行でバンドル機能 (英語) を使用し、複数のスクリプトの読み込みに最適化しています。

 

スクリプト ファイルにバンドルを定義する

最後に、新しいスクリプト バンドルを AppStart/BundleConfig.cs (英語) に追加してコード ファイルを読み込みます。

   bundles.Add(new ScriptBundle("~/bundles/headsitejs").Include(
                "~/Scripts/ApplicationInsightsSnippet.js"));

 

メリット

インストルメンテーション キーを 1 か所で設定できるようにしたことで、更新作業が簡潔になりました。1 KB 程度のページ サイズ削減は効果がわかりづらいですが、大規模な Web サイトでの積算量は大きくなります。Application Insight の JavaScript コードをキャッシュすることによるページ サイズの削減効果は、十分あると考えられます。

 

不要なモジュールの削除

ApplicationInsights.config (英語) には、このアプリケーションでは使用しないテレメトリ初期化子が含まれています。これは Azure クラウド サービスで使用される初期化子ですが、Azure Web アプリとしてデプロイされるこのアプリケーションでは不要です。

 <Add Type="Microsoft.ApplicationInsights.WindowsServer.AzureRoleEnvironmentTelemetryInitializer, Microsoft.AI.WindowsServer"/>

また、パフォーマンス カウンター モジュールも Azure Web アプリには対応していないため削除しました。

これらのモジュールを削除することでパフォーマンスを向上させることができます。

 

テレメトリへのビルド バージョンのタグ付け

アプリケーションの更新が頻繁にあると、例外や他のテレメトリの調査時に、データのバージョンがわからなくなることがあります。Application Insights ではプロパティとしてビルド バージョンをすべてのテレメトリに追加できるため、プロパティを確認したりフィルターを適用したりできます。

generate buildinfo.config (英語) では MSBuild を使用した場合のみ情報を取得できます。メインの csproj ファイルには次のコードが追加されています。

 
 <PropertyGroup>   <GenerateBuildInfoConfigFile> true </GenerateBuildInfoConfigFile> 
 <IncludeServerNameInBuildInfo> true </IncludeServerNameInBuildInfo>   </PropertyGroup> 

ビルド情報が含まれている場合、Application Insights の Web モジュールは自動的にビルド バージョンをプロパティとしてテレメトリのすべてのアイテムに追加します。このため、診断検索メトリックの把握の際に各バージョンのフィルターを適用することができます。

ビルド番号が正しく表示されるのは、MSBuild で Web アプリをビルドした場合のみです。Visual Studio のローカル ビルド プロセスでビルドした場合、バージョン プロパティにはプレースホルダーが設定されます。

 

すべての例外レポートの追加

Application Insights で例外レポートを生成するには、Web アプリにコードを追加する必要があります。この方法はアプリの種類によって異なり、Glimpse の Web API の場合はプロジェクトのソリューションに 4 つのファイルを変更または追加する必要があります (英語)

ApplicationInsightsErrorAttribute.cs (英語) で定義されている属性では、適用されている任意のクラスの例外をレポートします。

 using System;
using System.Web.Mvc;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.DataContracts;

namespace Glimpse.Site.Telemetry
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public class ApplicationInsightsErrorAttribute : HandleErrorAttribute
    {
        private TelemetryClient client = new TelemetryClient();

        public override void OnException(ExceptionContext filterContext)
        {
            if (filterContext != null && filterContext.HttpContext != null && filterContext.Exception != null)
            {
                //customError が Off の場合は AI HTTPModule で例外がレポートされる
                if (filterContext.HttpContext.IsCustomErrorEnabled)
                {
                    ExceptionTelemetry exc = new ExceptionTelemetry(filterContext.Exception);
                    exc.HandledAt = ExceptionHandledAt.Platform;
                    client.TrackException(exc);
                }
                base.OnException(filterContext);
            }
        }
    }
}

注: Glimpse.site では既にエラー処理が実装されています。このエラー処理と連動するように、ドキュメントの推奨事項に従って ExceptionFilterAttribute または ExceptionLogger の代わりに HandleErrorAttribute が使用されています。

FilterConfig.cs ( 英語 ) には ApplicationInsightsErrorAttribute フィルターが追加されています。これにより、効率的にすべてのクラスに属性を適用できます。

 using System.Web.Mvc;
using Glimpse.Site.Telemetry;

namespace Glimpse.Site
{
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new ApplicationInsightsErrorAttribute());
        }
    }
}

Glimpse.Site.csproj ( 英語 ) には新たに ApplicationInsightsErrorAttribute.cs ファイルが追加されました。

 <Compile Include="Telemetry\ApplicationInsightsErrorAttribute.cs" />

 

結果

Glimpse サイトの開発者は、Application Insights により有益なデータを入手することができました。結果として Glimpse サイトは正常に運用され、多くのユーザーに快適なエクスペリエンスが提供されます。Glimpse 開発者はサイトが安定し動作が良好であると感じており、Application Insights でも証明されています。しかし、Glimpse サイト内の大規模なコミュニティは常に進化を続けており、Glimpse サイトに既に存在していないページへの外部サイトからのリンクがあることに注意する必要があります。

Glimpse チームは無効サイトへのリンクを把握していなかったため、このようなリンクが原因の多数のエラーが Application Insights により収集されることを予測していませんでした。これらの無効リンクは、大半の Glimpse サイト ユーザーに影響はありませんが、ごく一部のユーザーにとってはエクスペリエンスの品質低下につながります。このエクスペリエンスをさらに改善するために、マイクロソフトでは発生数の多い例外の優先順位を決定しました。ここではその例を説明します。

 

ファイルが見つからない

System.IO.FileNotFoundException Could not find file 'D:\home\site\wwwroot\Views\Docs\Wiki\Content\Custom-Runtime-Policy.md'

この例外は重大なエラーのように思えますが、詳細に分析したところ、これは無効リンクによることがわかりました。リンク先のファイルの名前が変更され、古いドキュメントにアクセスを試みた場合に発生します。

 

キーが見つからない

System.Collections.Generic.KeyNotFoundException

この例外は、Glimpse のバージョンをアップグレードしたときに発生することがわかりました。PowerShell スクリプトを使用してアップグレードした場合、サーバーが想定していないクエリ文字列を NuGet パッケージが作成し、不正と判断されます。このため、NuGet パッケージはアップグレード元およびアップグレード先のバージョンを正しく取得できません。

 

Window.onerror に関するエラー

Error: Object doesn't support this property or method

このエラーは Windows Media Center の IE8 で window.onerror がサポートされていないために発生することがわかりました。通常はこのエラーは発生しません。

 

CORS スクリプト エラー

ブラウザーの同一生成元ポリシーでは、異なる生成元から読み込んだスクリプトでの例外情報の取得を制限しています。クロスドメイン エラー レポートでは、適切な CORS HTTP ヘッダーを指定してクロスオリジン属性を使用できます。詳しくは https://www.w3.org/TR/cors/ (英語) を参照してください。

JavaScript エラーが発生しても、セキュリティ ポリシーによりその情報が十分に得られない場合があります。この問題は、例外の発生原因のスクリプトがホストされているドメインが、例外の発生箇所のドメインとは異なる場合に発生します。セキュリティ上の制限により、このようなエラーでは Application Insights で詳細情報を表示することができません。ただし、この例外が発生するのは特定の 1 つのスクリプトだけであり、ほとんど発生する機会がないので優先順位は低く設定されています。

Glimpse サイトに Application Insights を追加すると、Glimpse 開発者はテレメトリを使用できるようになり、そのデータを利用状況やパフォーマンスの改善に活用できます。その結果、多くのユーザーがサイトで完全なエクスペリエンスを得られるため、非常に良い結果となります。この分析は、Nik Molnar (英語) および Anthony van der Hoorn (英語) の協力により行われました。2 人に感謝の意を表します。

 

まとめ

Glimpse に Application Insights を追加することにより、有効性が高いテレメトリを使用できるようになります。このプロセスで使用した手法を参考に、Application Insights のエクスペリエンスをさらにリッチに活用していただけますと幸いです。