web-dev-qa-db-ja.com

Apacheログを解析するための便利なawkおよびgrepスクリプトはありますか?

ログアナライザーを使用できますが、最近のWebログを解析して、現在何が起こっているかを確認する必要があることがよくあります。

特定のファイルを要求する上位10個のIPを把握するようなことをすることがあります

cat foo.log | grep request_to_file_foo | awk '{print $1}' |  sort -n | uniq -c | sort -rn | head

ツールボックスには何がありますか?

70
deadprogrammer

Apacheログファイルでは、awkだけでほとんど何でもできます。 Apacheログファイルは基本的に空白で区切られており、引用符が存在しないふりをして、必要な情報に列番号でアクセスできます。これが失敗するのは、結合されたログ形式があり、ユーザーエージェントに興味がある場合のみです。この場合、引用符( ")を区切り記号として使用し、別のawkコマンドを実行する必要があります。次のIPが表示されます。ヒット数でソートされたインデックスページをリクエストするすべてのユーザー:

awk -F'[ "]+' '$7 == "/" { ipcount[$1]++ }
    END { for (i in ipcount) {
        printf "%15s - %d\n", i, ipcount[i] } }' logfile.log

$ 7はリクエストされたURLです。最初に必要な条件を追加できます。 '$ 7 == "/"を必要な情報に置き換えます。

(ipcount [$ 1] ++)の$ 1を置き換える場合、結果を他の基準でグループ化できます。 $ 7を使用すると、アクセスされたページとその頻度が表示されます。もちろん、最初に条件を変更する必要があります。次の例は、特定のIPからユーザーがアクセスしたページを示しています。

awk -F'[ "]+' '$1 == "1.2.3.4" { pagecount[$7]++ }
    END { for (i in pagecount) {
        printf "%15s - %d\n", i, pagecount[i] } }' logfile.log

また、出力をsortにパイプして、Shellコマンドの一部として、またはawkスクリプト自体で結果を順番に取得することもできます。

awk -F'[ "]+' '$7 == "/" { ipcount[$1]++ }
    END { for (i in ipcount) {
        printf "%15s - %d\n", i, ipcount[i] | sort } }' logfile.log

後者は、awkスクリプトを拡張して他の情報を出力することにした場合に役立ちます。それはすべてあなたが知りたいことの問題です。これらは、あなたが興味を持っているものの出発点として役立つはずです。

54
Mark

想像もできない理由で、他の人が見たことのないことの1つは、Apacheログファイルの形式を、実際に重要な情報を含む、より簡単に解析できるバージョンに変更することです。

たとえば、HTTP基本認証は使用しないため、これらのフィールドをログに記録する必要はありません。私はam各要求の処理にかかる時間に関心があるので、それを追加します。1つのプロジェクトでは、(ロードバランサーで)サーバーが要求よりも遅い要求を処理しているかどうかも知りたいその他、プロキシするサーバーの名前をログに記録します。

これは、1つのサーバーのApache構成からの抜粋です。

# We don't want to log bots, they're our friends
BrowserMatch Pingdom.com robot

# Custom log format, for testing
#
#         date          proto   ipaddr  status  time    req     referer         user-agent
LogFormat "%{%F %T}t    %p      %a      %>s     %D      %r      %{Referer}i     %{User-agent}i" standard
CustomLog /var/log/Apache2/access.log standard env=!robot

これから実際に判断できないことは、各フィールド間がリテラルのタブ文字(\ t)であることです。つまり、Pythonで分析を行いたい場合、たとえば200以外のステータスを表示したい場合、次のようにできます。

for line in file("access.log"):
  line = line.split("\t")
  if line[3] != "200":
    print line

または、「画像をホットリンクしているのは誰ですか?」それはそのようになります

if line[6] in ("","-") and "/images" in line[5]:

アクセスログのIPカウントの場合、前の例:

grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" logfile | sort -n | uniq -c | sort -n

このようなものになります:

cut -f 3 log | uniq -c | sort -n

読みやすく、理解しやすく、計算コストがはるかに低い(正規表現なし)ため、9 GBのログでは、かかる時間が大きく異なります。これが本当にうまくいくのは、ユーザーエージェントに対して同じことをしたい場合です。ログがスペースで区切られている場合は、手動で正規表現マッチングまたは文字列検索を行う必要があります。このフォーマットを使用すると、簡単です。

cut -f 8 log | uniq -c | sort -n

上記と全く同じです。実際、実行したい要約は基本的にまったく同じです。

カットするとシステムのCPUがawkとgrepに費やされるのに、カットが正確に何桁も速く実行されるのはなぜですか?

24
Dan Udey

Awkとgrepを忘れてください。チェックアウト asql 。ログファイルのクエリにSQLのような構文を使用できるのに、なぜ読めないスクリプトを書くのか。例えば。

asql v0.6 - type 'help' for help.
asql> load /home/skx/hg/engaging/logs/access.log
Loading: /home/skx/hg/engaging/logs/access.log
sasql> select COUNT(id) FROM logs
46
asql> alias hits SELECT COUNT(id) FROM logs
ALIAS hits SELECT COUNT(id) FROM logs
asql> alias ips SELECT DISTINCT(source) FROM logs;
ALIAS ips SELECT DISTINCT(source) FROM logs;
asql> hits
46
asql> alias
ALIAS hits SELECT COUNT(id) FROM logs
ALIAS ips SELECT DISTINCT(source) FROM logs;
16
Vihang D

最近のN個のログエントリから上位のURL、上位の参照元、上位のユーザーエージェントを見つけるスクリプトは次のとおりです

#!/bin/bash
# Usage
# ls-httpd type count
# Eg: 
# ls-httpd url 1000
# will find top URLs in the last 1000 access log entries
# ls-httpd ip 1000
# will find top IPs in the last 1000 access log entries
# ls-httpd agent 1000
# will find top user agents in the last 1000 access log entries

type=$1
length=$2

if [ "$3" == "" ]; then
  log_file="/var/log/httpd/example.com-access_log"
else
  log_file="$3"
fi

if [ "$type" = "ip" ]; then
  tail -n $length $log_file | grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" | sort -n | uniq -c | sort -n
Elif [ "$type" = "agent" ]; then
  tail -n $length $log_file | awk -F\" '{print $6}'| sort -n | uniq -c | sort -n
Elif [ "$type" = "url" ]; then
  tail -n $length $log_file | awk -F\" '{print $2}'| sort -n | uniq -c | sort -n
fi

ソース

6
anoopjohn

アクセスログのIPカウントの場合:

cat log | grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" | sort -n | uniq -c | sort -n

少し見苦しいですが、動作します。また、netstatで以下を使用します(アクティブな接続を確認するため)。

netstat -an | awk '{print $5}' | grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" | egrep -v "(`for i in \`ip addr | grep inet |grep eth0 | cut -d/ -f1 | awk '{print $2}'\`;do echo -n "$i|"| sed 's/\./\\\./g;';done`127\.|0\.0\.0)" | sort -n | uniq -c | sort -n

彼らは私のお気に入りの「ワンライナー」の一部です:)

5
f4nt

ここで私の「sed」の例では、Apacheログのデフォルト形式を読み取り、自動処理に適したものに変換します。行全体が正規表現として定義され、変数が保存され、 '#'を区切り文字として出力に書き込まれます。

入力の簡略表記は次のとおりです:%s%s%s [%s] "%s"%s%s "%s" "%s"

入力行の例:xx.xx.xx.xx--[29/Mar/2011:12:33:02 +0200] "GET /index.html HTTP/1.0" 200 9443 "-" "Mozilla/4.0"

出力行の例:xx.xx.xx.xx#-#-#29/Mar/2011:12:33:02 + 0200#GET /index.html HTTP/1.0#200#9443#-#Mozilla/4.0

cat access.log | \ 
  sed 's/^\(.*\) \(.*\) \(.*\) \[\(.*\)\] \"\(.*\)\" \(.*\) \(.*\) \"\(.*\)\" \"\(.*\)\"$/\1#\2#\3#\4#\5#\6#\7#\8#\9/g'

正規表現の力を感じてください:-)

3
Kris

一般的な質問のリストを作成することは、この質問に対するこの回答の優れたインデックスになるでしょう。私のよくある質問は:

  • なぜヒット率が変化したのですか?
  • なぜ全体の応答時間が上がるのですか?」.

サーバーステータスページを(mod_statusを介して)監視し、アクティブなリクエストと最近完了したリクエストの応答時間を概算することで、このような変化に気付きました(大量のデータを見逃したことを十分に理解していますが、サンプルは十分です)。

次のLogFormatディレクティブを使用します(%Tは本当に便利です)

LogFormat "%h %l %u %t \"%r\" %>s %b 
    \"%{Referer}i\" \"%{User-Agent}i\" %T" custom

原因と効果と最初に何が起こったのかを探しています...通常はログ内のパターンの特定のサブセットについてなので、特定のパターン/正規表現について次のことを知る必要があります。

  • 特定のパターン(IPアドレス、CGI文字列、パラメーターなど)の間隔(分または時間)ごとのヒットカウント
  • おおよその応答時間のヒストグラム(%Tパラメーターを使用)

最終的には価値のあるものになるほど複雑になるため、私は通常Perlを使用します。


非Perlの例は、200以外のステータスコードの1分あたりのクイックヒット率です。

tail -9000 access_log | grep -v '" 200 ' | cut -d: -f2,3 | uniq -c

はい、私はそのgrepを使用して不正行為を行っています。quote-space-200-spaceはhttpステータスコードのみに一致すると想定しています... awkまたはPerlを使用してフィールドを分離できますが、不正確であることに注意してください。


Perlのより複雑な例は、パターンのヒット率の変化を視覚化することです。

以下のスクリプトでは、特にPerlに慣れていない場合は、多くの注意事項があります。

  • ログの一部を使用できるようにstdinを読み取り、tail(特にtail -fを使用)を使用します。
  • 正規表現のハックとDate :: Manipの使用によるエポックタイムスタンプ抽出のチート
  • わずかに変更するだけで、応答時間やその他の任意のデータを抽出できます

コードは次のとおりです。

#!/usr/bin/Perl
# script to show changes in hitrates for any regex pattern
# results displayed with arbitrary intervals
# and ascii indication of frequency
# gaps are also displayed properly
use Date::Manip;
use POSIX qw(strftime);
$pattern=shift || ".";
$ival=shift || 60;
$tick=shift || 10;
$minb=undef;
while (<>){
    next unless /$pattern/;
    $stamp="$1 $2" if m[(../.../....):(..:..:..)];
    $Epoch = UnixDate(ParseDate($stamp),"%s");
    $bucket= int($Epoch/$ival)*$ival;
    $minb=$bucket if $bucket<$minb || !defined($minb);
    $maxb=$bucket if $bucket>$maxb;
    $count{$bucket}++;
}
# loop thru the min/max range to expose any gaps
for($t=$minb;$t<=$maxb;$t+=$ival){
    printf "%s %s %4d %s\n",
            $t,
            strftime("%m/%d/%Y %H:%M:%S",localtime($t)),
            $count{$t}+0,
            substr("x"x100,0,$count{$t}/$tick
    );
}

標準的な指標のみを処理する場合は、チェックアウト

  • すべてのログをまとめるための 'mergelog'(ロードバランサーの背後に複数のパッチがある場合)および
  • webalizer(またはawstatsまたはその他の一般的なアナライザー)。
3
ericslaw

画像をホットリンクしているのは誰ですか:

awk -F\" '($2 ~ /\.(jpg|gif)/ && $4 !~ /^http:\/\/www\.mydomain\.com/){print $4}' access_log | sort | uniq -c | sort
2
rkthkr

ファイルをテーリングまたはキャットすることにより、awkを頻繁に使用します。毎晩、サーバーごとにWebレポートを配信しています。ログファイルとLogFormatに応じて、機能する1つのライナーのいくつかを編集する必要があります。

以下に簡単な例を示します。

サーバーのログを404/500ステータスコードのみに合わせたい場合は、次のようにします。

# $6 is the status code in my log file

tail -f ${Apache_LOG} |  awk  '$8 ~ /(404|500)/ {print $6}'

<切り取り>

echo ""
#echo  "Hits by source IP:"
echo "======================================================================"

awk '{print $2}' "$1" | grep -ivE "(127.0.0.1|192.168.100.)" | sort | uniq -c | sort -rn | head -25

echo ""
echo ""
#echo "The 25 most popular pages:"
echo "======================================================================"

awk '{print $6}' "$1" | grep -ivE '(mod_status|favico|crossdomain|alive.txt)' | grep -ivE '(.gif|.jpg|.png)' | \
 sed 's/\/$//g' | sort | \
 uniq -c | sort -rn | head -25

echo ""    
echo ""
echo "The 25 most popular pages (no js or css):"
echo "======================================================================"

awk '{print $6}' "$1" | grep -ivE '(mod_status|favico|crossdomain|alive.txt)' | grep -ivE '(.gif|.jpg|.png|.js|.css)' | \
 sed 's/\/$//g' | sort | \
   uniq -c | sort -rn | head -25

   echo ""


#echo "The 25 most common referrer URLs:"
echo "======================================================================"

awk '{print $11}' "$1" | \
 grep -vE "(^"-"$|/www.$Host|/$Host)" | \
 sort | uniq -c | sort -rn | head -25

echo ""

#echo "Longest running requests"
echo "======================================================================"

awk  '{print $10,$6}' "$1" | grep -ivE '(.gif|.jpg|.png|.css|.js)'  | awk '{secs=0.000001*$1;req=$2;printf("%.2f minutes req time for %s\n", secs / 60,req )}' | sort -rn | head -50

exit 0

</ snip>

2

Sedやawkではありませんが、Apacheやicecastのログファイルを処理するのに役立つことが2つあります。

AWStats には logresolvemerge.pl と呼ばれる非常に便利なスクリプトがあり、複数の圧縮または非圧縮ログファイルを組み合わせ、重複を取り除き、タイムスタンプでソートします。 DNSルックアップを実行し、マルチスレッドを実行するように構成することもできます。 awstatsは現在のデータベースより古いタイムスタンプのログ行を追加できないため、すべてを順番に追加する必要があるので、awstatsと一緒に使用する場合に特に便利ですが、everythinglogresolvemerge.plで、すべてうまく表示されます。

sedとawkは、日付を一般に文字列として扱うため、日付の処理がかなり悪いです。 awkにはいくつかの時間と日付の関数がありますが、それらはそれほど多くはありません。たとえば、2つのタイムスタンプ間の行の範囲を抽出することは、それらの正確なタイムスタンプがファイルで発生しない場合(それらの間の値が発生する場合でも)、困難です-Chrisの例には、まさにこの問題があります。これに対処するために、私は a PHP script と書いて、ログファイルのタイムスタンプ範囲を報告し、タイムスタンプ範囲ごとにチャンクを抽出することができます。 (ログファイルのタイムスタンプ形式と一致する必要はありません)。

このトピックを維持するために、いくつかの有用な奇妙な方法を示します。ApacheまたはIcecastログから提供される合計バイト数を取得します。

cat access.log | awk '{ sum += $10 } END { print sum }'

接続された合計秒数をicecastログから取得します。

cat access.log | awk '{ sum += $13 } END { print sum }'
1
Synchro

ほとんどの場合私がしがちなのは、時間に基づいてログのセクションを読み取ることです。そのため、次のスクリプトをsedを使用して書き、興味のある期間を引き出しました。これは、これまでに来たすべてのログファイルで機能します。全体で、アーカイブされたログも処理できます。

#!/ bin/bash 
#このスクリプトは2つの値の間の行のセットを返す必要があります。主な目的は、ログファイルを2回検索することです
#スクリプトの使用法: logship.sh "start" "stop"ファイル
 
#日付範囲に「/」が含まれている場合、次の2行でエスケープ文字を追加して、検索を実行できるようにします文字
 start = $(echo "$ 1" | sed 's/\ // \\\ // g')
 stop = $(echo "$ 2" | sed 's/\//\\\//g')

zipped=$(echo "$ 3" | grep -c "gz $")#ファイルが圧縮されているかどうかが表示されます
 
 if ["$ zipped" == "1"];次に、ファイルが圧縮されている場合は、sedの前にzcatに渡します
 zcat $ 3 | sed -n "/$start/,/$stop/p";
else
 sed -n"/$ start /、/ $ stop/p "$ 3; #圧縮されていない場合は、sed 
 fi 
を実行します。
1
Chris

この古いスレッドを回復し、大きなログファイルのasqlをあきらめた後、再度サーバーフォールトで解決策を探しました、wtopについて見つけました ここにあります それはライブモニタリングまたはプロセスログを実行して統計情報(上位N位)を取得できるオープンソースツールで、非常に柔軟で強力です。公式の場所は こちら

0
aseques