아파치 로그를 구문 분석하는 데 유용한 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.