チュートリアル: IIS レポートの作成

公開日: 2008 年 5 月 21 日 (作業者: SinghGurpreet (英語))

更新日: 2008 年 5 月 28 日 (作業者: SinghGurpreet (英語))

はじめに

IIS レポートは、独自のレポート セットを作成して表示できる非常に強力なツールに進化しました。基本的なレポート セットが付属しているだけではなく、独自のレポートを作成し、プラグインとして IIS Reports プラットフォームで使用できます。このチュートリアルでは、基本的なレポートの作成方法について説明します。このレポートでは、aspx ページとヒット数を比較します。レポートの作成は、UI モジュールの作成と同じ手順に従います。単純な UI モジュールを作成する手順については、http://learn.iis.net/page.aspx/441/understanding-ui-extension-authoring/ を参照してください。

IIS レポートを作成する手順は、次の 2 つの部分に分けることができます。

  • サーバー部分の作成
  • クライアント部分の作成

サーバー部分の作成は、次の 2 つの部分で構成されます。

  • ModuleProvider の作成
  • ModuleService の作成

クライアント部分の作成は、次の 3 つの部分で構成されます。

  • モジュールの作成
  • レポート機能の作成
  • レポートの作成

まず、サーバー部分を作成し、その後でクライアント部分を作成します。

モジュール プロバイダーの作成

この手順は、Inetmgr UI 用のモジュール プロバイダーの作成とよく似ています。

internal class TestModuleProvider : SimpleDelegatedModuleProvider {

        public override ModuleDefinition GetModuleDefinition(IManagementContext context) {
            return new ModuleDefinition(Name, "TestIisReportsClient.TestModule, " + AssemblyRef.TestReportClient);
        }

        public override Type ServiceType {
            get {
                return typeof(TestModuleService);
            }
        }

        public override bool SupportsScope(ManagementScope scope) {
            return (scope == ManagementScope.Server || scope == ManagementScope.Site);
        }

        public override string FriendlyName {
            get {
                return "IIS Reports Test";
            }
        }
    } 

TestModuleProvider は SimpleDelegatedModuleProvider から派生しています。これにより、このモジュールでは、サーバー管理者がサイト管理者にレポートを委任できます。

モジュール サービスの作成

TestModuleService は ModuleService から派生し、モジュール サービス プロキシや WMSVC で呼び出すことができる GetReport メソッドを公開します。内部的には、受け取った引数は ExecuteLogParserQuery に渡されます。ExecuteLogParserQuery は、LogParser を使用してクエリを解析し、出力を生成します。生成された ILogRecordSet は、オブジェクト配列にパックされ、クライアントに送信されます。TestModuleService は、GetFromClause() と GetCurrentSite() を使用して、解析する必要があるファイルのパスを取得します。

internal class TestModuleService : ModuleService {

        [ModuleServiceMethod(PassThrough = true)]
        public object[] GetReport(IDictionary arguments) {
            object[] dataObject = null;

            try {
                dataObject = ExecuteLogParserQuery(arguments);
            }
            catch (Exception ex) {
                RaiseException(ex.Message);
            }
            return dataObject;
        }

        private string GetQueryString(IDictionary arguments) {
            string endDate = ((DateTime)arguments["ToDate"]).ToString("yyyy-MM-dd");
            string startDate = ((DateTime)arguments["FromDate"]).ToString("yyyy-MM-dd");
            string from = GetFromClause();
            return "Select TOP 25 TO_LOWERCASE (cs-uri-query) as Site, Count(*) as hits from" + from
                + " where Site is not null and date BETWEEN TO_TIMESTAMP(\'" + startDate + "\',\'yyyy-MM-dd\') AND TO_TIMESTAMP(\'" + endDate + "\',\'yyyy-MM-dd\') group by Site order by Hits desc";
        }

        private object[] ExecuteLogParserQuery(IDictionary arguments) {
            string query = GetQueryString(arguments);
            object[] data = new object[2];
            ILogRecordset rs = ExecuteQuery(query);

            int columnCount = rs.getColumnCount();
            object[] headers = new object[columnCount];
                for (int i = 0; i < columnCount; i++) {
                headers[i] = rs.getColumnName(i);
            }
            data[0] = headers;

            // Add the results
            ArrayList rows = new ArrayList();
            while (!rs.atEnd()) {
                object[] row = new object[columnCount];
                ILogRecord record = rs.getRecord();
                for (int i = 0; i < columnCount; i++) {
                    object value = record.getValue(i);
                    if (value is DBNull) {
                        value = String.Empty;
                    }

                    row[i] = value;
                }

                rs.moveNext();
                rows.Add(row);
            }

            //Adding rows to object Array
            data[1] = rows;
            return data;
        }

        private ILogRecordset ExecuteQuery(string query) {
            LogQueryClass logParser = new LogQueryClass();
            ICOMIISW3CInputContext ctx = new COMIISW3CInputContextClassClass();

            return logParser.Execute(query, ctx);
        }

        private string GetFromClause() {
            Site site = GetCurrentSite();
            if (site == null) {
                ServerManager mgr = GetServerManager();
                StringBuilder sb = new StringBuilder();
                bool notFirst = false;
                foreach (Site s in mgr.Sites) {
                    if (notFirst) {
                        sb.Append(',');
                    }
                    notFirst = true;
                    sb.Append(GetFromClauseForSite(s));
                }
                return sb.ToString();
            }
            return GetFromClauseForSite(site);
        }

        private ServerManager GetServerManager() {
            return ManagementUnit.ReadOnlyServerManager;
        }

        private string GetFromClauseForSite(Site s) {
            return "'" + Path.Combine(Environment.ExpandEnvironmentVariables(s.LogFile.Directory), "W3SVC" + s.Id.ToString()) + "\\*.log'";
        }

        private Site GetCurrentSite() {
            if (ManagementUnit.ConfigurationPath.PathType == ConfigurationPathType.Site) {
                ServerManager mgr = GetServerManager();
                Site site = mgr.Sites[ManagementUnit.ConfigurationPath.SiteName];

                return site;
            }

            return null;
        }
    } 

モジュールの作成

IIS レポートを生成するには、モジュールが必要です。各モジュールには複数のレポート機能を含めることができ、各レポート機能には複数のレポートを含めることができます。また、各レポートには複数のレポート フィルターを含めることができます。説明を簡単にするために、レポート機能が 1 つであるモジュールを作成し、レポート機能にはレポートが 1 つだけ含まれるようにします。このレポートには、To Date と From Date の 2 つのフィルターがあり、指定した 2 つの日付の間のデータを選択できるようにします。

TestModule は IExtensibilityManager を取得し、TestReportingFeature を拡張機能マネージャーに登録します。IIS Reports モジュールは、実行時に IExtensibilityManager に拡張機能 ReportingFeature を照会し、レポートを表示します。

[プロジェクト] メニューの [参照の追加] をクリックし、[参照] タブで \Windows\system32\inetsrv ディレクトリを検索して、Microsoft.Web.Management.IisReports.Client.dll への参照を追加します。

internal class TestModule : Module {

        private ReportingFeature testReportingFeature;

        protected override void Initialize(IServiceProvider serviceProvider, Microsoft.Web.Management.Server.ModuleInfo moduleInfo) {
            base.Initialize(serviceProvider, moduleInfo);

            IExtensibilityManager extensibilityManager =
                (IExtensibilityManager)serviceProvider.GetService(typeof(IExtensibilityManager));
            Debug.Assert(extensibilityManager != null, "Extensibility Manager is null");

            testReportingFeature = new TestReportingFeature(this);

            if (extensibilityManager != null) {
                extensibilityManager.RegisterExtension(typeof(ReportingFeature), testReportingFeature);
            }
        }
    } 

このデモンストレーション用に使用しているレポート定義は ChartReportDefinition です。IIS Reports では、3 種類のレポート定義を使用できます。最も高度なレポート定義である ChartReportDefinition では、データのグラフとテーブルが作成され、TableReportDefinition ではデータのテーブルのみが表示されます。TestReportDefinition は、日付に基づいてデータをフィルター処理する FromDate と ToDate という 2 つのフィルターを公開します。TestReportDefinition には、内部でサービス プロキシおよびモジュール サービスを呼び出してオブジェクト配列を取得する GetData メソッドがあります。次に、オブジェクト配列からデータテーブルを作成します。このデータテーブルは、グラフやテーブルを表示するために IIS Reports で使用されます。

internal class TestReportDefinition : ChartReportDefinition {
        private ReportFilter[] _filters;

        public TestReportDefinition(ReportingFeature feature)
            : base(feature, "TestReportName", "Test Report Name", ReportCategory.WebServer) {
            ReportFilter filter1 =
                new ReportFilter("FromDate", "From Date", DataType.DateTime, DateTime.Now.Subtract(new TimeSpan(30, 0, 0, 0)), true);
            ReportFilter filter2 =
                new ReportFilter("ToDate", "To Date", DataType.DateTime, DateTime.Now, true);

            _filters = new ReportFilter[] { filter1, filter2 };
        }

        public override string XValueColumn {
            get {
                return "Site";
            }
        }

        public override IList<string> YValueColumns {
            get {
                return new string[] { "Hits" };
            }
        }

        public override IList<string> Columns {
            get {
                return new string[] { "Site", "Hits" };
            }
        }

        public override IEnumerable<ReportFilter> Filters {
            get {
                return _filters;
            }
        }

        protected override DataTable GetData(IDictionary<string, object> arguments) {
            Hashtable filters = new Hashtable();
            foreach (KeyValuePair<string, object> filter in arguments) {
                filters.Add(filter.Key, filter.Value);
            }

            object[] data = ((TestReportingFeature)Feature).ServiceProxy.GetReport(filters);
            object[] row;
            ArrayList rows = (ArrayList)data[1];
            DataTable table = new DataTable();
            table.Columns.Add("site", typeof(string));
            table.Columns.Add("hits", typeof(int));

            for (int i = 0; i < rows.Count; i++) {
                DataRow tableRow = table.NewRow();
                row = (object[])rows[i];
                tableRow["site"] = (string)row[0];
                tableRow["hits"] = (int)row[1];

                table.Rows.Add(tableRow);
            }
            return table;
        }
    } 

ServiceProxy の作成

internal class TestReportServiceProxy : ModuleServiceProxy {
        public object[] GetReport(IDictionary arguments) {
            return (object[])Invoke("GetReport", arguments);
        }
    } 

その他

ClassesInterop.cs

Logparser は COM オブジェクトを返すので、interop.cs が必要です。このファイルは、Interop.cs からダウンロードできます。

AssemblyRef.cs

このクラスは、クライアント モジュールを読み込むためにモジュール プロバイダーによって使用されます。

internal static class AssemblyRef {
        private static string testReportClient;

        internal static string TestReportClient {
            get {
                if (testReportClient == null) {
                    AssemblyName assemblyName = typeof(AssemblyRef).Assembly.GetName();
                    string assemblyFullName = assemblyName.FullName;
                    testReportClient = assemblyFullName.Replace(assemblyName.Name, "TestIisReportsClient");
                }

                return testReportClient;
            }
        }
    }