イントロ
今回は、仮想マシン(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のクローリングとスクレイピングをテーマに便利な各モジュールを紹介する。
著者:志村佳昭(株式会社トリニタス 技術顧問)







