ApacheのアクセスログをLog Parser Studioで解析してみた

## はじめに
Log Parser Studioは、マイクロソフトから無償で提供されているログ解析ツールです。
CUI版のLog ParserとGUI版のLog Parser Studio(Log Parserをインストールする必要あり)があります。
IISなど、主にマイクロソフト製品のログ解析に利用される事が多いと思われますが、今回は、Apacheのアクセスログを、Log Parser Studioで解析してみました。

## Log Parser、Log Parser Studioのインストール
Log Parser、Log Parser Studioをインストールします。
手順は以下が参考になると思います。

https://mebee.info/2020/04/10/post-8909/

https://www.microsoft.com/ja-jp/download/details.aspx?id=24659

https://techcommunity.microsoft.com/t5/exchange-team-blog/introducing-log-parser-studio/ba-p/601131

## クエリを実行してみる
以下のようなログフォーマットのアクセスログをLog Parser Studioに取り込み、実際にクエリを実行してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2554521/caef7298-fae4-9daa-18be-986572c350e5.png)
※ログの1行目には、クエリ実行結果用にヘッダーを追加しています。
※区切り文字は半角スペースにしています。

Log Parser Studioを起動し、赤枠のアイコンをクリックして、クエリエディターを開きます。
![temp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2554521/f65709c8-0dd7-f924-7770-ab9d56c95e00.png)
次に、左から5番目のアイコンをクリックし、解析するログファイルを選択します。
![temp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2554521/3d9fd691-1b2b-7c42-7186-4fab16404145.png)
ファイルは複数選択する事も可能です。
![temp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2554521/88f47c36-acf0-313f-610f-89614b020ea6.png)
次に、Log Typeを選択します(下矢印アイコンをクリック)。このログは半角スペース区切りなので、TSVLOGを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2554521/04b2eea2-c44f-a7ad-8f60-a002e4e3480c.png)
続いて、歯車アイコンをクリックし、iSeparatorの値を`tab`から`space`に変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2554521/eb61d36c-57fc-2119-b85d-c67213d4fa59.png)

:::note info
ログのフォーマットに合わせて、ログタイプや設定を変更します。
:::
設定完了後、左から2番目のアイコンをクリックすると、クエリが実行され、結果が表示されます。
![temp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2554521/3275d5d9-660f-13e2-0294-2c0448c45ac9.png)
この例で実行した`SELECT TOP 10 FROM '[LOGFILEPATH]'`というクエリは、ログファイルから最初の10行を取得するというものです。

## アクセスログを解析してみる
例えば、以下のような事を確認する事ができます。
#### サーバー処理時間(%T)が3秒以上かかったリクエスト数
以下を実行する事で、サーバー処理時間が閾値(この例では3秒)を超えているリクエスト数、サーバー処理時間の平均、サーバー処理時間の最大値を確認する事ができます。
````
SELECT count(*),avg(res_time),max(res_time) FROM '[LOGFILEPATH]' where res_time >= 3
````
#### サーバー処理時間(%T)が3秒以上のリクエスト(遅い順)
以下を実行する事で、サーバー処理時間が閾値(この例では3秒)を超えているリクエストを遅い順に一覧表示する事ができます。
この結果を確認する事で、どのURI(機能、処理)が遅いのか、傾向を確認する事ができます。
````
SELECT datetime,status,result,res_time,transfer,uri
  FROM '[LOGFILEPATH]'
 WHERE res_time >= 3
ORDER BY res_time DESC
````
#### サーバー処理時間(%T)が3秒以上のリクエスト件数(10分毎)
以下を実行する事で、サーバー処理時間が閾値(この例では3秒)を超えているリクエスト数を10分単位で確認する事ができます。
この結果を確認する事で、どの時間帯が遅かったのか、傾向を確認する事ができます。
````
SELECT
 QUANTIZE(to_timestamp(strcat(strcat('2018/10/', strcat(substr(datetime,1,2),' ')), substr(datetime,13,8)),'yyyy/MM/dd hh:mm:ss'),600) as time,
 count(*)
  FROM '[LOGFILEPATH]'
 WHERE res_time >= 3
GROUP BY time
ORDER BY time
````
#### 10分毎のリクエスト数
以下を実行する事で、10分単位でのリクエスト数を確認する事ができます。
この結果を確認する事で、アクセスが多い時間帯を確認する事ができます。
````
SELECT
 QUANTIZE(to_timestamp(strcat(strcat('2018/08/', strcat(substr(datetime,1,2),' ')), substr(datetime,13,8)),'yyyy/MM/dd hh:mm:ss'),600) as time,
 count(*)
  FROM '[LOGFILEPATH]'
GROUP BY time
ORDER BY time
````

:::note warn
年月の値は、アクセスログの年月に合わせ、適時変更します。
:::
#### 1秒毎のリクエスト数
以下を実行する事で、1秒単位でのリクエスト数を確認する事ができます。
この結果を確認する事で、同時アクセスが多い時間帯を確認する事ができます。
````
SELECT
 QUANTIZE(to_timestamp(strcat(strcat('2018/08/', strcat(substr(datetime,1,2),' ')), substr(datetime,13,8)),'yyyy/MM/dd hh:mm:ss'),1) as time,
 count(*)
  FROM '[LOGFILEPATH]'
GROUP BY time
ORDER BY time
````

:::note warn
年月の値は、アクセスログの年月に合わせ、適時変更します。
:::
#### 200以外のHTTPステータスの件数
以下を実行する事で、HTTPステータス200以外を返したリクエストの件数を確認する事ができます。
````
SELECT status,count(*) FROM '[LOGFILEPATH]' where status <> 200 group by status order by status
````

#### クエリ実行結果をCSV出力
クエリの実行結果は、左から4番目のアイコンをクリックする事で、CSVファイルに出力する事ができます。
結果の件数が多いなどしてGUIでは解析が難しい場合は、CSVファイルで詳細を確認する事が可能です。
![temp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2554521/ed719b27-6293-d162-2bd3-d7df0f9bf710.png)


#### クエリを保存してライブラリに追加
左から3番目のアイコンをクリックする事で、クエリを保存する事ができます。
![temp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2554521/045233cd-f5f3-5865-7a2b-f21cbb0435fe.png)
クエリを保存すると、Libraryタブの一覧に保存したクエリが追加されるので、よく利用するクエリは保存しておくと便利です。

※ライブラリの一覧でクエリをダブルクリックすると、保存したクエリが記載された状態でクエリエディタが開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2554521/ab842269-848b-615c-191b-8cedd9248580.png)

## (参考)Log Parser関数一覧
Log Parserで利用できる関数の一覧です。その他、Libraryの一覧にあるクエリも参考になると思います。

:::note info
関数の引数における[ ]は省略可能を意味します。
:::
#### 算術関数
| 関数名 | 説明 |
|:-|:-|
| ADD(値1,値2) | 2つの値（数値/文字/日時）の合計を計算 |
| BIT_AND(数値1,数値2) | 2つの値のビットごとのANDを計算 |
| BIT_NOT(数値) | 値のビットごとのNOTを計算 |
| BIT_OR(数値1,数値2) | 2つの値のビットごとのORを計算 |
| BIT_SHL(数値,ビット数) | 指定されたビット数分だけ値を左にシフト |
| BIT_SHR(数値,ビット数) | 指定されたビット数分だけ値を右にシフト |
| BIT_XOR(数値1,数値2) | 2つの値のビットごとのXORを計算 |
| DIV(数値1,数値2) | 2つの値の商を計算 |
| EXP(指数) | e（自然対数の底）の指定された引数のべき乗 |
| EXP10(指数) | 10のべき乗を計算 |
| FLOOR(数値) | 引数の整数部分 |
| LOG(数値) | 自然対数を計算 |
| LOG10(数値) | 10を底とする対数を計算 |
| MOD(数値,除数) | 2つの数値を除算した余りを計算 |
| MUL(数値1,数値2) | 2つの値の積を計算 |
| QNTFLOOR_TO_DIGIT(数値,有効桁数) | 数値を有効桁数で切り捨て |
| QNTROUND_TO_DIGIT(数値,有効桁数) | 数値を有効桁数で丸め |
| QUANTIZE(数値1,数値2) | 第2引数の倍数で第1引数に最も近い値（数値1>数値2で切り捨て） |
| ROUND(数値) | 引数に最も近い整数を計算 |
| SQR(数値) | 2乗値の計算 |
| SQRROOT(数値) | 平方根を計算 |
| SUB(値1,値2) | 2つの値（数値/文字/日時）の差を計算 |

#### 変換関数
| 関数名 | 説明 |
|:-|:-|
| HEX_TO_INT(文字列) | 16進数を整数自体に変換 |
| INT_TO_IPV4(数値) | 32ビット整数をIPV4アドレスに変換 |
| IPV4_TO_INT(文字列) | IPV4アドレスを32ビット整数に変換 |
| TO_DATE(日時) | 日時を日付のみに変換 |
| TO_HEX(値) | 文字/数値を16進数に変換 |
| TO_INT(値) | 文字/日時/実数を整数に変換 |
| TO_LOCALTIME(日時) | 世界協定時刻（UTC）から現地時刻に変換 |
| TO_REAL(値) | 文字/日時/整数を実数に変換 |
| TO_STRING(数値) | 数値を文字列に変換 |
| TO_TIME(日時) | 日時を時刻のみに変換 |
| TO_TIMESTAMP(文字列,書式) | 文字列を書式日時に変換 |
| TO_UTCTIME(日時) | 現地時刻から世界協定時刻（UTC）に変換 |

#### 文字列操作関数
| 関数名 | 説明 |
|:-|:-|
| EXTRACT_EXTENSION(ファイルパス) | ファイルパスのファイル拡張子部分 |
| EXTRACT_FILENAME(ファイルパス) | ファイルパスのファイル名部分 |
| EXTRACT_PATH(ファイルパス) | ファイルパスのディレクトリパス部分 |
| EXTRACT_PREFIX(文字列,番号,区切文字) | 文字列を区切文字で分割し、最初から指定番号の文字列 |
| EXTRACT_SUFFIX(文字列,番号,区切文字) | 文字列を区切文字で分割し、指定番号から最後までの文字列 |
| EXTRACT_TOKEN(文字列,番号,区切文字) | 文字列を区切文字で分割し、指定番号の部分文字列 |
| EXTRACT_VALUE(文字列,キー文字,[区切文字]) | 文字列内の指定されたキー値の文字列 |
| HEX_TO_ASC(文字列) | 16進数をASCII文字列に変換 |
| HEX_TO_HEX16(文字列) | 16進数を16ビットWORDに変換 |
| HEX_TO_HEX32(文字列) | 16進数を32ビットWORDに変換 |
| HEX_TO_HEX8(文字列) | 16進数を8ビットWORDに変換 |
| HEX_TO_PRINT(文字列) | 16進数を印刷可能なASCII文字列に変換 |
| INDEX_OF(文字列,検索文字列) | 最初に検索文字列が出現する位置番号 |
| LAST_INDEX_OF(文字列,検索文字列) | 最後に検索文字列が出現する位置番号 |
| LTRIM(文字列) | 文字列の左側の空白を削除 |
| REPLACE_CHR(文字列,検索文字,置換文字列) | 検索文字が文字列に一致すれば置換 |
| REPLACE_STR(文字列,検索文字列,置換文字列) | 検索文字列が文字列に一致すれば置換 |
| ROT13(文字列) | ROT13アルゴリズムで文字列をエンコード/デコード |
| RTRIM(文字列) | 文字列の右側の空白を削除 |
| STRCAT(文字列1,文字列2) | 文字列1に文字列2を付加 |
| STRCNT(文字列,検索文字列) | 文字列中の検索文字列の出現回数 |
| STRLEN(文字列) | 文字列の長さ |
| STRREPEAT(文字列,回数) | 文字列の回数分繰り返し |
| STRREV(文字列) | 文字列の前後反転 |
| SUBSTR(文字列,開始位置,文字数) | 指定位置から長さ分の部分文字列を抽出 |
| TO_LOWERCASE(文字列) | アルファベットを小文字に変換 |
| TO_UPPERCASE | アルファベットを大文字に変換 |
| TRIM | 文字列の前後の空白を削除 |
| URLESCAPE(文字列,[コードページ]) | 文字列をURLエンコード |
| URLUNESCAPE(文字列,[コードページ]) | URLエンコードされた文字列をデコード |

#### システム情報関数
| 関数名 | 説明 |
|:-|:-|
| COMPUTER_NAME() | ローカルコンピュータのNetBIOS名 |
| RESOLVE_SID(SID) | SIDの完全なアカウント名 |
| REVERSEDNS(IPアドレス) | IPアドレスに対応するホスト名 |
| SYSTEM_DATE() | 世界協定時刻（UTC）のシステム日付 |
| SYSTEM_TIME() | 世界協定時刻（UTC）のシステム時刻 |
| SYSTEM_TIMESTAMP() | 世界協定時刻（UTC）のシステム日時 |
| SYSTEM_UTCOFFSET() | 現地時刻と世界協定時刻（UTC）との差 |

#### その他の関数
| 関数名 | 説明 |
|:-|:-|
| CASE 条件値 WHEN テスト値1 戻り値1...～END | 条件値がテスト値と一致すれば戻り値を戻す |
| COALESCE(値1,値2,値3...) | NULL以外の値で最初に合致したもの |
| HASHMD5_FILE(ファイルパス) | ファイルの内容のMD5ハッシュを計算した16進数 |
| HASHSEQ(文字列) | 文字列の値が一意になる整数 |
| IN_ROW_NUMBER() | 入力レコードの行番号 |
| OUT_ROW_NUMBER() | 出力レコードの行番号 |
| REPLACE_IF_NOT_NULL(値1,値2) | 値1がNULLの場合、値2を戻す |
| SEQUENCE(開始番号) | 開始番号から始まる入力レコードの連番 |
| WIN32_ERROR_DESCRIPTION(エラーコード) | Windowsエラーコードに関連付けられたテキストメッセージ |

## おわりに
Log Parser Studioを使い、Apacheのアクセスログを解析する方法について記載しました。
他にもWindowsのイベントログの解析など、色々な使い方ができるツールだと思いますので、簡易的なログ解析をする際に、利用してみてはと思います。