Você tem algum script útil de awk e grep para analisar os logs do apache? [fechadas]

64

Eu posso usar analisadores de log, mas muitas vezes preciso analisar os registros da web recentes para ver o que está acontecendo no momento.

Às vezes, faço coisas como descobrir os 10 principais ips que solicitam um determinado arquivo

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

O que você tem na sua caixa de ferramentas?

    
por deadprogrammer 22.05.2009 / 00:14

12 respostas

54

Você pode fazer praticamente qualquer coisa com arquivos de log do apache apenas com o awk. Os arquivos de log do Apache são basicamente separados por espaços em branco, e você pode fingir que as citações não existem e acessar qualquer informação em que esteja interessado pelo número da coluna. A única ocasião em que isso é interrompido é se você tiver o formato de log combinado e estiver interessado em agentes de usuário, nesse ponto, será necessário usar aspas (") como separador e executar um comando awk separado. O seguinte mostrará os IPs de cada usuário que solicita a página de índice classificada pelo número de ocorrências:

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

$ 7 é o URL solicitado. Você pode adicionar as condições que quiser no começo. Substitua o '$ 7 == "/" com qualquer informação que você quiser.

Se você substituir o $ 1 em (ipcount [$ 1] ++), poderá agrupar os resultados por outros critérios. Usando $ 7 mostraria quais páginas foram acessadas e com que frequência. Claro, então você gostaria de alterar a condição no início. O seguinte mostraria quais páginas foram acessadas por um usuário de um IP específico:

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

Você também pode canalizar a saída por meio de ordenação para obter os resultados em ordem, como parte do comando shell ou também no próprio script awk:

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

O último seria útil se você decidisse expandir o script awk para imprimir outras informações. É tudo uma questão do que você quer descobrir. Estes devem servir como ponto de partida para o que você estiver interessado.

    
por 03.06.2009 / 04:21
23

Uma coisa que eu nunca vi ninguém fazer, por razões que eu não consigo imaginar, é mudar o formato do arquivo de log do Apache para uma versão mais fácil de ser analisada com as informações que realmente importam para você.

Por exemplo, nunca usamos autenticação básica HTTP, por isso não precisamos registrar esses campos. Estou interessado em quanto tempo cada solicitação leva para ser veiculado, então vamos adicionar isso. Para um projeto, também queremos saber (em nosso balanceador de carga) se algum servidor estiver atendendo a solicitações mais lentas outros, então registramos o nome do servidor ao qual estamos fazendo o proxy.

Aqui está um trecho da configuração do apache de um servidor:

# 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

O que você não pode dizer a partir disso é que entre cada campo existe um caractere de tabulação literal (\ t). Isso significa que, se eu quiser fazer algumas análises no Python, talvez mostre status não-200, por exemplo, posso fazer isso:

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

Ou se eu quisesse fazer 'quem é imagens hotlinking?' seria

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

Para contagens de IP em um log de acesso, o exemplo anterior:

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

se torna algo assim:

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

Mais fácil de ler e entender, e muito menos computacionalmente caro (sem regex) que, em registros de 9 GB, faz uma enorme diferença em quanto tempo leva. Quando isso fica realmente limpo é se você quiser fazer a mesma coisa para os agentes do usuário. Se os seus logs forem delimitados por espaços, você precisará fazer algumas correspondências de expressões regulares ou pesquisar sequências manualmente. Com este formato, é simples:

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

Exatamente o mesmo que o acima. De fato, qualquer resumo que você queira fazer é essencialmente o mesmo.

Por que eu gastaria a CPU do meu sistema em awk e grep quando o corte faria exatamente o que eu quero mais rapidamente?

    
por 05.06.2009 / 23:46
14

Esqueça o awk e o grep. Confira asql . Por que escrever scripts ilegíveis quando você pode usar a sintaxe do sql para consultar o logfile. Por exemplo.

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;
    
por 05.06.2009 / 04:22
6

Aqui está um script para encontrar os principais URLs, os principais referenciadores e as principais useragents das N entradas de log recentes

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

Source

    
por 27.11.2011 / 15:03
4

para contagens de IP em um log de acesso:

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

É um pouco feio, mas funciona. Eu também uso o seguinte com netstat (para ver conexões ativas):

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

Eles são alguns dos meus "liners" favoritos:)

    
por 22.05.2009 / 04:19
3

Criar uma lista de perguntas comuns seria um ótimo índice para essas respostas a essa pergunta. Minhas perguntas comuns são:

  • por que a taxa de hits mudou?
  • por que o tempo de resposta geral está aumentando? '.

Eu observo essas alterações monitorando as páginas de status do servidor (via mod_status) para taxa de acerto e tempo de resposta aproximado para solicitações ativas e concluídas recentemente (sabendo muito bem que sinto falta de uma enorme pilha de dados, mas as amostras são boas o suficiente).

Eu uso a seguinte diretiva LogFormat (o% T é realmente útil)

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

Estou à procura de causa e efeito e o que aconteceu primeiro ... geralmente sobre subconjuntos específicos de padrões em meus logs, então eu preciso saber o seguinte para qualquer padrão / expressão regular:

  • hitcounts por intervalo (minuto ou hora) para um determinado padrão (endereço IP ou string cgi ou parâmetros, etc)
  • histogramas de tempo de resposta aproximado (usando o parâmetro% T)

Eu geralmente uso o perl, porque eventualmente ele fica complexo o suficiente para valer a pena.

Um exemplo não perl seria uma taxa de acerto rápida por minuto para códigos de status que não são 200:

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

Sim, eu estou trapaceando com esse grep, presumindo que um space-space-200-space corresponda apenas a códigos de status http .... poderia usar awk ou perl para isolar o campo apenas para não ser exato.

Um exemplo mais complexo em perl pode ser visualizar uma mudança na taxa de acerto para um padrão.

Há muito para mastigar no script abaixo, especialmente se você não for familiarizado com o perl.

  • lê stdin para que você possa usar partes de seus logs, use tail (especialmente com tail -f), com ou sem greps e outros filtros ...
  • cheats epoch timestamp extração com hack de um regex e uso de Date :: Manip
  • você poderia modificá-lo apenas um pouco para extrair o tempo de resposta ou outros dados arbitrários
O código

segue:

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

Se você quiser apenas processar métricas padrão, confira

  • 'mergelog' para reunir todos os seus logs (se você tiver vários apaches atrás de um balanceador de carga) e
  • webalizer (ou awstats ou outro analisador comum).
por 05.06.2009 / 23:34
3

Aqui, meu exemplo 'sed', lê o formato padrão dos logs do apache e o converte em algo mais conveniente para processamento automático. A linha inteira é definida como expressão regular, variáveis são salvas e escritas para saída com '#' como separador.

A notação simplificada da entrada é: % s% s% s [% s] "% s"% s% s "% s" "% s"

Exemplo de linha de entrada: xx.xx.xx.xx - - [29 / Mar / 2011: 12: 33: 02 +0200] "GET /index.html HTTP / 1.0" 200 9443 "-" "Mozilla / 4.0"

Exemplo de linha de saída: 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/^\(.*\) \(.*\) \(.*\) \[\(.*\)\] \"\(.*\)\" \(.*\) \(.*\) \"\(.*\)\" \"\(.*\)\"$/########/g'

Sinta o poder das expressões regulares: -)

    
por 29.03.2011 / 17:40
2

Eu uso muito o awk seguindo o arquivo. Toda noite eu me entrego um relatório da web para cada servidor. Dependendo do seu arquivo de log e do seu LogFormat, você precisará editar alguns dos liners para trabalhar para você ....

Aqui está um exemplo simples:

Se eu quiser manter os logs no meu servidor apenas para códigos de status de 404/500, eu faria o seguinte:

# $6 is the status code in my log file

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

< recorte >

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 >

    
por 19.11.2011 / 04:19
2

Quem está criando links para suas imagens:

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

Embora não seja sed ou awk, há duas coisas que achei úteis para lidar com arquivos de log do apache e do icecast.

AWStats tem um script muito útil chamado logresolvemerge.pl que combina vários arquivos de log compactados ou descompactados, distribui dupes e classifica por timestamp. Ele também pode fazer pesquisas de DNS e ser configurado para executar multithread. É particularmente útil ao usar com awstats porque awstats não podem adicionar linhas de registro com registros de data e hora mais antigos que o banco de dados atual, então tudo deve ser adicionado em ordem, mas isso é muito fácil pois você atira tudo em logresolvemerge.pl e tudo sai bem.

sed e awk são muito ruins em lidar com datas porque geralmente as tratam como strings. O awk tem algumas funções de data e hora, mas elas não são muito úteis. Por exemplo, extrair um intervalo de linhas entre dois timestamps é difícil se esses timestamps exatos não ocorrerem no arquivo (mesmo se os valores entre eles ocorrerem) - O exemplo de Chris tem exatamente esse problema. Para lidar com isso, eu escrevi um script PHP que informa os intervalos de timestamp do arquivo de log e também pode extrair um pedaço por intervalo de timestamp, usando qualquer formato de data ou hora que você gosta (não precisa corresponder ao formato de data e hora do arquivo de log).

Para manter isso no tópico, aqui estão alguns awkisms úteis: Obtenha o número total de bytes exibidos do log do apache ou do icecast:

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

Obtenha o número total de segundos conectados a partir de um log do icecast:

cat access.log | awk '{ sum += $13 } END { print sum }'
    
por 19.09.2012 / 15:01
0

A coisa que costumo fazer na maior parte do tempo é ler seções de um log com base no tempo, então eu escrevi o seguinte script usando sed para extrair o período em que estou interessado, ele funciona em todos os arquivos de log que eu vim através e pode lidar com os logs arquivados também.

#!/bin/bash
#This script should return a set of lines between 2 values, the main purpose is for searching a log file between 2 times
#Script usage: logship.sh "start" "stop" file

#If the file contains any "/" in the date range the following 2 lines add the escape character so that the search can be performed for those characters
start=$(echo "$1" | sed 's/\//\\//g')
stop=$(echo "$2" | sed 's/\//\\//g')

zipped=$(echo "$3" | grep -c "gz$")     #figures out if the file is zipped or not

if [ "$zipped" ==  "1" ]; then          #If the file is zipped then pass it through zcat before sed
        zcat $3 | sed -n "/$start/,/$stop/p";
else
        sed -n "/$start/,/$stop/p" $3;  #if it's not zipped just run sed
fi
    
por 29.05.2012 / 00:28
0

Recuperando esse segmento antigo, depois de desistir do asql para grandes arquivos de log, procurei uma solução de againg, também em serverfault, que encontrei sobre o wtop aqui é uma ferramenta opensource, capaz de monitorar ao vivo ou processar logs e obter estatísticas (top N), muito flexível e poderosa, lugar oficial é aqui

    
por 26.08.2013 / 15:18