執筆者 Vishal Sood
IIS 詳細ログを使用すると、Web プラットフォームが拡張されてリアルタイム分析がサポートされ、顧客にリアルタイム レポートを提供したり、同じ作業をパートナーと連携して行ったりできます。 詳細ログ機能には、ログ エントリをリアルタイムで使用できるオプションが含まれています。 各要求の間に送信されるすべてのイベントが集計され、publishLogEvent ログ定義プロパティにより、他のアプリケーションで使用するリアルタイム イベントを発生させるかどうかが制御されます。
要件
- この記事は開発者を対象としており、基本的なネイティブ コード作成のスキルを前提としています。
- IIS パイプラインについて理解しておく必要はありません。ただし、「参照」セクションの記事を参照し、使用されているメソッドとデータ構造を確認しておくと役に立つ場合があります。
- IS 詳細ログは、インターネット インフォメーション サービス (IIS) 7 の拡張機能であり、使用できなくなりました。 IIS 8.5 の拡張ログをお勧めします。
リアルタイム ログの有効化
リアルタイム ログは、IIS 詳細ログのログ定義ごとに設定します。 ログ定義のリアルタイム ログを有効にするには、以下の操作を行います。
IIS マネージャーで、詳細ログ機能を開きます。 [接続] ウィンドウでサーバーをクリックし、[ホーム] ページの [詳細ログ] アイコンをダブルクリックします。
詳細ログ機能を有効にします。 [アクション] ウィンドウで、[詳細ログを有効にする] をクリックします。
リアルタイム ログを有効にするログ定義を選択します。
- IIS マネージャーで、サーバー、Web サイト、ディレクトリ、またはアプリケーション レベルで詳細ログ機能を開きます。
- [詳細ログ] 機能のページで、ログ定義をクリックし、[アクション] ウィンドウで、[ログ定義の編集] クリックします。
[リアルタイム イベントを発行する] チェック ボックスをオンにして、選択したログ定義のイベントのリアルタイム ログを有効にします。
次のセクションの説明に従い、イベントをリアルタイムで使用する IIS モジュールを記述します。
リアルタイム イベントを使用する IIS モジュールの記述
詳細ログ機能によって発生したリアルタイム イベントをログするには、IIS モジュールを作成する必要があります。 このセクションでは、IIS トレース インフラストラクチャについて確認し、参照用に使用できる簡単なモジュールを作成するためのサンプル コードを圧縮 (zip 形式) フォルダーで提供します。
Note
サンプル コードは、メモリ リークやその他の問題についてテストされておらず、参照用としてのみ使用すること意図しています。
IIS トレース インフラストラクチャ
このセクションでは、リアルタイム イベントのログに使用される IIS トレースの概念について説明します。
IGlobalTraceEventProvider::GetTraceEvent メソッド
詳細ログにより発生するリアルタイム イベントを使用するには、作成した IIS モジュールをグローバル イベントに登録する必要があります。 OnGlobalTraceEvent メソッドは、システムによって発生するすべてのイベントに対して呼び出す必要があります。 これを登録すると、リアルタイム ログ イベントにアクセスできます。 詳細については、「CGlobalModule::OnGlobalTraceEvent メソッド」を参照してください。
データ構造
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;
};
この構造体の詳細については、「HTTP_TRACE_EVENT 構造体」を参照してください。
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;
};
この構造体の詳細については、「HTTP_TRACE_EVENT_ITEM 構造体」を参照してください。
pProviderGuid
HTTP_TRACE_EVENT 構造体には、pProviderGuid プロパティ (プロバイダー識別子を含む LPCGUID) が含まれています。 その重要性を理解しておくことは重要です。
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;
}
サンプル コード では、この値を使用して不要なイベントをフィルター処理します。
サンプル コード
このセクションでは、この記事の前で説明したリアルタイム ログの概念を示すサンプル コードを示します。 CAnalyticsGlobalModule.zip (圧縮 (zip 形式) フォルダーのサンプル コードのコピー) をダウンロードしてください。
Note
サンプル コードは、メモリ リークやその他の問題についてテストされておらず、参照用としてのみ使用すること意図しています。
CAnalyticsGlobalModule::OnGlobalTraceEvent
//
// {3C729B22-F9A9-4096-92A4-07E0DDF403EB}
//
static const GUID _LOGGING_PUBLISHING_GUID =
{ 0x3c729b22, 0xf9a9, 0x4096, { 0x92, 0xa4, 0x7, 0xe0, 0xdd, 0xf4, 0x3, 0xeb } };
//
// This call is happening on the same thread (synchronous/blocking call) as
// the call to RaiseTraceEvent, so bail a.s.a.p. if this isn't something
// we want to handle, and minimize the work we do here
//
//
GLOBAL_NOTIFICATION_STATUS
CAnalyticsGlobalModule::OnGlobalTraceEvent(
__in IGlobalTraceEventProvider * pProvider)
{
HRESULT hr = S_OK;
IHttpContext * pHttpContext = NULL;
HTTP_TRACE_EVENT * pTraceEvent = NULL;
DBG_ASSERT(pProvider != NULL
//
// We only want to handle trace events that are raised for
// logging purposes, so bail a.s.a.p. if this event isn't
// for us
//
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;
}
//
// We now need the HTTP context which is used to get the site info later
//
hr = pProvider->GetCurrentHttpRequestContext(&pHttpContext
if (FAILED(hr))
{
TRACEHR(hr
goto Finished;
}
ProcessLogEvent(pTraceEvent, pHttpContext
Finished:
return GL_NOTIFICATION_CONTINUE;
}
ProcessLogEvent
ProcessLogEvent メソッドは、ログ データをローカル データ構造にコピーします。これは後で、Web サービスまたはデータベースにデータをプッシュするために使用できます。
Note
クライアントへの応答が遅くなる可能性があるため、要求内のデータそのものを処理しないでください。
Note
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;
}
//
// Copy the name of this event item
//
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 there's no data to copy, mark it empty
//
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;
}
//
// At this point, you've copied the event into your memory and can now process your copy, queue it, etc.
//
// WriteEventViewerLog(pHttpTraceEvent->pszEventName); // Can write to eventViewer log to verify that event is processed...
Finished:
return;
}
まとめ
このチュートリアルでは、IIS 詳細ログ機能でのリアルタイム ログの動作と、簡単な IIS モジュールを作成してログ データをリアルタイムで使用する方法について確認しました。