正規表現でログの情報を抽出・整形(powershell編)

# はじめに

powershellの正規表現を使って、ログの一部の情報だけを抽出・整形して表示したい。



ここでは、apacheのアクセスログを題材として、
特定のIPからのリクエストに関するサマリを表示できることを目標にする。
↓これが
```:access_log
123.123.123.123 - - [24/Feb/2022:10:03:34 +0000] "GET /favicon.ico HTTP/1.1" 404 196 "http://999.999.999.999:8080/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
```
↓こうなるのが目標
```powershell
PS > .\extract.ps1 .\logs\access_log 123.123.123.123 
IP : 123.123.123.123
Date : 24/Feb/2022:10:03:34 +0000
Request : GET /favicon.ico HTTP/1.1
StatusCode : 404
Size : 196
URL : http://999.999.999.999:8080/
```

# 方針

apacheのaccess_logは、デフォルトでは以下のような形式で出力される。下の場合、左から順に、
* 接続元のIP : `123.123.123.123`
* アクセス時刻 : `[24/Feb/2022:10:03:34 +0000]`
* リクエストの詳細 : `"GET /favicon.ico HTTP/1.1"`
* ステータスコード : 404
* 送信したデータ量 : 196
* URL : "http://999.999.999.999:8080/"
* OS・ブラウザの情報：`"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"`

が記載されている。

```:/var/log/httpd/access_log
123.123.123.123 - - [24/Feb/2022:10:03:34 +0000] "GET /favicon.ico HTTP/1.1" 404 196 "http://999.999.999.999:8080/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
```


上記を以下のパターンでマッチングさせる。
`(?<IP>^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)`のように、`()`で括って`?<IP>`と名前を付けておくと、後からその名前で取り出すことができる。
```:Pattern
(?<IP>^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*\[(?<Date>.*)\] \"(?<Request>.*)\" (?<StatusCode>[0-9]+) (?<Size>[0-9]+) \"(?<URL>.*)\" 
```

詳細は以下を参照。

* 正規表現について - PowerShell
    * https://docs.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_regular_expressions?view=powershell-7.2
* ログファイル - Apache HTTP サーバ バージョン 2.4
    * https://httpd.apache.org/docs/2.4/ja/logs.html

# 実装

### ①access_logから情報を抽出するpowershellスクリプト`extract.ps1`を書く
折角`<?IP>`のように名前を付けているのだが、なぜか`$_.Matches.Groups["IP"].value`と書くことはできず、
仕方なくインデックスで取っている。
※一応$_.Matches.Groups[1].Nameには「IP」という文字列が格納されているようだ


```powershell:extract.ps1
Param(
    [String]$filepath,
    [String]$IPaddr
)

$Pattern = '(?<IP>' + $IPaddr + ').*\[(?<Date>.*)\] \"(?<Request>.*)\" (?<StatusCode>[0-9]+) (?<Size>[0-9]+) \"(?<URL>.*)\" '
# $Pattern '(?<IP>^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*\[(?<Date>.*)\] \"(?<Request>.*)\" (?<StatusCode>[0-9]+) (?<Size>[0-9]+) \"(?<URL>.*)\" '

Select-String -Pattern $Pattern $filepath | ForEach-Object {
    $IP =  $_.Matches.Groups[1].value
    $Date = $_.Matches.Groups[2].value
    $Request = $_.Matches.Groups[3].value
    $StatusCode = $_.Matches.Groups[4].value
    $Size = $_.Matches.Groups[5].value
    $URL = $_.Matches.Groups[6].value

    Write-Output "IP : $IP"
    Write-Output "Date : $Date"
    Write-Output "Request : $Request"
    Write-Output "StatusCode : $StatusCode"
    Write-Output "Size : $Size"
    Write-Output "URL : $URL"
    Write-Output ""
}
```


### ②サンプルになるアクセスログを用意する
一つだけ`123.123.123.123`からのアクセスが紛れ込んでおり、この情報を抽出したい。
```:access_log
111.111.111.111 - - [24/Feb/2022:10:01:45 +0000] "GET / HTTP/1.1" 200 12976 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
111.111.111.111 - - [24/Feb/2022:10:01:45 +0000] "GET /Carousel%20Template%20%E3%83%BB%20Bootstrap%20v5.0_files/bootstrap.bundle.min.js.%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89 HTTP/1.1" 404 196 "http://999.999.999.999/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
111.111.111.111 - - [24/Feb/2022:10:01:45 +0000] "GET /files/carousel.css HTTP/1.1" 200 1695 "http://999.999.999.999/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
111.111.111.111 - - [24/Feb/2022:10:01:45 +0000] "GET /files/bootstrap.min.css HTTP/1.1" 200 155845 "http://999.999.999.999/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
111.111.111.111 - - [24/Feb/2022:10:03:34 +0000] "GET / HTTP/1.1" 200 10 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
123.123.123.123 - - [24/Feb/2022:10:03:34 +0000] "GET /favicon.ico HTTP/1.1" 404 196 "http://999.999.999.999:8080/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
111.111.111.111 - - [24/Feb/2022:10:03:37 +0000] "GET /Carousel%20Template%20%E3%83%BB%20Bootstrap%20v5.0_files/bootstrap.bundle.min.js.%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89 HTTP/1.1" 404 196 "http://999.999.999.999/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36"
```

### ③実行
`extract.ps1`の引数にログのパスとIPアドレスを指定して実行する。
うまく情報が抽出できていることがわかる。

```powershell
PS > .\extract.ps1 .\logs\access_log 123.123.123.123
IP : IP
Date : 24/Feb/2022:10:03:34 +0000
Request : GET /favicon.ico HTTP/1.1
StatusCode : 404
Size : 196
URL : http://999.999.999.999:8080/
```

# おわりに
* 実際にはlinuxサーバ上にあるapacheのログをpowershellで扱うことはまずないため、このスクリプトが日の目を浴びる機会はそうそうない・・・。サーバ上でawkとかでやることになると思われる。
    * awkを使う方法　→　https://qiita.com/alphajinsei/items/801e306b5512f19a370d
* `折角`<?IP>`のように名前を付けているのだが、なぜか$_.Matches.Groups["IP"].valueと書くことはできず、
仕方なくインデックスで取っている`の件、うまい書き方ご存じの方いたらぜひ教えてください。。。