はじめに
awkを使って、ログの一部の情報だけを抽出・整形して表示したい。
ここでは、apacheのアクセスログ/var/log/httpd/access_log
から、
送信元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"
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"
②awkを叩く
-
awk 'match($0, /正規表現/, a) {print a[0], a[1]}' ファイル名
が基本。- 正規表現内で
(hoge)
と書くことで、()
内でヒットした文字列をキャプチャして配列a
に保存することができる。a[0]
はマッチした文字列全体をあらわし、a[1]
以降にキャプチャした文字列が格納される。
- 正規表現内で
-
記法は
awk 'フィールド {アクション}' ファイルパス
- 正規表現は
/[0-9+]/
のように//
で囲って記載。
- 正規表現は
-
match関数を用いる。引数はmatch(文字列, パターン, 結果格納先)
-
(^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*\[(.*)\] \"(.*)\" ([0-9]+) ([0-9]+) \"(.*)\"
でマッチングさせる。- awkでは使えないが(要出典)、判読しやすいように名前付きキャプチャで書くと以下。
(?<IP>^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*\[(?<Date>.*)\] \"(?<Request>.*)\" (?<StatusCode>[0-9]+) (?<Size>[0-9]+) \"(?<URL>.*)\"
- awkでは使えないが(要出典)、判読しやすいように名前付きキャプチャで書くと以下。
-
OFS = ", "
で区切り文字を", "にしてcsv形式で出力させている。 -
送信元IPとステータスコードだけをうまく抽出できた。
[ec2-user@ip-xxx-xxx-xxx-xxx log_bk]$ awk 'match($0, /(^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*\[(.*)\] \"(.*)\" ([0-9]+) ([0-9]+) \"(.*)\" /, a) && OFS = ", " {print a[1],a[4]}' access_log
123.123.123.123, 404
111.111.111.111, 404
- 特定IPからの通信に絞りたい場合は以下。
- フィールドに
&& a[1]=="111.111.111.111"
を加える。
- フィールドに
[ec2-user@ip-xxx-xxx-xxx-xxx log_bk]$ awk 'match($0, /(^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*\[(.*)\] \"(.*)\" ([0-9]+) ([0-9]+) \"(.*)\" /, a) && a[1]=="111.111.111.111" && OFS = ", " {print a[1],a[4]}' access_log
111.111.111.111, 404
補足
鯖缶の視点としては、不審なIPからの通信が成功してしまっているか判別する際に便利かもしれない。
上記のフィールドに&& a[4]==200
を追加して引っかかってしまった日には・・・