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


70

Posso usar analisadores de log, mas geralmente preciso analisar logs da web recentes para ver o que está acontecendo no momento.

Às vezes, faço coisas como descobrir os 10 ips principais 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?


11
Na verdade, eu tinha esse grande e bonito regex que escrevi à mão para analisar todos os meus logs personalizados do apache em campos individuais para envio a um banco de dados. Estou me chutando por não ter mais. Era um forro; devolvi uma variável para cada elemento de log - então eu estava inserindo no MySQL. Se eu o encontrar, postarei aqui.
Kyle Hodgson

Respostas:


54

Você pode fazer praticamente qualquer coisa com os 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 aspas não existem e acessar as informações de seu interesse pelo número da coluna. O único momento em que isso ocorre é se você possui o formato de log combinado e está interessado nos agentes do usuário; nesse momento, você deve usar aspas (") como separador e executar um comando awk separado. A seguir, serão mostrados os IPs de todo usuário que solicita a página de índice classificada pelo número de hits:

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 desejar no início. Substitua '$ 7 == "/" pelas informações desejadas.

Se você substituir $ 1 em (ipcount [$ 1] ++), poderá agrupar os resultados por outros critérios. Usar US $ 7 mostraria quais páginas foram acessadas e com que frequência. Obviamente, você deseja alterar a condição no início. O seguinte mostraria quais páginas foram acessadas por um usuário a partir 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 através da classificaçã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ê deseja descobrir. Estes devem servir como ponto de partida para o que você estiver interessado.


Sim, sempre parece estranho ver pipelines loucos de gato / grep / awk. Quando você está desanimado, geralmente é o suficiente. As três primeiras cláusulas da postagem original podem ser escritas trivialmente como "awk '/ request_to_file_foo / {print $ 1}' foo.log". O awk pode usar um arquivo como entrada e pode usar o regex para saber com quais linhas se preocupar.
Zac Thompson

Elegante e simples. Boa.
Olivier Dulac

Cuidado, espaços parece permitidos no "authuser" (3) campo, que quebra tudo, e eu pessoalmente acho que deveria ser proibido, para permitir-nos fazer isso ;-)
Mandark

23

Uma coisa que eu nunca vi mais alguém fazer, por razões que não consigo imaginar, é alterar o formato do arquivo de log do Apache para uma versão mais facilmente analisável com as informações que realmente são importantes para você.

Por exemplo, nunca usamos autenticação básica HTTP, portanto, não precisamos registrar esses campos. Eu estou interessado em quanto tempo cada pedido leva a servir, por isso vamos acrescentar que, em. Para um projeto, nós também queremos saber (em nosso balanceador de carga), se todos os servidores estão servindo pedidos mais lento do que outros, então nós registramos o nome do servidor para o qual estamos fazendo 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ê realmente não pode dizer disso é que entre cada campo existe um caractere de tabulação literal (\ t). Isso significa que, se eu quiser fazer alguma análise em Python, talvez mostre status que não seja 200, por exemplo, eu 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 está ligando as imagens?' 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

torna-se algo como isto:

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

Mais fácil de ler e entender e muito menos caro computacionalmente (sem regex) que, em logs de 9 GB, faz uma enorme diferença em quanto tempo leva. Quando isso fica MUITO legal, é se você deseja fazer o mesmo com os agentes do usuário. Se seus logs forem delimitados por espaço, você precisará fazer alguma correspondência de expressão regular ou pesquisa de string 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 exatamente o mesmo.

Por que diabos eu gastaria a CPU do meu sistema em awk e grep quando o cut fará exatamente o que eu quero ordens de magnitude mais rapidamente?


2
Seus exemplos para o novo formato ainda são muito complicados - as contagens de IP se tornam cut -f 3 log | uniq -c | sort -nagentes do usuário cut -f 8 log | uniq -c | sort -n.
Creshal

Você está certo, isso é mais simples. Atualizei os exemplos para refletir isso.
Dan Udey

"arquivo de gato | string grep" é inútil, por que não "arquivo de string grep"?
C4f4t0r

2
Não tenho desculpas e atualizei o exemplo de acordo.
Dan Udey

15

Esqueça o awk e o grep. Confira asql . Por que escrever scripts ilegíveis quando você pode usar sql como sintaxe para consultar o arquivo de log. 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;

Interessante, mas você pode ter problemas se seus logs forem particularmente grandes, eu acho. Além disso, como ele lida com os formatos de log personalizados?
Vagnerr 5/06/09

Estou tentando no momento, o tempo de carregamento é muito lento (pelo menos na versão 0.9). Carregando um 200Mb log está levando mais de cinco minutos ..
aseques

Devo dizer que, após o tempo de carregamento (demorou cerca de 15 minutos), a sintaxe deste programa é ótima, você pode classificar, contar e agrupar. Muito legal.
aseques

O Apache HTTPD possui um método com o qual você pode enviar efetivamente os logs para um banco de dados. Sim, as gravações podem demorar, mas um proxy encadeado pode fazer a coisa certa no meio. De qualquer forma, isso tornará a consulta de logs em um SQL como sintaxe muito mais rápida. Nenhum carregamento envolvido também - o servidor de banco de dados está perpetuamente "ON".
nearora 28/05

6

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

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

Fonte


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 o netstat (para ver as 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 favoritos "one liners" :)


3

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

  • por que o hitrate mudou?
  • por que o tempo de resposta geral está aumentando? '.

Percebo essas alterações monitorando as páginas de status do servidor (via mod_status) quanto à taxa de bits e tempo de resposta aproximado para solicitações ativas e concluídas recentemente (sabendo muito bem que sinto falta de uma pilha enorme 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 procurando causa-efeito e o que aconteceu primeiro ... geralmente sobre subconjuntos específicos de padrões nos meus logs, portanto, 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 ou parâmetros cgi, etc)
  • histogramas de tempo aproximado de resposta (usando o parâmetro% T)

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


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

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

Sim, estou trapaceando com esse grep, presumindo que um espaço de aspas-200-espaço corresponda apenas aos códigos de status http .... poderia usar awk ou perl para isolar o campo, mas lembre-se de que pode ser impreciso.


Um exemplo mais complexo em perl pode ser visualizar uma alteração na taxa de bits de um padrão.

Há muito o que mastigar no script abaixo, especialmente se você não é familiar com o perl.

  • lê stdin para que você possa usar partes de seus logs, usar tail (especialmente com tail -f), com ou sem greps e outros filtros ...
  • engana a extração do timestamp da época com corte 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

código a seguir:

#!/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ê deseja apenas processar métricas padrão, finalize a compra

  • '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).

3

Aqui, meu exemplo 'sed', lê o formato padrão dos logs do apache e o converte em algo mais conveniente para o processamento automático. A linha inteira é definida como expressão regular, as variáveis ​​são salvas e gravadas na 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/^\(.*\) \(.*\) \(.*\) \[\(.*\)\] \"\(.*\)\" \(.*\) \(.*\) \"\(.*\)\" \"\(.*\)\"$/\1#\2#\3#\4#\5#\6#\7#\8#\9/g'

Sinta o poder das expressões regulares :-)


Isso facilitou o processamento com AWK. Estava procurando uma maneira rápida de configurar um deliminador comum e isso acertou em cheio.
Citricguy

Eu senti o poder do regex e só queria passar meu próprio ajuste, que corta o "HTML / 1.1" e separa o protocolo (de uma maneira provavelmente não compatível com os padrões) em seu próprio campo. Aproveite: `` `cat access.log | sed 's /^(.*) (. *) (. *) [(. *)] \ "([[: alpha:]] \ +) (. *) HTTP \ / 1 \ .1 \" ( . *) (. *) \ "(. *) \" (. *) \ "$ / \ 1 # \ 2 # \ 3 # \ 4 # \ 5 # \ 6 # \ 7 # \ 8 # \ 9 # \ 10 / g '' ''
Josh Rumbut

2

Eu uso muito o awk arrastando ou bloqueando o arquivo. Toda noite eu 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 alterar os logs no meu servidor apenas para códigos de status 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}'

<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

Quem está vinculando suas imagens a quente:

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

1

O que costumo fazer na maioria das vezes é ler seções de um log com base no tempo, então escrevi o script a seguir usando sed para extrair o período em que estou interessado, ele funciona em todos os arquivos de log que vim e também pode lidar com os logs arquivados.

#! / bin / bash
#Este script deve retornar um conjunto de linhas entre 2 valores, o objetivo principal é pesquisar um arquivo de log entre 2 vezes
#Script use: logship.sh arquivo "start" "stop"

#Se o arquivo contiver "/" no período, as duas linhas a seguir adicionam o caractere de escape para que a pesquisa possa ser realizada para esses caracteres
start = $ (eco "$ 1" | sed 's / \ // \\\ // g')
stop = $ (eco "$ 2" | sed 's / \ // \\\ // g')

zipado = $ (eco "$ 3" | grep -c "gz $") #figura se o arquivo estiver zipado ou não

if ["$ zipado" == "1"]; # Se o arquivo estiver compactado, passe-o pelo zcat antes de sed
        zcat $ 3 | sed -n "/ $ start /, / $ stop / p";
outro
        sed -n "/ $ start /, / $ stop / p" $ 3; #se não estiver compactado, basta executar o sed
fi

1

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

O AWStats possui um script muito útil chamado logresolvemerge.pl que combina vários arquivos de log compactados ou descompactados, tira dupes e classifica por carimbo de data / hora. Ele também pode fazer pesquisas de DNS e ser configurado para executar multithread. É particularmente útil quando usado com o awstats, porque o awstats não pode adicionar linhas de registro com registros de data e hora mais antigos que o banco de dados atual; portanto, tudo deve ser adicionado em ordem, mas isso é muito fácil, basta você checar tudo em logresolvemerge.pl e tudo sair bem.

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

Para manter este tópico, aqui estão alguns awkisms úteis: Obtenha o número total de bytes exibidos no log do apache ou 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 }'

+1 para log apache-soma byte simples com awk
RYMO

0

Recuperando esse thread antigo, depois de desistir do asql para grandes arquivos de log, procurei uma solução novamente, também em serverfault, descobri sobre o wtop aqui que é uma ferramenta de código aberto, capaz de monitorar ao vivo ou processar logs e obter estatísticas (topo N), muito flexível e poderoso, o local oficial é aqui

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.