イントロ
今回は、仮想マシン(VirtualBox)を想定していたが、必要があってApacheログを解析したので、
「Apacheログ徹底解析」に変更します。
Apacheの設定ファイル
Apacheの設定ファイルは、既定では/etc/httpd/conf/httpd.confで記述されます。ログフォーマットは以下で指定されます:
LogFormat “%h %l %u %t \”%r\” %>s %b \”%{Referer}i\” \”%{User-Agent}i\”” combined
CustomLog logs/access_log combined
“combined”というフォーマットが既定で設定されています。
apacheログの内容
○apacheログの内容
“combined”というフォーマットの場合のログファイルを記します。
参考URL:https://webllica.com/apache-web-server-log-analyze/
【ログ例】
122.26.4.0 – – [24/Aug/2021:18:01:13 +0900] “GET /column/1612/ HTTP/1.1” 200 78469 “-” “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36”
各項目はスペースで区切らています。
また項目によっては「””」で括れています。
項目名 | ログ例 |
出力内容 | |
ソースIPアドレス | 122.26.4.0 |
アクセス元の IPアドレスです。このアドレスによりどこからアクセスされたか識別できます。IPアドレスによるアクセス拒否を行う場合は、この情報を元に設定を行います。 | |
クライアントユーザ名 | – |
未設定の場合「-」が出力されます。一般的な Web サイトへのアクセスの場合、未設定となるケースが多いです。 | |
認証ユーザ名 | – |
認証されたユーザ名を出力します。未設定の場合「-」が出力されます。 | |
アクセス時刻 | [24/Aug/2021:18:01:13 +0900] |
Web サーバーへアクセスした日時の情報です。後ろの「+0900」はタイムゾーンを表します。 | |
リソース | “GET /column/1612/ HTTP/1.1” |
どのリソースに、どのプロトコルで、どのようなアクションをしたかを表します。アクセス先の情報を表示する場合、ドメイン情報は出力されません。このログでは、HTTP 1.1 で以下のページを表示するために、情報を取得しにきたと判定できます。 Windows11(2)、アップグレードインストール |
|
ステータス | 200 |
200 は正常終了を表します。100番台:情報レスポンス、200番台:成功レスポンス、300番台:リダイレクション、400番台:クライアントエラー等 | |
転送量 | 78469 |
アクセスしたリソースに対する転送量です。単位は byte となります。 | |
リファラ | “-“ |
アクセス元の URL を表します。どのページから遷移したか分かります。 | |
ユーザエージェント | “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36” |
どの OS/ブラウザからアクセスしたかを表します。Windows NT 10.0 と出力されているので、Windows10で64bitOSだと分かります。またブラウザは Chrome からのアクセスであると分かります。検索エンジンの bot からアクセスがあった場合は「Googlebot」のようにエージェント情報が出力されます。 ユーザエージェントは、ブラウザ戦争の歴史であり、その1とその2を参照して下さい、非常に興味深いです(^_^)。 【その1】:UserAgentからOS/ブラウザなどの調べかたのまとめ 【その2】:世界中を巻き込んだブラウザ戦争と「ユーザーエージェント文字列」をめぐる複雑怪奇な変遷とは? |
apacheログをExcelに取り込む
○apacheログをExcelに取り込む
取得したデータをデータベースに格納するのではなく、誰でも簡単にアクセスしたり編集できたりする
『スプレッドシート』に格納することができたら、幸せだ。
apacheログの各項目は「スペース」で区切られているCSV形式となっている。
また項目によってはダブルコーテーション(“”)で括られている。
「検索機能」で特定の文字列を含む行を抽出できる。
apacheログをワンライナーで解析
Linuxコマンド(ツール)「cat」「grep」「awk」「cut」「sort」「uniq」をパイプで接続して、
ワンライナー(1行)コマンドで、ログ解析を行う。
以下の実行結果は、あるサイトの実データです。
なおログファイル名は、「access.log」としました。
※参考URL:https://qiita.com/bezeklik/items/f5c292c4360cde140bef
機能名 | 実行コマンド | 実行結果 |
コマンド説明 | ||
備考 | ||
各URLのページビュー数を表示する | cat access.log | awk -F ‘”‘ ‘{print $2}’ | awk ‘{print $2}’ | sort | uniq -c | sort -r | 36 /column/1149/ 13 /column/ 12 /column/131/ 9 /column/1213/ 9 /column/1090/ 8 /column/295/ 7 /column/147/ 7 /column/1106/ |
最初の「awkコマンド」では項目の区切り文字として「”」(ダブルクオート)を指定している。 2番目の「awkコマンド」では項目の区切り文字として既定の「 」(スペース)を指定して、第2項目(URL)を取り出している。 取り出した内容をソート(sort)して、uniqで同一のものを集計して、最後に全体を逆順にソートして表示している。 |
||
各ページがどの程度閲覧されているか確認できる | ||
時間別アクセス数を表示する | cat access.log | awk ‘{print $4}’ | cut -b 2-15 | sort | uniq -c | 301 10/Aug/2021:08 391 10/Aug/2021:09 322 10/Aug/2021:10 361 10/Aug/2021:11 548 10/Aug/2021:12 335 10/Aug/2021:13 |
最初の「awkコマンド」で、年月日時(2-15)を取り出しては項目の区切り文字として「”」(ダブルクオート)を指定している。 次に年月日時をソートして、uniqで同一のものを集計して昇順に表示している。 |
||
1時間単位でのアクセス数を確認することができる。 なお分単位のアクセス数を確認する場合は、cut -b 2-18とすればいい。 日単位のアクセス数ならばcut -b 2-12とすればいい。 |
||
アクセス元のIPアドレスの出現数を表示する | cat access.log | awk ‘{print $1}’| sort | uniq -c | sort -r | 2726 138.199.18.157 2408 191.101.217.15 796 119.245.205.197 253 52.149.228.111 135 133.32.224.193 |
最初の「awkコマンド」で、アクセス元のIPアドレスを取り出す。 次にIPアドレスをソートして、uniqで同一のものを集計して、アクセス数の逆順に表示している。 |
||
WEBサーバに負荷が掛かっている場合等、怪しいサイトの確認ができる | ||
ユーザエージェント別アクセス数を表示する | cat access_.log | awk -F ‘”‘ ‘{print $6}’ | sort | uniq -c | sort -n -r | head -n 5 | 796 WordPress/4.8.17; https://trinitas.jp 591 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 253 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 1.1.4322) |
ユーザエージェント別アクセス数を表示する。 | ||
ユーザエージェントからOS別/ブラウザ別のアクセス数を求める | ||
ブラウザ別アクセス数を表示する | for UA in MSIE Firefox Chrome Safari Opera; do COUNT=`cat access.log | awk -F ‘”‘ ‘{print $6}’ | grep “$UA” | wc -l`; echo “$UA: $COUNT”; done | MSIE: 253 Firefox: 2 Chrome: 273 Safari: 273 |
ブラウザ別アクセス数を表示する(厳密ではない、簡易版です) | ||
ブラウザは/MSIE/Firefox/Chrome/Safari/Opera/を対象とする。 各ブラウザのUserAgent一覧 |
||
代表的なクローラのアクセス数を表示する | for UA in Googlebot bingbot facebook Twitterbot Applebot Baidu spider Sonic ; do COUNT=`cat access.log | awk -F ‘”‘ ‘{print $6}’ | grep “$UA” | wc -l`; echo “$UA: $COUNT”; done | Googlebot: 39 bingbot: 44 facebook: 2 Twitterbot: 53 Applebot: 2 Baidu: 2 spider: 2 |
代表的なクローラのアクセス数を表示する(厳密ではない、簡易版です) | ||
クローラは以下を対象とした: Googlebot/bingbot/facebook/Twitterbot/Applebot/Baidu/spider/Sonic クローラ(ロボット)の一覧 |
||
代表的なOS別のアクセス数を表示する | for UA in Linux Windows “Mac OS” “iPhone OS” Android ; do COUNT=`cat access.log | awk -F ‘”‘ ‘{print $6}’ | grep “$UA” | wc -l`; echo “$UA: $COUNT”; done | Linux: 339 Windows: 6474 Mac OS: 402 Android: 53 iPhone OS: 85 |
代表的なOS別のアクセス数を表示する(厳密ではない、簡易版です) | ||
OSは以下を対象とした: Linux Windows “Mac OS” Android ”iPhone OS” 厳密ではない、単なる目安程度の判別です(~_s~;)。 PCではやはりWindowsが強い、携帯ではiPhoneが強い程度は判ります。 |
||
1日の総転送量(バイト数)を計算する | cat access.log | awk -F ‘”‘ ‘{print $3}’| awk ‘$2 != “-” {count = count+$2} END {cnt = count/1024/1024; print “総転送量:”,count,”Bytes (“int(cnt)” MB)”; }’ | 総転送量: 248615433 Bytes (237 MB) |
各転送量を加算して、最後に、総転送量(バイト数)を計算する。 なお、MB換算値も表示する |
||
– | ||
Apacheログを時間でソートする | cat ssl_access.log | sort -k 4 | 【省略】s |
何故、時間でソートする必要があるのか!?元々時系列でソートされているのでは!?。 アクセスログの時間はアクセスのあった時間、アクセスログに書き込まれるのはレスポンスが終わってから。従って、レスポンスに時間がかかると、ログに書き込まれる時間は前後する。 |
||
第4項目でソート(-k 4)すればいいだけです | ||
WEBサーバへの同時アクセス数を表示する | echo -n “同時アクセス数(実):”; echo `/bin/netstat -an | /bin/grep -E “:(80|443) ” | /bin/grep -E “ESTABLISHED” | /usr/bin/wc -l ` | 同時アクセス数(実):8 |
ポート番号80(http)/433(https)を監視して、ステータスが「ESTABLISHED」のものを抽出する。 抽出した行数を表示して、同時アクセス数として表示する。 |
||
同時アクセス数を調べて、WEBサーバの負荷を確認できる ※異質ですが、解析としては重要なので、ここに記載した。 |
apacheログをPython言語で解析
○apacheログをPython言語で解析
唐突ですが、apacheログをPython言語で開発した場合です。
Pythonには便利なライブラリが用意されています:「apache-log-parser」
※「pip install apache-log-parser」でインストールします。
※python自身については、次回以降に詳述します。
「python parse_log.py」コマンドで実行した内容です。
◯parse_log.pyのソースコード
# -*- coding: utf-8 -*- import apache_log_parser import pprint #with open('access_20210830.log', mode='rt') as f: with open('access.log', mode='rt') as f: log_data = f.read().splitlines() print(log_data[0]) print("\n\n") print(log_data[1]) print("\n\n") print(log_data[2]) print("\n\n") line_parser = apache_log_parser.make_parser("%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"") count = 0 for item in log_data: count+=1 #インクリメント(「++」は使えない) if count > 3: break #特定項目抽出DICT型 print("年月日時分秒:"+line_parser(item)['time_received']) print("★★★"+str(count)+"行目★★★") pprint.pprint(line_parser(item), indent=4) """複数行コメント #print("★★★1行目★★★") #日本語出力には1行目の宣言が必要、またUTF-8化 #pprint.pprint(line_parser(log_data[0])) """
apacheログをPHP言語で解析
あるレンタルサーバの利用制限があり、調査のために、以下の機能をPHPの言語で作成したので紹介する。
※起動パラメータは、「ログファイル名」「閾値」とする。
○1秒間のアクセスが30件以上ある時間帯がどの位の頻度で発生するか
= $limit ) { echo " 時刻=$time2 件数=$count ソース=$src\n"; } } if ( $count >= $limit ) { echo " 時刻=$time2 件数=$count ソース=$src\n"; } /* 既に出現したソースか? */ function is_exist($src, $src_array) { foreach ($src_array as $value) { if ( strcmp($src, $value) == 0 ) { return true; } } return false; } /* 指定されたソースが何件あるかカウントする */ function src_count($time, $file) { $fh = fopen($file, "r"); $count = 0; $data = ""; while(($line = fgets($fh))) { $data = explode(' ',$line); $time2 = substr($data[3],13,8); if ( strcmp($time, $time2) == 0) { $count++; } } return $count; } ?>
○1分間のアクセスが50件以上ある時間帯がどの位の頻度で発生するか
= $limit ) { echo " 時刻=$time2 件数=$count ソース=$src\n"; } } if ( $count >= $limit ) { echo " 時刻=$time2 件数=$count ソース=$src\n"; } /* 既に出現したソースか? */ function is_exist($src, $src_array) { foreach ($src_array as $value) { if ( strcmp($src, $value) == 0 ) { return true; } } return false; } /* 指定されたソースが何件あるかカウントする */ function src_count($time, $file) { $fh = fopen($file, "r"); $count = 0; $data = ""; while(($line = fgets($fh))) { $data = explode(' ',$line); $time2 = substr($data[3],13,6); if ( strcmp($time, $time2) == 0) { $count++; } } return $count; } ?>
○1日アクセスが300件以上のソースアドレスを表示する
= $limit ) { echo " ソース=$src 件数=$count\n"; $count = 0; } } if ( $count >= $limit ) { echo " ソース=$src 件数=$count\n"; } /* 既に出現したソースか? */ function is_exist($src, $src_array) { foreach ($src_array as $value) { if ( strcmp($src, $value) == 0 ) { return true; } } return false; } /* 指定されたソースが何件あるかカウントする */ function src_count($src, $file) { $fh = fopen($file, "r"); $count = 0; $data = ""; while(($line = fgets($fh))) { $data = explode(' ',$line); $src2 = $data[0]; if ( strcmp($src, $src2) == 0) { $count++; } } return $count; } ?>
○1日のデータ転送量を計算する
次回について
次回から、しばらく、Python言語のモジュール(パッケージ)について紹介したい。
取り敢えず、WEBのクローリングとスクレイピングをテーマに便利な各モジュールを紹介する。
著者:志村佳昭(株式会社トリニタス 技術顧問)