次の方法で共有


Windows SharePoint Services 3.0 での利用状況のイベント ログ

概要: ログが有効になっている場合に Windows SharePoint Services 3.0 で作成されるログ ファイルを解析して、利用状況のイベント データを効果的に分析します。

Radu Rusu, Microsoft Corporation

Erick R. Lerma, Microsoft Corporation

Les W. Smith, Microsoft Corporation

2007 年 9 月

適用先 : Windows SharePoint Services 3.0

目次 :

  • 利用状況のイベントログの概要

  • 利用状況のログ ファイルの形式の調査

  • 利用状況のイベント ログの解析

  • コード サンプルの試用

  • まとめ

  • 追加情報

利用状況のイベントログの概要

この資料では、Windows SharePoint Services 3.0 から利用状況のイベント データを取得するために最適な方法について説明します。それは、配置でログが有効になっているときに作成されたログ ファイルを解析することです。また、Windows SharePoint Services 3.0 で生成されたログ ファイルの形式に関する情報を提供し、これらのファイルから利用データ抽出する Microsoft Visual C++ アプリケーションの作成方法を例を挙げて説明します。

重要

このドキュメントで提供されるサンプルは、情報提供のみを目的としており、明示または黙示に関わらず、これらの情報についてマイクロソフトはいかなる責任も負わないものとします。このドキュメントのサンプルによるリスクまたは使用による結果はすべて、ユーザーの責任となります。

Windows SharePoint Services 3.0 では、次を経由しても利用状況のイベント データを取得できます。

  • SPWeb クラスの GetUsageData メソッドを実装するマネージ コード

  • GetUsageBlob メソッドをポストする Windows SharePoint Services 3.0 リモート プロシージャ コール (RPC) プロトコル

  • Microsoft Office SharePoint Designer 2007

ただし、これらの各方法は、ログ ファイルの解析と比較すると制限が多くなります。GetUsageBlob RPC メソッド、およびマネージ コードでこのメソッドをポストする Windows SharePoint Services 2.0 サンプルの使用の詳細については、Microsoft ダウンロード センターから Usage BLOB Parser をダウンロードしてください。

注意

SPWeb クラスの GetUsageData メソッドでは、レポートの種類または指定された期間に応じて、異なる形式で利用状況データを返します。ただし、このメソッドには 2000 行までという制限があるので、サイトの利用状況の分析には十分に活用できません。GetUsageBlob メソッドでは、同じデータが返され、行の制限はありません。ただし、このメソッドではデータが扱いやすい形式で返されないので、解析が困難です。Office SharePoint Designer 2007 を使用すると、同じデータが解析され、利用状況が圧縮された形式でまとめられます。ただし、SharePoint Designer クライアントでは、オブジェクト モデルを経由してこのデータを公開することはありません。そのため、返されたデータをサーバー アプリケーションで実用的に使用することはできません。利用状況のデータを返すためのこれらのメソッドは、情報が SharePoint Web サイト単位にしか適用されず、Web アプリケーション単位には適用できないという点で、さらに制限が大きくなっています。これらのデータでは、利用状況の情報が長期間にわたって蓄積されます。したがって必然的に、ヒットのフィールド間の相関関係 (例 : 特定の時間に、どのユーザーがどのページを見たかなど) は保存されなくなります。

Windows SharePoint Services 3.0 では、SharePoint Central Administration の [利用状況の解析処理] ページで [ログを有効にする] が選択されている場合、Web アプリケーションごとに毎日、利用状況のイベント ログが生成されます。ログが有効になっている場合、Windows SharePoint Services では、既定でフロントエンド Web サーバーの Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\Logs パスにログ ファイルが作成されますが、別の場所を指定することもできます。Logs ディレクトリには、Web サーバーの各 Web アプリケーション用のフォルダが含まれています。各 Web アプリケーション フォルダには、各 Web アプリケーションを識別する GUID による名前が付いています。各 Web アプリケーションのフォルダには、1 日単位のサブフォルダが含まれています。つまり、各 Web アプリケーションの毎日の利用状況のログが含まれます。Windows SharePoint Services 3.0 のログの用途としては、各 Web アプリケーション単位の情報を含めることに加えて、ユーザーをページ ヒット数やタイム スタンプに関連付けることもできます。

注意

Windows SharePoint Services 3.0 で生成されたログ ファイルを参照するには、そのファイルを含むコンピュータの管理者 (または管理者を含むけれども管理者に限定されない、STS_WPG グループのメンバ) である必要があります。

IIS ログによる利用状況イベント ログの補完

Windows SharePoint Services 3.0 の利用状況のイベント ログによって提供される情報は、Microsoft インターネット インフォメーション サービス (IIS) で生成されたログによって提供される情報で補完できる場合があります。IIS ログには、サーバーの IP アドレス、要求の種類、要求のポート番号、およびその他の情報が含まれます。IIS ログの詳細については、サイト利用状況のログを記録する (IIS 6.0)を参照してください。

利用状況のログ ファイルの形式の調査

Windows SharePoint Services 3.0 のログ ファイルは、Web アプリケーションで発生したページのヒットごとのエントリで構成されています。各ログ エントリは、バイナリ形式の構造体から始まります。ここでは、フィールドがエントリの後続の各部分に使用されるバイト数を示します。

注意

Windows SharePoint Services 3.0 は、ログ ファイルの最上位に "Windows SharePoint Services HTTP log file" という文字列で始まる 300 バイトの間隔を追加します。この間隔は、悪意のあるスクリプトがファイルに書き込まれることを防ぎます。

次の表は、構造体の各フィールドの名前とデータ型を示し、エントリの各フィールドに含まれる情報を説明します。

表 1. 構造体で表されるフィールド

フィールド名

データの種類

説明

pPrev

uint64

前のエントリをポイントします。

bitFlags

uint8

ヒットの種類を示すフラグです。以下のいずれかの値になります。

0  通常のヒット。

1  Microsoft Office SharePoint Designer クライアント アプリケーションで、参照 (サイトの参照元 URL からであるかどうか) を示すのに使用されます。

2  リストの更新。

4  更新以外のリストの操作 (owssvr.dll へのポストなど)。

8  Internet Explorer の Office Server Extensions (OSE) [ディスカッション] ボタンを使用して実行されるディスカッション要求。

cbEntry

uint16

次のエントリに繰り越すバイト数。

cbSiteUrl

uint16

要求が行われたサイトを含むサイト コレクションの最上位サイトの絶対 URL。例: https://Server/sites/Top_site

cbWeb

uint16

最上位サイトから相対のサイトまたはサブサイトの URL。例 : Subsite_1/Subsite_2/Subsite_3

cbDoc

uint16

参照されたページのサイトの相対 URL。例 : lists/List_Name/allitems.aspx

cBytes

uint32

受信バイト数と送信バイト数を含む、要求で使用される帯域幅。

httpStatus

uint16

IIS ログにあるものと同様の http 状態コード。Windows SharePoint Services 3.0 では、成功したヒットのみがログに記録されるので、値は常に 200 と 299 の間になります (つまり、304、401、または 404 になることはありません)。ほとんどすべての記録されたヒットの http 状態コードは 200 になります。

cbUser

uint16

要求を実行しているユーザー名。例 : DOMAIN\User_Alias

cbRefQS

uint16

適用できる場合、参照元 URL で使用されるクエリ文字列。

cbRef

uint16

適用できる場合、該当ページに移動する直前にユーザーが参照していた URL。要求が実行されたサイトが参照元 URL に含まれる場合、およびブラウザに URL が完全に入力された場合を除きます。

cbUAS

int16

ユーザー エージェント。

cbQS

uint16

適用できる場合、要求 URL で使用されるクエリ文字列。

version

uint32

要求された URL のバージョンを示す ETag。

予約済み

uint16

予約済み。定義は必要ありません。IIS インスタンスの ID。

各ログ エントリは、バイト数を含む構造体を保持することに加えて、次も含みます。

  • エントリを読みやすくするためのキャリッジ リターンとライン フィード (\r\n)。

  • 要求が実行されたサイトの GUID。

  • サーバーのローカル タイム ゾーンで表される要求のタイム スタンプ。

  • それぞれが構造体の各フィールドに対応する値を含む null 終端された文字列。

ログ ファイルの処理時に、Windows SharePoint Services 3.0 により、最上位サイトの URL とサブサイトの URL の間にアンパサンド (&) が挿入されます。この操作により、ログ ファイルが処理済みとしてマークされるので、利用状況の処理ジョブが同じ日に再度実行された場合に、作業データが 2 回数えられることを防止できます。たとえば、だれかが昼間に処理時刻を 01:00 (午前 1 時) から 11:00 (午後 11 時) に変更した場合に、前日のログが 2 回数えられることはありません。

利用状況のイベント ログの解析

利用状況のイベント ログは簡単に読み取れますが、ファイルの解析とサイトの利用状況に関する情報を読み取りやすい形式で提供するツールを使用することもできます。

Visual C++ または Microsoft Visual C# にあるようなログを生成するサーバーで実行される、利用状況のログを再処理するツールを作成できます。ツールでは、データベースへの出力や, .csv ファイルへの出力など、クエリを実行するためにさまざまな形式で出力を提供できます。

ツールでは、各ログ エントリの前に含まれる、次の形式の構造体を読み取る必要があります。

typedef struct _VLogFileEntry
{
    unsigned long long pPrev; /* Pointer to previous entry (8 bytes)*/
    unsigned char bitFlags; /* Flags describing the current log entry (1 byte)*/
    unsigned short cbEntry; /* Number of bytes to skip ahead to next entry (2 bytes)*/
    unsigned short cbSiteUrl; // (2 bytes)
    unsigned short cbWeb; // (2 bytes)
    unsigned short cbDoc; // (2 bytes)
    unsigned long cBytes; /* Bandwidth consumed (bytes in + bytes out) (4 bytes)*/
    unsigned short      httpStatus;// (2 bytes)
    unsigned short      cbUser;    // (2 bytes)
    unsigned short      cbRefQS;   // (2 bytes)
    unsigned short      cbRef;     // (2 bytes)
    short               cbUAS;     // (2 bytes)
    unsigned short      cbQS;      // (2 bytes)
    unsigned long       version;   // (4 bytes)
    unsigned short      reserved;  // (2 bytes)
} VLogFileEntry;

/*Note: The bitFlags field is a bitwise mask of the following values:
0x00000000 - Generic request
0x00000001 - List update request
0x00000010 - List non update request
0x00000100 - Discussion request
0x00001000 - Windows SharePoint 2007 request
*/

ファイルがメモリ アドレスにマップされた後で、次のようなコードを使用して、ログの各エントリをスキャンして、サイトの利用状況の情報を返すことができます。

unsigned long cbEntrySize = 0;

for(pCur = pBase; 
    pCur < pEnd; 
    pCur += cbEntrySize)
{
    pLFE = (VLogFileEntry *)pCur;

    pszSiteGuid = pCur + sizeof(VLogFileEntry) + 2;
    pszTS   = pszSiteGuid + cbSiteGuid + 1;
    pszSite = pszTS + cbTimeStamp + 1; 
    *(pszSite + pLFE->cbSiteUrl) = '\0';
    pszWeb = pszSite + pLFE->cbSiteUrl + 1;
    pszDoc  = pszWeb + pLFE->cbWeb + 1;
    pszUser = pszDoc + pLFE->cbDoc + 1;

例では、現在のエントリを構造体としてキャストした後で、サイトの GUID、タイム スタンプ、最上位サイトの URL、サブサイトの相対 URL、参照したページのファイル名、ユーザー名を収集します。また、各エントリのバイナリ構造体とサイト GUID の間に存在するキャリッジ リターンとラインフィードに 2 バイトが使用され、エントリの別の部分の間に存在する NULL セパレータでは 1 バイトが使用されていることが考慮されています。

上記の for ループには、破損したログ データのケースのエラー処理も含まれます。次のコードでは、構造体の特定の部分に基づいて、エントリの合計サイズを収集します。その後、サイズが指定した値を超える場合、エントリにキャリッジ リターンとライン フィードしか含まれない場合、またはエントリが合計サイズに等しくない場合があるかどうか確認します。

const unsigned long maxCbEntrySize = 4096;

cbEntrySize = sizeof(VLogFileEntry) \
        + cbWebAppGuid + cbSiteGuid + cbTimeStamp \
        + pLFE->cbSiteUrl + pLFE->cbWeb + pLFE->cbDoc \
        + pLFE->cbUser + pLFE->cbQS \
        + pLFE->cbRefQS + pLFE->cbRef \
        + pLFE->cbUAS + 13; /* 11 NULLs (1 per field) and 2 bytes for \r\n */

// Check for corrupt log files.
fError  = (cbEntrySize > maxCbEntrySize ||
    !(*(pCur + sizeof(VLogFileEntry)) == '\r') ||
    !(*(pCur +  sizeof(VLogFileEntry) + 1) == '\n') ||
    !(pLFE->cbEntry == cbEntrySize));

if (fError)
{
    printf("Error reading WSS log file, aborting.\n");
    goto cleanup;
}

コード サンプルの試用

次のサンプルは、C++ アプリケーションの Project_Name.cpp ファイルで Windows SharePoint Services 3.0 ログ ファイルを解析し、利用状況のデータを .csv ファイルとして生成するために使用できるコードを示します。

重要

このサンプルの主な目的は、ログ ファイルを解析するコードの記述方法に関する基本的な考慮事項を例示することです。このサンプルでは、コードで実行する必要がある操作に重点を置くために、通常のデータ検証とエラー処理の大部分が削除されています。したがってこのサンプルには、実稼動システムで使用されるコードがまったく含まれていません。このサンプルに対しては、テクニカル サポートは利用できません。

サンプルをテストするには、ログ ファイルがあるサーバーで Microsoft Visual Studio 2005 を開いて、Microsoft Visual C++ コンソール アプリケーションを作成します。

Visual C++ コンソール アプリケーションを作成するには、次の手順に従ってください。

  1. Visual Studio の [ファイル] メニューの [新規作成] をポイントし、[プロジェクト] をクリックします。

  2. [新しいプロジェクト] ダイアログ ボックスで、[プロジェクトの種類] の [Visual C++ プロジェクト] をクリックします。[テンプレート] で、[コンソール アプリケーション] クリックします。

  3. [Name] ボックスにプロジェクトの名前 (Project_Name) を入力し、[場所] ボックスに、アプリケーションを作成する場所を入力し、[OK] をクリックします。

  4. [ソリューション エクスプローラ] で、作成された [Project_Name.cpp] ファイルをダブルクリックして、Visual Studio に既定で含まれるコードを次のコードに置き換えます。

    #include "stdafx.h"
    #include "windows.h"
    #include "assert.h"
    #include <stdio.h>
    
    const char szPaddingLogFile[] = \
    "Windows SharePoint Services HTTP log file                   "\
    "                                                            "\
    "                                                            "\
    "                                                            "\
    "                                                           ";
    
    typedef struct _VLogFileEntry
    {
        unsigned long long  pPrev; // Pointer to previous entry.
        unsigned char       bitFlags;
        unsigned short      cbEntry; /* Number of bytes to skip ahead 
    to next entry. */
        unsigned short      cbSiteUrl;
        unsigned short      cbWeb;
        unsigned short      cbDoc;
        unsigned long       cBytes; /* Bandwidth consumed 
    (bytes in + bytes out) */
        unsigned short      httpStatus;
        unsigned short      cbUser;
        unsigned short      cbRefQS;
        unsigned short      cbRef;
        short               cbUAS;
        unsigned short      cbQS;
        unsigned long       version;
        unsigned short      reserved;
    } VLogFileEntry;
    
    
    int main(int argc, char * argv[])
    {
        bool fError = FALSE;
        if (argc < 3)
        {
            printf(
                "\nUsage: %s wsslogfile csvfile optionalField1 optionalField2\n", 
                argv[0]);
            return(1);
        }
    
        char *szFile = argv[1];
        char *szCsvFile = argv[2];
        char *szOptionalField1 = argc > 3 ? argv[3] : NULL;
        char *szOptionalField2 = argc > 4 ? argv[4] : NULL;
        char *szGuid = NULL;
        char *szReplace = NULL;
    
        /* Format of each .csv line. Include optional fields
        passed as command-line arguments, if any.*/
        char *szFormat = "%s,%s,%s,%s,%s,%s,%s,%s\r\n";
        if (NULL == szOptionalField1)
            szFormat += 3;
        if (NULL == szOptionalField2)
            szFormat += 3;
    
        FILE *csvFile = NULL;
        fopen_s(&csvFile, szCsvFile, "a");
    
        // Bytes (with no braces).
        static const unsigned short cbWebAppGuid = 36; 
        static const unsigned short cbSiteGuid   = 36; 
        static const unsigned short cbTimeStamp  = 8;
    
        printf("\r\nParsing %s to %s \r\n",  szFile, szCsvFile);
        char *pBase, *pEnd;
        HANDLE hF, hFM;
        if ((hF = CreateFile(
                szFile,
                GENERIC_READ, 
                0, 
                NULL, 
                OPEN_EXISTING, 
                FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
                NULL)) == INVALID_HANDLE_VALUE)
        {
            printf(
                "Cannot open file %s (perhaps because it doesn't exist)",
                szFile);
            return (1);
        }
    
        DWORD dwFileSize, dwFileSizeHigh = 0;
        dwFileSize = GetFileSize(hF, &dwFileSizeHigh);
    
        /* We should never encounter a file larger than about 1 GB. */
        if (dwFileSizeHigh || dwFileSize > 1000000000)
        {
            printf(" File too large %s", szFile);
            CloseHandle(hF);
            return (1);
        }
    
        if (dwFileSize == 0)
        {
            printf(" Skipping empty file %s", szFile);
            CloseHandle(hF);
            return (1);
        }
    
        hFM = CreateFileMapping(hF, NULL, PAGE_WRITECOPY, 0, 0, NULL);
        if ((NULL == hFM) || 
            (NULL == (pBase = (char *)MapViewOfFile(hFM, FILE_MAP_COPY, 0, 0, 0))))
        {
            printf(" Can't map file %s", szFile);    
            if (hFM)
                CloseHandle(hFM);
            CloseHandle(hF);
            return (1);
        }
    
        pEnd = pBase + dwFileSize - sizeof(VLogFileEntry);
    
        char *pCur, *pszSite, *pszSiteGuid, *pszTS;
        char *pszWeb, *pszDoc, *pszUser;
        VLogFileEntry *pLFE;
        unsigned long cItemsProcessed = 0;
        unsigned long cbEntrySize = 0;
        const unsigned long maxCbEntrySize = 4096;
    
        //Skip ahead to the end of the file padding.
        unsigned long cbPadding = sizeof(szPaddingLogFile)/sizeof(szPaddingLogFile[0]);
        if (!strncmp(pBase, szPaddingLogFile, cbPadding))
            pBase += cbPadding;
    
        for(pCur = pBase;
            pCur < pEnd;
            pCur += cbEntrySize)
        {
            pLFE = (VLogFileEntry *)pCur;
    
            cbEntrySize = sizeof(VLogFileEntry) \
                + cbWebAppGuid + cbSiteGuid + cbTimeStamp \
                + pLFE->cbSiteUrl + pLFE->cbWeb + pLFE->cbDoc \
                + pLFE->cbUser + pLFE->cbQS \
                + pLFE->cbRefQS + pLFE->cbRef \
                + pLFE->cbUAS + 13; /* 11 NULLs (1 per field) and 2 bytes for \r\n */
    
            // Check for corrupt log files.
            fError  = (cbEntrySize > maxCbEntrySize ||
                !(*(pCur + sizeof(VLogFileEntry)) == '\r') ||
                !(*(pCur +  sizeof(VLogFileEntry) + 1) == '\n') ||
                !(pLFE->cbEntry == cbEntrySize));
    
            if (fError)
            {
                printf("Error reading WSS log file, aborting.\n");
                goto cleanup;
            }
    
            // Skip 2 bytes for \r\n.
            pszSiteGuid = pCur + sizeof(VLogFileEntry) + 2;
            // Skip 1 byte for the NULL separator.
            pszTS = pszSiteGuid + cbSiteGuid + 1;
            pszSite = pszTS + cbTimeStamp + 1;
            // Stop at the end of the site URL. 
            *(pszSite + pLFE->cbSiteUrl) = '\0';
            // Skip 1 byte for the NULL separator.
            pszWeb = pszSite + pLFE->cbSiteUrl + 1;
            pszDoc  = pszWeb + pLFE->cbWeb + 1;
            pszUser = pszDoc + pLFE->cbDoc + 1;
    
            /* Output is in this format: timestamp, site guid, siteUrl, 
             subsite, document, user, optional1, optional2*/
            fprintf(csvFile, 
                szFormat,
                pszTS,
                pszSiteGuid,
                pszSite,
                pszWeb,
                pszDoc,
                pszUser,
                szOptionalField1,
                szOptionalField2);
        }
    
        cleanup:
        UnmapViewOfFile(pBase);
        CloseHandle(hFM);
        CloseHandle(hF);
        fclose(csvFile);
    
        return fError;
    }
    
  5. [ ビルド ] メニューの [ソリューションのビルド] をクリックします。

  6. プロンプトで、プロジェクトの新しい .exe ファイルを含むフォルダに移動します。

  7. プロンプトで、「Project_Name.exe 」とスペース、ログファイルへの完全なパスとスペース、およびファイルの作成場所を入力します。次の例では、2007 年 4 月 1 日の利用状況の情報を含むルート ディレクトリに .csv ファイルを作成するように指定しています。

    WssLogParser.exe C:\WINDOWS\system32\LogFiles\STS\33AEF972-56BA-
       4294-98C7-0ACCF64585B8\2007-04-01\00.log c:\2007_04_01.csv
    

オプションで、他に 2 つのパラメータを渡して, .csv ファイルに 2 つの追加のフィールドを含めることもできます。たとえば、ログ ファイルへのパスの一部として機能する、ログの日付または Web アプリケーションの GUID を含めることができます。これらの追加のパラメータは、複数の Web アプリケーションまたは日数にまたがるログ ファイルが多数存在し、出力が 1 つの .csv ファイルに格納される状況でツールが使用される場合に、Web アプリケーションまたは利用状況のデータの日付を明確に保つために役立ちます。

まとめ

サイトの利用状況のイベント ログには、Windows SharePoint Services 3.0 で生成されるログを使用することで最も簡単にアクセスできます。上記の例では、これらのログを解析して、指定された形式で出力を生成する方法を例示しています。データを .csv 形式に出力するのではなく、情報をデータベースにエクスポートして、より大規模な操作という背景で処理できるツールを作成できます。

追加情報

Windows SharePoint Services 3.0 の詳細については、次の情報を参照してください。