아파치 로그를 구문 분석하는 데 유용한 awk 및 grep 스크립트가 있습니까? [닫은]


69

로그 분석기를 사용할 수 있지만, 현재 발생하는 상황을 확인하기 위해 최근 웹 로그를 구문 분석해야하는 경우가 종종 있습니다.

때로는 특정 파일을 요청하는 상위 10 IP를 파악하는 것과 같은 일을합니다.

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

툴박스에 무엇이 있습니까?


1
나는 실제로 데이터베이스에 제출하기 위해 모든 아파치 사용자 정의 로그를 개별 필드로 구문 분석하기 위해 직접 작성한이 큰 아름다운 정규식을 가지고있었습니다. 나는 더 이상 그것을 가지고 있지 않다고 스스로를 차고 있습니다. 하나의 라이너였습니다. 각 로그 요소에 대해 하나의 변수를 다시 제공했습니다. 그런 다음 MySQL에 삽입하고있었습니다. 찾은 경우 여기에 게시합니다.
Kyle Hodgson

답변:


54

awk만으로 아파치 로그 파일로 거의 모든 것을 할 수 있습니다. Apache 로그 파일은 기본적으로 공백으로 구분되며 따옴표가없는 것으로 가장하고 열 번호로 관심있는 정보에 액세스 할 수 있습니다. 이 분류가 유일한 시간은 결합 된 로그 형식이 있고 사용자 에이전트에 관심이있는 경우에만 따옴표 ( ")를 구분 기호로 사용하고 별도의 awk 명령을 실행해야합니다. 다음은 IP의 IP를 보여줍니다. 조회수를 기준으로 색인 페이지를 요청하는 모든 사용자 :

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

요청한 URL은 $ 7입니다. 처음에 원하는 조건을 추가 할 수 있습니다. '$ 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

쉘 명령의 일부 또는 awk 스크립트 자체에서 결과를 순서대로 가져 오기 위해 sort를 통해 출력을 파이프 할 수도 있습니다.

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

후자는 다른 정보를 인쇄하기 위해 awk 스크립트를 확장하기로 결정한 경우 유용합니다. 모두 당신이 찾고자하는 문제입니다. 이것들은 당신이 관심있는 무엇이든을위한 출발점으로 작용해야합니다.


예, 미친 긴 고양이 / 그렙 / awk 파이프 라인을 보는 것은 항상 이상해 보입니다. 일단 당신이 어색하면 일반적으로 충분합니다. 원래 게시물의 처음 세 절은 "awk '/ request_to_file_foo / {print $ 1}'foo.log"로 간단하게 작성할 수 있습니다. awk는 파일을 입력으로 사용할 수 있으며 정규식을 사용하여 관심있는 줄을 알 수 있습니다.
Zac Thompson

우아하고 간단합니다. 좋은.
Olivier Dulac

"authuser"(3) 필드에 공백이 허용되어 모든 것을 망가뜨릴 수 있으니주의하십시오. 개인적으로 우리가 이것을 할 수 없도록 금지되어야한다고 생각합니다 ;-)
Mandark

23

내가 상상할 수없는 이유로 다른 사람이 본 적이없는 것 중 하나는 Apache 로그 파일 형식을 실제로 중요한 정보가 포함 된보다 쉽게 ​​구문 분석 가능한 버전으로 변경하는 것입니다.

예를 들어 HTTP 기본 인증을 사용하지 않으므로 해당 필드를 기록 할 필요가 없습니다. 나는 오전 각 요청에 봉사하는 데 걸리는 시간에 관심이, 그래서 우리는에 그것을 추가 할 것입니다. 하나 개의 프로젝트를 위해, 우리는 또한 모든 서버가 느린 다른 사람보다 요청을 처리하는 경우 (우리의 부하 분산에) 알고 싶은, 그래서 우리는 이름을 기록 프록시하는 서버의

다음은 한 서버의 아파치 구성에서 발췌 한 것입니다.

# 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)라는 것입니다. 이것은 파이썬에서 일부 분석을 원한다면 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

읽기 쉽고 이해하기 쉽고 계산 비용이 훨씬 적게 드는 (정규식 없음) 9GB 로그에서 소요 시간이 크게 달라집니다. 이것이 REALLY 깔끔해지면 사용자 에이전트에 대해 동일한 작업을 수행하려는 것입니다. 로그가 공백으로 구분 된 경우 정규식 일치 또는 문자열 검색을 직접 수행해야합니다. 이 형식을 사용하면 간단합니다.

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

위와 정확히 동일합니다. 실제로, 원하는 요약은 본질적으로 동일합니다.

왜 지구상에서 시스템의 CPU를 awk와 grep에 소비하면 cut이 원하는 크기만큼 빠르게 처리 할 수 ​​있습니까?


2
새 형식에 대한 예는 실제로 여전히 복잡합니다. IP 수는 cut -f 3 log | uniq -c | sort -n사용자 에이전트가 cut -f 8 log | uniq -c | sort -n됩니다.
Creshal

네 말이 맞아, 더 간단 해 이를 반영하기 위해 예제를 업데이트했습니다.
Dan Udey

"cat file | grep string"은 쓸모가 없습니다. 왜 "grep string file"입니까?
c4f4t0r

2
나는 변명의 여지가 없으며 그에 따라 예제를 업데이트했습니다.
Dan Udey

15

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;

흥미롭지 만 로그가 특히 큰 경우 문제가 발생할 수 있습니다. 또한 사용자 정의 로그 형식에 얼마나 잘 대처합니까?
Vagnerr 2016 년

나는 현재 그것을 시도하고있다.로드 시간이 너무 느리다 (적어도 버전 0.9에서는). 200Mb 로그를로드하는 데 5 분 이상 소요됩니다.
aseques

로드 시간 (약 15 분 소요) 후에이 프로그램의 구문이 훌륭하면 정렬, 계산 및 그룹화 할 수 있습니다. 정말 좋아요
aseques

Apache HTTPD에는 효과적으로 로그를 데이터베이스에 보낼 수있는 방법이 있습니다. 예, 쓰기에는 시간이 오래 걸릴 수 있지만 스레드 프록시는 중간에 끼워진 올바른 작업을 수행 할 수 있습니다. 어쨌든 그것은 구문과 같은 SQL의 로그 쿼리를 훨씬 빠르게 만듭니다. 데이터베이스 서버가 영구적으로 "켜짐"상태입니다.
nearora

6

최근 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

출처


4

액세스 로그의 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

그들은 내가 좋아하는 "한 라이너"중 일부입니다 :)


3

일반적인 질문 목록을 작성하면이 질문에 대한 답변을 얻을 수 있습니다. 가장 일반적인 질문은 다음과 같습니다.

  • 왜 적중률이 변경 되었습니까?
  • 전체 응답 시간이 왜 증가합니까? '.

활성 및 최근 완료된 요청에 대한 적중률 및 대략적인 응답 시간에 대해 서버 상태 페이지 (mod_status를 통해)를 모니터링하여 이러한 변경 사항을 알 수 있습니다 (거대한 데이터 더미가 누락되었지만 샘플은 충분하다는 것을 잘 알고 있음).

다음 LogFormat 지시문을 사용합니다 (% T가 정말 유용합니다)

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

원인-결과와 가장 먼저 일어난 일을 찾고 있습니다. 일반적으로 내 로그의 특정 패턴 하위 집합에 대한 것이므로 주어진 패턴 / 정규 표현식에 대해 다음을 알아야합니다.

  • 주어진 패턴 (ip 주소 또는 cgi 문자열 또는 매개 변수 등)에 대한 간격 당 분당 횟수 (분 또는 시간)
  • 대략적인 응답 시간 히스토그램 (% T 매개 변수 사용)

나는 일반적으로 가치가있을만큼 복잡해지기 때문에 펄을 사용합니다.


펄이 아닌 예제는 200이 아닌 상태 코드에 대한 분당 quickie hitrate입니다.

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

따옴표-공간 -200- 공간이 http 상태 코드와 만 일치한다고 가정하고 그 grep을 사용하여 부정 행위하고 있습니다 .... awk 또는 perl을 사용하여 필드를 분리 할 수 ​​있습니다.


펄에서보다 복잡한 예는 패턴의 적중률 변화를 시각화하는 것입니다.

펄에 익숙하지 않은 경우 아래 스크립트에서 씹을 것이 많습니다.

  • stdin을 읽으므로 로그의 일부를 사용하고 꼬리 (특히 tail -f와 함께)를 사용하고 greps 및 기타 필터링을 사용하거나 사용하지 않고 ...
  • 정규식 해킹 및 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
    );
}

표준 측정 항목 만 처리하려면 Checkout

  • 로드 밸런서 뒤에 여러 개의 아파치가있는 경우 모든 로그를 가져 오는 'mergelog'
  • 웨비나이 저 (또는 awstats 또는 기타 일반적인 분석기).

3

여기 내 'sed'예는 아파치 로그의 기본 형식을 읽고 자동 처리를 위해 더 편리한 것으로 변환합니다. 전체 행은 정규식으로 정의되고 변수는 '#'을 구분 기호로 사용하여 출력에 저장 및 기록됩니다.

입력의 단순화 된 표기법은 다음과 같습니다. % 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'

정규 표현식의 힘을 느껴보십시오 :-)


이로 인해 AWK 처리가 쉬워졌습니다. 일반적인 딜리 미네 이터를 설정하는 빠른 방법을 찾고 있었는데 이것이 문제가되었습니다.
Citricguy

나는 정규 표현식의 힘을 느꼈고 내 자신의 조정을 통과하고 싶었습니다. "HTML / 1.1"을 잘라 내고 프로토콜을 아마도 비표준 호환 방식으로 자체 필드로 분리했습니다. 즐기십시오 :```cat access.log | sed 's /^(.*) (. *) (. *) [(. *)] \ "([[: alpha :]] \ +) (. *) HTTP \ / 1 \ .1 \"( . *) (. *) \ "(. *) \"\ "(. *) \"$ / \ 1 # \ 2 # \ 3 # \ 4 # \ 5 # \ 6 # \ 7 # \ 8 # \ 9 # \ 10 / g '`
Josh Rumbut

2

나는 파일을 tailing 또는 cat'ing하여 awk를 많이 사용합니다. 매일 밤마다 각 서버에 대한 웹 보고서를 제공합니다. 로그 파일과 LogFormat에 따라 작동하도록 하나의 라이너 중 일부를 편집해야합니다.

다음은 간단한 예입니다.

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

이미지를 핫 링크하는 사람 :

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

1

내가 대부분의 시간을 할애하는 것은 시간을 기준으로 로그 섹션을 읽는 것이므로 관심있는 기간을 꺼내기 위해 sed를 사용하여 다음 스크립트를 작성했습니다. 내가 온 모든 로그 파일에서 작동합니다. 보관 된 로그도 처리 할 수 ​​있습니다.

#! / bin / bash
#이 스크립트는 두 값 사이의 줄 집합을 반환해야합니다. 주된 목적은 두 번 사이의 로그 파일을 검색하는 것입니다.
# 스크립트 사용법 : logship.sh "start" "stop"파일

# 파일에 날짜 범위에 "/"가 포함 된 경우 다음 두 줄은 이스케이프 문자를 추가하여 해당 문자를 검색 할 수 있도록합니다.
start = $ (에코 "$ 1"| sed 's / \ // \\\ // g')
stop = $ (에코 "$ 2"| sed 's / \ // \\\ // g')

zipped = $ (echo "$ 3"| grep -c "gz $") # 파일이 압축되어 있는지 여부를 나타냅니다

[ "$ zipped"== "1"]이면; # 파일이 압축 된 경우 sed 전에 zcat을 통해 전달하십시오.
        zcat $ 3 | sed -n "/ $ start /, / $ stop / p";
그밖에
        sed -n "/ $ start /, / $ stop / p"$ 3; # 압축되지 않은 경우 sed를 실행하십시오.
fi

1

sed 또는 awk가 아니지만 아파치 및 아이스 캐스트 로그 파일을 처리하는 데 유용한 두 가지가 있습니다.

AWStats 에는 logresolvemerge.pl 이라는 매우 유용한 스크립트가 있습니다.이 스크립트 는 여러 압축 또는 압축되지 않은 로그 파일을 결합하고 듀피를 제거하며 타임 스탬프별로 정렬합니다. 또한 DNS 조회를 수행하고 멀티 스레드를 실행하도록 구성 할 수 있습니다. awstats와 함께 사용할 때 특히 유용합니다. awstats는 현재 데이터베이스보다 오래된 타임 스탬프가있는 로그 라인을 추가 할 수 없으므로 모든 항목을 순서대로 추가해야하지만 logresolvemerge.pl에서 모든 항목 을 처리하기 만하면 매우 간단합니다 .

sed와 awk는 일반적으로 문자열로 취급하기 때문에 날짜 처리가 매우 나쁩니다. awk에는 시간과 날짜 기능이 있지만 그다지 많지 않습니다. 예를 들어 파일에서 정확한 타임 스탬프가 발생하지 않으면 (두 타임 스탬프 사이의 행 범위를 추출하는 것은 어렵습니다) 값이 크더라도 Chris의 예에는 정확히이 문제가 있습니다. 이를 처리하기 위해 로그 파일 타임 스탬프 범위를보고하고 원하는 날짜 또는 시간 형식을 사용하여 타임 스탬프 범위별로 청크를 추출 할 수 있는 PHP 스크립트 를 작성했습니다 (로그 파일의 타임 스탬프 형식과 일치하지 않아도 됨).

이 주제를 유지하기 위해 다음과 같은 유용한 어 키즘이 있습니다. 아파치 또는 아이스 캐스트 로그에서 제공되는 총 바이트 수를 가져옵니다.

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

icecast 로그에서 연결된 총 시간 (초)을 가져옵니다.

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

awk로 간단한 바이트 합산 아파치 로그 +1
rymo

0

이 오래된 스레드를 복구, 큰 로그 파일에 대한 asql 포기 후, 또한 저기 serverfault에, 내가 WTOP에 대한 발견, againg 솔루션을 보였다 여기에 (상단 즉 실시간 감시 또는 프로세스 로그를 수행 할 수 있고 통계를 얻을, 그것이 오픈 소스 도구입니다 N) 매우 유연하고 강력한 공식 장소는 여기

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.