Apakah Anda memiliki skrip awk dan grep yang berguna untuk mem-parsing log apache? [Tutup]

70

Saya dapat menggunakan penganalisis log, tetapi sering saya harus mengurai log web baru-baru ini untuk melihat apa yang terjadi saat ini.

Saya kadang-kadang melakukan hal-hal seperti mencari tahu 10 ips teratas yang meminta file tertentu

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

Apa yang Anda miliki di kotak alat Anda?

deadprogrammer
sumber
1
Saya sebenarnya memiliki regex besar yang indah ini yang telah saya tulis dengan tangan untuk mem-parsing semua log apache kustom saya ke masing-masing bidang untuk diserahkan ke database. Saya menendang diri sendiri bahwa saya tidak memilikinya lagi. Itu adalah satu liner; memberi Anda kembali satu variabel untuk setiap elemen log - lalu saya memasukkan ke dalam MySQL. Jika saya menemukannya saya akan mempostingnya di sini.
Kyle Hodgson

Jawaban:

54

Anda dapat melakukan hampir semua hal dengan file log apache dengan awk saja. File-file log Apache pada dasarnya dipisahkan oleh spasi, dan Anda dapat berpura-pura bahwa kutipan tidak ada, dan mengakses informasi apa pun yang Anda minati berdasarkan nomor kolom. Satu-satunya saat ini rusak adalah jika Anda memiliki format log gabungan dan tertarik pada agen pengguna, di mana Anda harus menggunakan tanda kutip (") sebagai pemisah dan menjalankan perintah awk terpisah. Berikut ini akan menunjukkan kepada Anda IP dari setiap pengguna yang meminta halaman indeks diurutkan berdasarkan jumlah klik:

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

$ 7 adalah url yang diminta. Anda dapat menambahkan kondisi apa pun yang Anda inginkan di awal. Ganti '$ 7 == "/" dengan informasi apa pun yang Anda inginkan.

Jika Anda mengganti $ 1 dalam (ipcount [$ 1] ++), maka Anda dapat mengelompokkan hasilnya berdasarkan kriteria lain. Menggunakan $ 7 akan menunjukkan halaman apa yang diakses dan seberapa sering. Tentu saja Anda ingin mengubah kondisi di awal. Berikut ini akan menunjukkan halaman apa yang diakses oleh pengguna dari IP tertentu:

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

Anda juga dapat mem-pipe output melalui sortir untuk mendapatkan hasil secara berurutan, baik sebagai bagian dari perintah shell, atau juga dalam skrip awk itu sendiri:

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

Yang terakhir akan berguna jika Anda memutuskan untuk memperluas skrip awk untuk mencetak informasi lainnya. Ini semua masalah apa yang ingin Anda ketahui. Ini harus berfungsi sebagai titik awal untuk apa pun yang Anda minati.

Menandai
sumber
Yah, sepertinya aneh melihat pipa panjang kucing / grep / awk yang gila. Setelah Anda awk, itu biasanya sudah cukup. Tiga klausa pertama dari posting asli bisa ditulis dengan sepele sebagai "awk '/ request_to_file_foo / {print $ 1}' foo.log". awk dapat mengambil file sebagai input, dan dapat menggunakan regex untuk mengetahui baris mana yang harus diperhatikan.
Zac Thompson
Elegan dan sederhana. Baik.
Olivier Dulac
Hati-hati, ruang tampaknya diizinkan di bidang "authuser" (ke-3), yang merusak segalanya, dan saya pribadi berpikir itu harus dilarang, untuk memungkinkan kami melakukan ini ;-)
Mandark
23

Satu hal yang belum pernah saya lihat dilakukan orang lain, karena alasan yang tidak dapat saya bayangkan, adalah mengubah format file log Apache menjadi versi yang lebih mudah diurai dengan informasi yang sebenarnya penting bagi Anda.

Misalnya, kami tidak pernah menggunakan autentikasi dasar HTTP, jadi kami tidak perlu mencatat bidang-bidang itu. Saya saya tertarik pada berapa lama setiap permintaan dibutuhkan untuk melayani, jadi kita akan menambahkan bahwa dalam. Untuk satu proyek, kami juga ingin tahu (pada penyeimbang beban kami) jika ada server yang melayani permintaan lebih lambat daripada yang lain, jadi kami log nama dari server yang kami proksi kembali.

Berikut kutipan dari konfigurasi apache satu server:

# 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

Apa yang Anda tidak dapat benar-benar tahu dari ini adalah bahwa antara setiap bidang adalah karakter tab literal (\ t). Ini berarti bahwa jika saya ingin melakukan beberapa analisis dengan Python, mungkin menunjukkan status non-200 misalnya, saya dapat melakukan ini:

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

Atau jika saya ingin melakukan 'siapa yang men-hotlink gambar?' itu akan

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

Untuk jumlah IP dalam log akses, contoh sebelumnya:

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

menjadi sesuatu seperti ini:

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

Lebih mudah untuk membaca dan memahami, dan jauh lebih murah secara komputasi (tanpa regex) yang, pada 9 GB log, membuat perbedaan besar dalam berapa lama. Ketika ini menjadi BENAR-BENAR rapi adalah jika Anda ingin melakukan hal yang sama untuk agen-Pengguna. Jika log Anda dibatasi oleh ruang, Anda harus melakukan pencocokan ekspresi reguler atau pencarian string dengan tangan. Dengan format ini, mudah:

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

Persis sama dengan di atas. Sebenarnya, setiap ringkasan yang ingin Anda lakukan pada dasarnya sama persis.

Mengapa saya harus menghabiskan CPU sistem saya pada awk dan grep ketika cut akan melakukan persis apa yang saya inginkan pesanan besarnya lebih cepat?

Dan Udey
sumber
2
Contoh Anda untuk format baru sebenarnya masih terlalu rumit - jumlah IP menjadi cut -f 3 log | uniq -c | sort -n, agen pengguna cut -f 8 log | uniq -c | sort -n.
Creshal
Anda benar, itu lebih sederhana. Saya telah memperbarui contoh untuk mencerminkan itu.
Dan Udey
"cat file | grep string" tidak berguna, mengapa tidak "grep string file"?
c4f4t0r
2
Saya tidak punya alasan, dan telah memperbarui contoh yang sesuai.
Dan Udey
15

Lupakan awk dan grep. Lihat asql . Mengapa menulis skrip yang tidak dapat dibaca ketika Anda dapat menggunakan sintaks seperti sql untuk menanyakan file log. Misalnya.

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;
Vihang D
sumber
Menarik, tetapi Anda mungkin mengalami masalah jika log Anda terlalu besar, saya akan berpikir. Juga seberapa baik ia mengatasi format log kustom?
Vagnerr
Saya mencobanya saat ini, waktu buka sangat lambat (setidaknya dalam versi 0.9). Memuat log 200MB butuh lebih dari lima menit ..
aseques
Saya harus mengatakan bahwa setelah waktu buka (butuh sekitar 15 menit), sintaks program ini sangat bagus, Anda dapat mengurutkan, menghitung, dan mengelompokkan berdasarkan. Sangat bagus.
aseques
Apache HTTPD memiliki metode yang dengannya Anda dapat secara efektif mengirim log ke database. Ya, menulis mungkin memakan waktu lama, tetapi proksi berulir mungkin melakukan hal yang benar di tengah. Bagaimanapun, itu akan membuat log kueri dalam sintaks seperti SQL jauh lebih cepat. Tidak ada pemuatan yang terlibat juga - server database selalu "ON".
nearora
6

Berikut ini adalah skrip untuk menemukan url teratas, rujukan teratas dan agen pengguna teratas dari entri log N terbaru

#!/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

Sumber

anoopjohn
sumber
4

untuk jumlah IP dalam log akses:

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

Agak jelek, tapi berhasil. Saya juga menggunakan yang berikut ini dengan netstat (untuk melihat koneksi aktif):

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

Mereka adalah beberapa "one liners" favorit saya :)

f4nt
sumber
3

Membuat daftar pertanyaan umum akan menjadi indeks yang bagus untuk jawaban atas pertanyaan ini. Pertanyaan umum saya adalah:

  • mengapa hitrate itu berubah?
  • mengapa waktu respons keseluruhan meningkat? '.

Saya melihat perubahan seperti itu dengan memonitor halaman status server (via mod_status) untuk hitrate dan perkiraan waktu respons untuk permintaan yang aktif dan yang baru saja diselesaikan (mengetahui sepenuhnya bahwa saya kehilangan setumpuk data yang besar, tetapi sampelnya cukup bagus).

Saya menggunakan arahan LogFormat berikut (% T sangat berguna)

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

Saya mencari sebab-akibat dan apa yang terjadi pertama kali ... biasanya tentang himpunan bagian pola tertentu dalam log saya, jadi saya perlu mengetahui yang berikut untuk setiap pola / ekspresi reguler yang diberikan:

  • hitcounts per interval (menit atau jam) untuk pola tertentu (alamat ip atau string atau parameter cgi, dll)
  • histogram perkiraan waktu respons (menggunakan% T parameter)

Saya biasanya menggunakan perl, karena pada akhirnya menjadi cukup kompleks untuk menjadi berharga.


Contoh non-perl adalah quickrate hitrate per menit untuk kode status non-200:

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

Ya saya selingkuh dengan grep itu, menganggap kutipan-ruang-200-cocok hanya kode status http .... bisa menggunakan awk atau perl untuk mengisolasi bidang hanya perlu diingat itu bisa tidak akurat.


Contoh yang lebih kompleks dalam perl mungkin untuk memvisualisasikan perubahan dalam hitrate untuk suatu pola.

Ada banyak yang harus dikunyah dalam skrip di bawah ini, terutama jika Anda tidak terbiasa dengan perl.

  • membaca stdin sehingga Anda dapat menggunakan bagian dari log Anda, gunakan ekor (terutama dengan ekor -f), dengan atau tanpa greps dan penyaringan lainnya ...
  • menipu ekstraksi cap waktu zaman dengan hack dari regex dan penggunaan Date :: Manip
  • Anda dapat memodifikasinya hanya sedikit untuk mengekstrak waktu respons atau data sewenang-wenang lainnya

kode berikut:

#!/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
    );
}

Jika Anda hanya ingin memproses metrik standar, checkout

  • 'mergelog' untuk mengumpulkan semua log Anda (jika Anda memiliki banyak apache di belakang load balancer) dan
  • webalizer (atau awstats atau penganalisa umum lainnya).
ericslaw
sumber
3

Di sini contoh 'sed' saya, ia membaca format default dari apache logs dan mengubahnya menjadi sesuatu yang lebih nyaman untuk pemrosesan otomatis. Seluruh baris didefinisikan sebagai ekspresi reguler, variabel disimpan dan ditulis ke keluaran dengan '#' sebagai pemisah.

Notasi input yang disederhanakan adalah:% s% s% s [% s] "% s"% s% s "% s" "% s"

Contoh input line: xx.xx.xx.xx - - [29 / Mar / 2011: 12: 33: 02 +0200] "DAPATKAN /index.html HTTP / 1.0" 200 9443 "-" "Mozilla / 4.0"

Contoh output line: 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'

Rasakan kekuatan ekspresi reguler :-)

Keris
sumber
Ini membuat pemrosesan dengan AWK menjadi mudah. Sedang mencari cara cepat untuk mengatur pembatas umum dan ini berhasil.
Citricguy
Saya telah merasakan kekuatan regex dan hanya ingin meneruskan tweak saya sendiri, yang memotong "HTML / 1.1" dan memisahkan protokol (dengan cara yang mungkin tidak sesuai standar) ke dalam bidangnya sendiri. Selamat menikmati: `` `cat access.log | sed 's /^(.*) (. *) (. *) [(. *)] \ "([[: alpha:]] \ +) (. *) HTTP \ / 1 \ .1 \" ( . *) (. *) \ "(. *) \" \ "(. *) \" $ / \ 1 # \ 2 # \ 3 # \ 4 # \ 5 # \ 6 # \ 7 # \ 8 # \ 9 # \ 10 / g '`` `
Josh Rumbut
2

Saya sering menggunakan awk dengan mengekor atau melampirkan file. Setiap malam saya mengirimkan sendiri laporan web untuk setiap server. Bergantung pada file log Anda dan LogFormat Anda, Anda perlu mengedit beberapa liner yang sesuai untuk Anda ...

Ini contoh sederhana:

Jika saya ingin mengekstrak log di server saya hanya untuk 404/500 kode status saya akan melakukan ini:

# $6 is the status code in my log file

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

<snip>

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>

Michael Steinfeld
sumber
2

Siapa yang menautkan panas gambar Anda:

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

Hal yang saya cenderung lakukan sebagian besar waktu adalah membaca bagian-bagian log berdasarkan waktu, jadi saya menulis skrip berikut menggunakan sed untuk mencabut periode yang saya minati, ini bekerja pada setiap file log yang saya datangi menemukan dan dapat menangani log yang diarsipkan juga.

#! / bin / bash
Script ini harus mengembalikan satu set garis antara 2 nilai, tujuan utamanya adalah untuk mencari file log antara 2 kali
Penggunaan #Script: file logship.sh "start" "stop"

#Jika file berisi "/" dalam rentang tanggal 2 baris berikut tambahkan karakter pelarian sehingga pencarian dapat dilakukan untuk karakter tersebut
start = $ (echo "$ 1" | sed 's / \ // \\\ // g')
stop = $ (echo "$ 2" | sed 's / \ // \\\ // g')

zip = $ (echo "$ 3" | grep -c "gz $") # mencari tahu apakah file tersebut di-zip atau tidak

if ["$ zip zip" == "1"]; lalu #Jika file tersebut di-zip lalu lewati saja melalui zcat sebelum sed
        zcat $ 3 | sed -n "/ $ start /, / $ stop / p";
lain
        sed -n "/ $ start /, / $ stop / p" $ 3; #Jika itu tidak zip hanya menjalankan sed
fi
Chris
sumber
1

Meskipun tidak sed atau awk, ada dua hal yang menurut saya berguna untuk menangani file log apache dan icecast.

AWStats memiliki skrip yang sangat berguna yang disebut logresolvemerge.pl yang akan menggabungkan beberapa file log terkompresi atau tidak terkompresi, strip dupes dan urutkan berdasarkan timestamp. Itu juga dapat melakukan pencarian DNS dan dikonfigurasi untuk menjalankan multithreaded. Ini sangat berguna saat menggunakan dengan awstats karena awstats tidak dapat menambahkan baris log dengan stempel waktu lebih lama dari database saat ini, jadi semua harus ditambahkan secara berurutan, tetapi itu sangat mudah karena Anda hanya membuang semua yang ada di logresolvemerge.pl dan semuanya muncul dengan baik.

sed dan awk cukup buruk dalam menangani kurma karena mereka umumnya memperlakukan mereka sebagai string. awk memiliki beberapa fungsi waktu dan tanggal, tetapi mereka tidak banyak. Misalnya mengekstraksi rentang garis antara dua stempel waktu sulit jika stempel waktu yang tepat tidak muncul dalam file (bahkan jika nilai di antara mereka melakukannya) - contoh Chris 'memiliki masalah ini persis. Untuk mengatasinya, saya menulis skrip PHP yang melaporkan rentang cap waktu file log dan juga dapat mengekstraksi potongan berdasarkan rentang cap waktu, menggunakan format tanggal atau waktu apa pun yang Anda suka (tidak perlu mencocokkan format cap waktu file log).

Untuk mempertahankan topik ini, berikut adalah beberapa contoh berguna: Dapatkan total jumlah byte yang dilayani dari apache atau log icecast:

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

Dapatkan total jumlah detik yang terhubung dari log icecast:

cat access.log | awk '{ sum += $13 } END { print sum }'
Sinkron
sumber
+1 untuk log apache penjumlahan byte sederhana dengan awk
rymo
0

Memulihkan utas lama ini, setelah menyerah pada asql untuk file log besar, mencari solusi againg, juga di serverfault, saya menemukan tentang wtop di sini ini adalah alat opensource, yang mampu melakukan pemantauan langsung atau memproses log dan mendapatkan statistik (atas N), sangat fleksibel dan kuat, tempat resmi ada di sini

aseques
sumber