it-swarm-vi.tech

Bạn có bất kỳ tập lệnh awk và grep hữu ích nào để phân tích nhật ký Apache không?

Tôi có thể sử dụng máy phân tích nhật ký, nhưng thường thì tôi cần phân tích nhật ký web gần đây để xem những gì đang xảy ra vào lúc này.

Đôi khi tôi làm những việc như tìm ra 10 ips hàng đầu yêu cầu một tệp nhất định

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

Bạn có gì trong hộp công cụ của bạn?

70
deadprogrammer

Bạn có thể làm khá nhiều thứ với các tệp nhật ký Apache chỉ với awk. Các tệp nhật ký Apache về cơ bản được phân tách khoảng trắng và bạn có thể giả vờ các trích dẫn không tồn tại và truy cập bất kỳ thông tin nào bạn quan tâm theo số cột. Lần duy nhất điều này bị hỏng là nếu bạn có định dạng nhật ký kết hợp và quan tâm đến tác nhân người dùng, tại thời điểm đó bạn phải sử dụng dấu ngoặc kép (") làm dấu tách và chạy lệnh awk riêng biệt. Sau đây sẽ cho bạn thấy IP của mỗi người dùng yêu cầu trang chỉ mục được sắp xếp theo số lần truy cập:

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

$ 7 là url được yêu cầu. Bạn có thể thêm bất cứ điều kiện nào bạn muốn lúc đầu. Thay thế '$ 7 == "/" bằng bất kỳ thông tin nào bạn muốn.

Nếu bạn thay thế $ 1 in (ipcount [$ 1] ++), thì bạn có thể nhóm kết quả theo các tiêu chí khác. Sử dụng $ 7 sẽ hiển thị những trang nào được truy cập và tần suất. Tất nhiên sau đó bạn sẽ muốn thay đổi điều kiện ngay từ đầu. Phần sau đây sẽ hiển thị những trang nào được người dùng truy cập từ một IP cụ thể:

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

Bạn cũng có thể dẫn đầu ra thông qua sắp xếp để có được kết quả theo thứ tự, như là một phần của lệnh Shell, hoặc trong chính tập lệnh awk:

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

Cái sau sẽ hữu ích nếu bạn quyết định mở rộng tập lệnh awk để in ra thông tin khác. Tất cả chỉ là vấn đề bạn muốn tìm hiểu. Chúng nên đóng vai trò là điểm khởi đầu cho bất cứ điều gì bạn quan tâm.

54
Mark

Một điều tôi chưa từng thấy ai làm, vì những lý do mà tôi không thể tưởng tượng được, là thay đổi định dạng tệp nhật ký Apache thành phiên bản dễ phân tích cú pháp hơn với thông tin thực sự quan trọng với bạn.

Ví dụ: chúng tôi không bao giờ sử dụng xác thực cơ bản HTTP, vì vậy chúng tôi không cần phải ghi nhật ký các trường đó. Tôi am quan tâm đến việc mỗi yêu cầu sẽ phục vụ trong bao lâu, vì vậy chúng tôi sẽ thêm nó vào. Đối với một dự án, chúng tôi cũng muốn biết (trên bộ cân bằng tải của chúng tôi) nếu bất kỳ máy chủ nào đang phục vụ yêu cầu chậm hơn những người khác, vì vậy chúng tôi đăng nhập tên của máy chủ mà chúng tôi ủy quyền trở lại.

Đây là một đoạn trích từ cấu hình Apache của một máy chủ:

# 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

Điều bạn không thể thực sự nói từ điều này là giữa mỗi trường là một ký tự tab theo nghĩa đen (\ t). Điều này có nghĩa là nếu tôi muốn thực hiện một số phân tích trong Python, có thể hiển thị các trạng thái không 200 chẳng hạn, tôi có thể làm điều này:

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

Hoặc nếu tôi muốn làm 'ai đang hotlinking hình ảnh?' nó sẽ là

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

Đối với số lượng IP trong nhật ký truy cập, ví dụ trước:

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

trở thành một cái gì đó như thế này:

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

Dễ đọc và dễ hiểu hơn, và ít tốn kém hơn về mặt tính toán (không có regex), trên nhật ký 9 GB, tạo ra sự khác biệt rất lớn trong thời gian sử dụng. Khi điều này thực sự gọn gàng là nếu bạn muốn làm điều tương tự cho các Tác nhân người dùng. Nếu nhật ký của bạn được phân cách bằng dấu cách, bạn phải thực hiện một số tìm kiếm biểu thức chính quy hoặc tìm kiếm chuỗi bằng tay. Với định dạng này, thật đơn giản:

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

Chính xác như trên. Trong thực tế, bất kỳ tóm tắt nào bạn muốn làm về cơ bản là giống hệt nhau.

Tại sao tôi lại dành CPU của hệ thống của mình cho awk và grep khi bị cắt sẽ thực hiện chính xác những gì tôi muốn các đơn đặt hàng có cường độ nhanh hơn?

24
Dan Udey

Hãy quên đi awk và grep. Hãy xem asql . Tại sao viết các tập lệnh không thể đọc được khi bạn có thể sử dụng cú pháp như sql để truy vấn logfile. Ví dụ.

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

Dưới đây là tập lệnh để tìm các url hàng đầu, người giới thiệu hàng đầu và người dùng hàng đầu từ các mục nhật ký N gần đây

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

Nguồn

6
anoopjohn

cho số lượng IP trong nhật ký truy cập:

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

Nó hơi xấu, nhưng nó hoạt động. Tôi cũng sử dụng như sau với netstat (để xem các kết nối hoạt động):

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

Chúng là một trong những "lớp lót" yêu thích của tôi :)

5
f4nt

Dưới đây là ví dụ 'sed' của tôi, nó đọc định dạng mặc định của nhật ký Apache và chuyển đổi nó thành thứ gì đó thuận tiện hơn để xử lý tự động. Toàn bộ dòng được định nghĩa là biểu thức chính quy, các biến được lưu và ghi vào đầu ra với '#' là dấu phân cách.

Ký hiệu đơn giản của đầu vào là:% s% s% s [% s] "% s"% s% s "% s" "% s"

Ví dụ dòng đầu vào: xx.xx.xx.xx - - [29/tháng 3 năm 2011: 12: 33: 02 +0200] "GET /index.html HTTP/1.0" 200 9443 "-" "Mozilla/4.0"

Dòng đầu ra ví dụ: xx.xx.xx.xx # - # - # 29/tháng 3 năm 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'

Cảm nhận sức mạnh của biểu thức thông thường :-)

3
Kris

Xây dựng một danh sách các câu hỏi phổ biến sẽ là một chỉ số tuyệt vời cho câu trả lời cho câu hỏi này. Câu hỏi phổ biến của tôi là:

  • tại sao hitrate thay đổi?
  • tại sao tổng thời gian phản hồi lại tăng? '.

Tôi nhận thấy những thay đổi như vậy bằng cách theo dõi các trang trạng thái máy chủ (thông qua mod_status) để biết thời gian phản hồi và thời gian phản hồi gần đúng cho các yêu cầu đang hoạt động và gần đây đã hoàn thành (biết rõ rằng tôi bỏ lỡ một đống dữ liệu khổng lồ, nhưng các mẫu đủ tốt).

Tôi sử dụng chỉ thị LogFormat sau đây (% T thực sự hữu ích)

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

Tôi đang tìm kiếm nguyên nhân và những gì đã xảy ra đầu tiên ... thường là về các tập hợp con cụ thể của các mẫu trong nhật ký của tôi, vì vậy tôi cần biết những điều sau đây cho bất kỳ mẫu/biểu thức chính quy cụ thể nào:

  • số lần truy cập trên mỗi khoảng (phút hoặc giờ) cho một mẫu nhất định (địa chỉ ip hoặc chuỗi cgi hoặc tham số, v.v.)
  • biểu đồ thời gian đáp ứng gần đúng (sử dụng tham số% T)

Tôi thường sử dụng Perl, vì cuối cùng nó cũng đủ phức tạp để có giá trị.


Một ví dụ không phải là Perl sẽ là một hitrate nhanh mỗi phút cho các mã trạng thái không 200:

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

Có, tôi đang gian lận với grep đó, giả sử không gian trích dẫn không gian 200 chỉ phù hợp với mã trạng thái http .... có thể sử dụng awk hoặc Perl để cô lập trường chỉ cần nhớ rằng nó có thể không chính xác.


Một ví dụ phức tạp hơn trong Perl có thể là hình dung một sự thay đổi trong hitrate cho một mẫu.

Có rất nhiều thứ để nhai trong kịch bản dưới đây, đặc biệt nếu bạn không hài lòng với Perl.

  • đọc stdin để bạn có thể sử dụng các phần của nhật ký của mình, sử dụng đuôi (đặc biệt là với đuôi -f), có hoặc không có greps và lọc khác ...
  • gian lận trích xuất dấu thời gian Epoch với hack regex và sử dụng Date :: Manip
  • bạn chỉ có thể sửa đổi nó một chút để trích xuất thời gian phản hồi hoặc dữ liệu tùy ý khác

mã sau:

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

Nếu bạn chỉ muốn xử lý số liệu tiêu chuẩn, hãy kiểm tra

  • 'mergelog' để có được tất cả các bản ghi của bạn với nhau (nếu bạn có nhiều lỗi sau bộ cân bằng tải) và
  • webalizer (hoặc awstats hoặc phân tích phổ biến khác).
3
ericslaw

Ai đang nóng liên kết hình ảnh của bạn:

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

Tôi sử dụng awk rất nhiều bằng cách cắt đuôi hoặc chỉnh sửa tập tin. Mỗi đêm tôi cung cấp cho mình một báo cáo web cho mỗi máy chủ. Tùy thuộc vào tệp nhật ký của bạn và LogFormat của bạn, bạn sẽ cần chỉnh sửa một số lớp lót để hoạt động cho bạn ...

Đây là một ví dụ đơn giản:

Nếu tôi muốn theo dõi nhật ký trên máy chủ của mình chỉ với mã trạng thái 404/500 tôi sẽ làm điều này:

# $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>

2
Michael Steinfeld

Mặc dù không phải là sed hay awk, có hai điều tôi thấy hữu ích khi xử lý các tệp nhật ký Apache và icecast.

AWStats có một tập lệnh rất hữu ích được gọi là logresolvemerge.pl sẽ kết hợp nhiều tệp nhật ký được nén hoặc không nén, tách bản sao và sắp xếp theo dấu thời gian. Nó cũng có thể thực hiện tra cứu DNS và được cấu hình để chạy đa luồng. Nó đặc biệt hữu ích khi sử dụng với awstats vì awstats không thể thêm các dòng nhật ký có dấu thời gian cũ hơn cơ sở dữ liệu hiện tại, vì vậy tất cả phải được thêm theo thứ tự, nhưng điều đó rất dễ dàng khi bạn chỉ cần tặc lưỡi mọi thứ tại logresolvemerge.pl và tất cả đều bật ra độc đáo.

sed và awk khá tệ trong việc xử lý ngày vì họ thường coi chúng là chuỗi. awk có một số chức năng thời gian và ngày, nhưng chúng không nhiều. Ví dụ: trích xuất một phạm vi các dòng giữa hai dấu thời gian là khó nếu các dấu thời gian chính xác đó không xảy ra trong tệp (ngay cả khi các giá trị giữa chúng có) - ví dụ của Chris có chính xác vấn đề này. Để giải quyết vấn đề đó, tôi đã viết a PHP script báo cáo phạm vi dấu thời gian của tệp nhật ký và cũng có thể trích xuất một đoạn theo phạm vi dấu thời gian, sử dụng bất kỳ định dạng ngày hoặc giờ nào bạn muốn (không cần phải khớp với định dạng dấu thời gian của tệp nhật ký).

Để giữ chủ đề này, đây là một số ý tưởng hữu ích: Nhận tổng số byte được cung cấp từ nhật ký Apache hoặc icecast:

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

Lấy tổng số giây được kết nối từ nhật ký icecast:

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

Điều tôi có xu hướng làm hầu hết thời gian là đọc các phần của nhật ký dựa trên thời gian, vì vậy tôi đã viết đoạn script sau bằng cách sử dụng sed để rút ra khoảng thời gian tôi quan tâm, nó hoạt động trên mọi tệp nhật ký tôi đã đến trên và có thể xử lý các bản ghi lưu trữ là tốt.

[.__.] #!/bin/bash [.__.] # Tập lệnh này sẽ trả về một tập hợp các dòng giữa 2 giá trị, mục đích chính là để tìm kiếm tệp nhật ký trong khoảng 2 lần [.__.] # Sử dụng tập lệnh: logship.sh "bắt đầu" "dừng" tệp [.__.] [.__.] # Nếu tệp chứa bất kỳ "/" nào trong phạm vi ngày, 2 dòng sau sẽ thêm ký tự thoát để có thể thực hiện tìm kiếm cho những mục đó ký tự [.__.] start = $ (echo "$ 1" | sed 's/\ // \\\ // g') [.__.] stop = $ (echo "$ 2" | sed 's/\//\\\//g')[.__.[[.__.[zipped=$(echo "$ 3" | grep -c "gz $") #f hình ra nếu tập tin được nén hay không [.___ ] [.__.] nếu ["$ đã nén" == "1"]; sau đó #Nếu tệp được nén, sau đó chuyển qua zcat trước sed [.__.] zcat $ 3 | sed -n "/$start/,/$stop/p";[.__.[else[.__.] sed -n"/$ start /,/$ stop/p "$ 3; # nếu nó không được nén chỉ chạy sed [.__.] fi [.__.]
1
Chris

Khôi phục chủ đề cũ này, sau khi từ bỏ asql cho các tệp nhật ký lớn, tìm kiếm một giải pháp, cũng như trong serverfault, tôi đã tìm thấy về wtop ở đây đó là một công cụ mã nguồn mở, có khả năng giám sát trực tiếp hoặc nhật ký quá trình và nhận số liệu thống kê (top N), rất linh hoạt và mạnh mẽ, vị trí chính thức là tại đây

0
aseques