apacheログ徹底解析

イントロ

今回は、仮想マシン(VirtualBox)を想定していたが、必要があってApacheログを解析したので、
「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のクローリングとスクレイピングをテーマに便利な各モジュールを紹介する。

著者:志村佳昭(株式会社トリニタス 技術顧問)