IIS 7.0 用 Advanced Logging - リアルタイム ログ

作成者 : Vishal Sood
発行日 : 2009 年 3 月 18 日 (作業者 : dmnelson(英語))
更新日 : 2009 年 5 月 12 日 (作業者 : dmnelson(英語))

インターネット インフォメーション サービス (IIS) 7.0 用 Advanced Logging では、ユーザー要求とクライアント データを非常に柔軟に記録できます。Advanced Logging 拡張機能は、IIS マネージャーのユーザー インターフェイスまたは IIS 7.0 構成システムと連携可能なツールで管理できます。

この記事の内容 :

  • 概要
  • 要件
  • リアルタイム ログについて
  • リアルタイム イベントを使用する IIS モジュールの作成
  • まとめ
  • リファレンス

概要

IIS 7.0 用 Advanced Logging には以下の機能が含まれており、顧客に適切なサービスを提供できるように支援します。

  • クライアント ログ。Advanced Logging は、クライアント側の利用指標を受け取る機能を IIS に組み込みます。これにより、顧客が好む、適切かつ重要なユーザー エンゲージメントの指標が提供されます。
  • 集中型のログ。Advanced Logging は、クライアント側の指標の収集および記録を行う中央ログ ファームを設定する機能を備えています。これにより、分散されたログの収集に必要な労力が削減されます。またログ収集が統合されたことで、より効率的に後処理を行うことができます。
  • ログのフィルタリング。Advanced Logging は、要求ごとに複数のログ (各ログには、ログの目的に関連するデータが含まれます) を作成することにより、特定の目的に関連する情報をフィルタリングする機能を備えています。たとえば、あるログにはサービスの品質 (QoS) に関するデータ、別のログには分析に関するデータが含まれます。現在のソリューションを使用して情報の抽出が必要な後処理は、すべて不要になります。
  • リアルタイム ログ。Advanced Logging は、プラットフォームを拡張してリアルタイム分析をサポートします。これにより、顧客にリアルタイムなレポートを提供したり、パートナーと連携して同様のことを行うことができます。
  • 豊富/柔軟なログ。Advanced Logging では、ユーザーにとって重要なことを決め付けることはしません。代わりに、十分な制御を提供して、ユーザーにとって重要なフィールドを指定したり、その他のフィールドを簡単に追加したり、またログ ロールオーバーや要求フィルターに関するポリシーを提供したりすることができます。このような機能は、現在存在しません。今は、管理コンソールによる簡単な構成で、HTTP 要求/応答ヘッダー、IIS サーバー変数、およびクライアント側のフィールドを記録できます。

要件

  • この記事の対象者は、基本的なネイティブ コードの作成技術を持つ開発者です。
  • IIS パイプラインの知識は必要ありません。ただし IIS パイプラインの知識があると、リファレンスにある記事を参照し、使用されるメソッドおよびデータ構造について学ぶ際に役立ちます。

リアルタイム ログについて

IIS 7.0 用 Advanced Logging には、ログ エントリのリアルタイムな使用を可能にするオプションがあります。Advanced Logging プラットフォームでは、この目的のために、十分に証明された IIS インフラストラクチャを使用します。

Advanced Logging は、各要求中に送信されたすべてのイベントを集めます。ログ定義プロパティ publishLogEvent は、他のアプリケーションによる使用に対してリアルタイム イベントが生成されるかどうかを制御します。

この Advanced Logging 機能を使用するには、次の手順を実行します。

  1. IIS マネージャーで、[Publish real-time events] チェック ボックスをオンにして、イベントのリアルタイム ログを有効にします。
    Ee175733.Advanced Logging for IIS fig 1(ja-jp,TechNet.10).jpg
  2. 以下のセクションで説明されているように、リアル タイムにイベントを使用する IIS モジュールを作成します。

リアルタイム イベントを使用する IIS モジュールの作成

IIS 7.0 用 Advanced Logging によって生成されるリアルタイム イベントを記録するには、IIS モジュールを作成する必要があります。このセクションでは、IIS トレース インフラストラクチャについて確認し、リファレンスとして使用できる簡単なモジュールを作成するためのサンプル コードを圧縮 (zip 形式) フォルダーで提供します。

   このサンプル コードは、メモリ リークやその他の問題についてテストされていませんので、リファレンスとしてのみ使用してください。

IIS トレース インフラストラクチャ

このセクションでは、リアルタイム イベントの記録に使用される IIS トレースの概念をいくつか説明します。

IGlobalTraceEventProvider::GetTraceEvent メソッド

Advanced Logging によって生成されたリアルタイム イベントを使用するには、作成した IIS モジュールをグローバル イベントに登録する必要があります。OnGlobalTraceEvent メソッドは、システムによってイベントが生成されるたびに呼び出されます。これに登録すると、リアルタイム ログ イベントにアクセスできます。詳細については、「IIS 7.0: CGlobalModule::OnGlobalTraceEvent Method (英語) を参照してください。

データ構造

HTTP_TRACE_EVENT 構造

HTTP_TRACE_EVENT 構造は、リアルタイム ログ インフラストラクチャの重要要素を形成します。リアルタイム ログ情報は、この構造の形式で渡されます。

struct HTTP_TRACE_EVENT{
   LPCGUID pProviderGuid;
   DWORD dwArea;
   LPCGUID pAreaGuid;
   DWORD dwEvent;
   LPCWSTR pszEventName;
   DWORD dwEventVersion;
   DWORD dwVerbosity;
   LPCGUID pActivityGuid;
   LPCGUID pRelatedActivityGuid;
   DWORD dwTimeStamp;
   DWORD dwFlags;
   DWORD cEventItems;
   __field_ecount(cEventItems) HTTP_TRACE_EVENT_ITEM * pEventItems;
};

この構造の詳細については、「IIS 7.0: HTTP_TRACE_EVENT Structure (英語)」を参照してください。

HTTP_TRACE_EVENT_ITEM 構造

HTTP_TRACE_EVENT 構造は、ログが生成された際のログ定義に含まれるログ フィールド数に応じて、1 つまたは複数の HTTP_TRACE_EVENT_ITEM 構造を含みます。

struct HTTP_TRACE_EVENT_ITEM{
   LPCWSTR pszName;
   HTTP_TRACE_TYPE dwDataType;
   PBYTE pbData;
   DWORD cbData;
   LPCWSTR pszDataDescription;
};

この構造の詳細については、「IIS 7.0: HTTP_TRACE_EVENT_ITEM Structure (英語)」を参照してください。

pProviderGuid

HTTP_TRACE_EVENT 構造には、プロバイダーの ID を含む LPCGUID である、pProviderGuid プロパティが含まれます。その重要性を理解してください。

CAnalyticsGlobalModule::OnGlobalTraceEvent に記述されているように、OnGlobalTraceEvent は、システムによってイベントが生成されるたびに呼び出されます。つまり、着信イベントから不要なイベントをフィルタリングし、対象のイベント (リアルタイム ログ イベント) のみを利用できるようにする必要があります。これは pProviderGuid プロパティ値の 3C729B22-F9A9-4096-92A4-07E0DDF403EB を使用すると実現できます。

//
// {3C729B22-F9A9-4096-92A4-07E0DDF403EB}
//
static const GUID _LOGGING_PUBLISHING_GUID = 
{ 0x3c729b22, 0xf9a9, 0x4096, { 0x92, 0xa4, 0x7, 0xe0, 0xdd, 0xf4, 0x3, 0xeb } };
………………
………………
    
if ((pTraceEvent->pProviderGuid != &_LOGGING_PUBLISHING_GUID) &&
        (!IsEqualGUID(*(pTraceEvent->pProviderGuid), _LOGGING_PUBLISHING_GUID)))
{
    goto Finished;
}

サンプル コードでは、この値を使用して不要なイベントをフィルタリングします。

サンプル コード

このセクションでは、この記事で既に述べたリアルタイム ログの概念を説明するサンプル コードを示します。圧縮 (zip 形式) フォルダーのサンプル コード CAnalyticsGlobuleModule.zip (英語) をダウンロードしてください。

   このサンプル コードは、メモリ リークやその他の問題についてテストされていませんので、リファレンスとしてのみ使用してください。

CAnalyticsGlobalModule::OnGlobalTraceEvent

//
// {3C729B22-F9A9-4096-92A4-07E0DDF403EB}
//
static const GUID _LOGGING_PUBLISHING_GUID = 
{ 0x3c729b22, 0xf9a9, 0x4096, { 0x92, 0xa4, 0x7, 0xe0, 0xdd, 0xf4, 0x3, 0xeb } }; 
//
//  この呼び出しは、RaiseTraceEvent 呼び出しと同じスレッド (同期/ブロック呼び出し)
//  で行われます。そのため、この呼び出しを処理しない場合は、
//  できるだけ早く終了し、ここで行う作業を最小限にします。
//
//    
GLOBAL_NOTIFICATION_STATUS
CAnalyticsGlobalModule::OnGlobalTraceEvent(
    __in IGlobalTraceEventProvider  *  pProvider)
{
    HRESULT                 hr              = S_OK;
    IHttpContext *          pHttpContext    = NULL;
    HTTP_TRACE_EVENT *      pTraceEvent     = NULL;    DBG_ASSERT(pProvider != NULL);    //
    //  ログ目的で生成されたトレース イベントのみを処理します。
    //  そのため目的のイベントでない場合は、できるだけ早く終了します。
    // 
    //
    hr = pProvider->GetTraceEvent(&pTraceEvent);
    if (FAILED(hr))
    {
        TRACEHR(hr);
        goto Finished;
    }    if (pTraceEvent->pProviderGuid == NULL)
    {
        TRACEMSG(SS_DEFAULT, 
                 TRACE_LEVEL_INFORMATION, 
                 L"Not handling trace event - NULL value for provider GUID");
        goto Finished;
    }    if ((pTraceEvent->pProviderGuid != &_LOGGING_PUBLISHING_GUID) &&
        (!IsEqualGUID(*(pTraceEvent->pProviderGuid), _LOGGING_PUBLISHING_GUID)))
    {
        goto Finished;
    }    //
    //  後でサイト情報を取得するために使用する HTTP コンテキストが必要です。
    //
    hr = pProvider->GetCurrentHttpRequestContext(&pHttpContext);
    if (FAILED(hr))
    {
        TRACEHR(hr);
        goto Finished;
    }
    ProcessLogEvent(pTraceEvent, pHttpContext);
Finished:
    
    return GL_NOTIFICATION_CONTINUE;    
} 

ProcessLogEvent

ProcessLogEvent メソッドは、ログ データをローカル データ構造にコピーするため、後からそのローカル データ構造を使用して、データを Web サービスまたはデータベースにプッシュできます。

   要求自体のデータは処理されません。それによって、クライアントに対する要求の応答が遅くなる場合があるためです。

   ProcessLogEvent のコードでは、イベントによって使用されるメモリが AllocateRequestMemory によって割り当てられた一時的なメモリであるかもしれないことに留意する必要があります。スレッドのブロックを解除する場合は、データをコピーする必要があります。

    void ProcessLogEvent(
        __in    HTTP_TRACE_EVENT *  pHttpTraceEvent,
        __in    IHttpContext     *  pHttpContext)
    {
        HRESULT                     hr = S_OK;
        DWORD                       cchName = 0;
        HTTP_TRACE_EVENT *          pNewHttpTraceEvent  = NULL;
        LPCSTR                      pszHostName     = NULL;

        pNewHttpTraceEvent = new HTTP_TRACE_EVENT;
        if (pNewHttpTraceEvent == NULL)
        {
            goto Finished;
        }        pNewHttpTraceEvent->pEventItems = new HTTP_TRACE_EVENT_ITEM[pHttpTraceEvent->cEventItems];
        if (pNewHttpTraceEvent->pEventItems == NULL)
        {
            goto Finished;
        }        ZeroMemory(pNewHttpTraceEvent->pEventItems, sizeof(HTTP_TRACE_EVENT_ITEM) * pHttpTraceEvent->cEventItems);
        
        for (DWORD ix = 0; ix < pHttpTraceEvent->cEventItems; ix++)
        {
            if (pHttpTraceEvent->pEventItems[ix].pszName == NULL)
            {
                pNewHttpTraceEvent->pEventItems[ix].pszName = NULL;
                pNewHttpTraceEvent->pEventItems[ix].cbData = 0;
                pNewHttpTraceEvent->pEventItems[ix].pbData = NULL;
                continue;
            }
            //
            //  このイベント項目の名前をコピーします。
            //            cchName = wcslen(pHttpTraceEvent->pEventItems[ix].pszName);
            pNewHttpTraceEvent->pEventItems[ix].pszName = new WCHAR[cchName + 1];
            if (pNewHttpTraceEvent->pEventItems[ix].pszName == NULL)
            {
                goto Finished;
            }            memcpy((VOID *)pNewHttpTraceEvent->pEventItems[ix].pszName, pHttpTraceEvent->pEventItems[ix].pszName, (cchName+1) * sizeof(WCHAR));            //
            //  コピーするデータがない場合は、空としてマークします。
            //           
            if ((pHttpTraceEvent->pEventItems[ix].cbData == 0) ||
                (pHttpTraceEvent->pEventItems[ix].pbData == NULL))
            {
                pNewHttpTraceEvent->pEventItems[ix].cbData = 0;
                pNewHttpTraceEvent->pEventItems[ix].pbData = NULL;
                continue;
            }            pNewHttpTraceEvent->pEventItems[ix].pbData = new BYTE[pHttpTraceEvent->pEventItems[ix].cbData];
            if (pNewHttpTraceEvent->pEventItems[ix].pbData  == NULL)
            {
                goto Finished;
            }            memcpy(pNewHttpTraceEvent->pEventItems[ix].pbData, pHttpTraceEvent->pEventItems[ix].pbData, pHttpTraceEvent->pEventItems[ix].cbData);
            pNewHttpTraceEvent->pEventItems[ix].cbData = pHttpTraceEvent->pEventItems[ix].cbData;
            pNewHttpTraceEvent->pEventItems[ix].dwDataType = pHttpTraceEvent->pEventItems[ix].dwDataType;
        }        //
        //  この時点でイベントはメモリにコピーされたため、コピーされたイベントの処理、問い合わせなどを行うことができます。
        ////        WriteEventViewerLog(pHttpTraceEvent->pszEventName);   // イベントが処理されているかどうかを確認するために、eventViewer ログに書き込むことができます。    Finished:
        return;
    }

まとめ

このチュートリアルでは、Advanced Logging 機能のリアルタイム ログがどのように動作するかについて、またリアルタイムにログ データを使用する方法について、簡単な IIS モジュールを作成して検証しました。

リファレンス

関連コンテンツ

記事