quinta-feira, 21 de março de 2019

Shell Scripting para Análise de Log do Squid

0 comentários
Encontrei um script shell muiiiito bacana aqui. Eu o escrevi no ano de 2008, quando fazia minha especialização em Administração de Redes GNU/Linux, pela Universidade Federal de Lavras (UFLA).
O professor havia passado um arquivo de log do squid muito grande. Não me lembro certamente, mas sei que tinham mais de 4GB somente de texto. E ele pediu para que fizéssemos um script shell para analisar aquele 'pequeno' arquivo: Fazer um ranking dos 10 IPs que mais consumiram dados em um data específica.
Por exemplo, eu chamaria o script da seguinte forma na linha de comandos e ele me retornaria a lista dos 10 IPs locais que consumiram mais banda (em ordem decrescente): ./meuscript.sh 20180606 /var/log/squid3/access.log
Cada linha do arquivo de log corresponde a um registro de acesso através do Squid. Está aqui um exemplo com 5 linhas:
1528362727.333 47 192.168.1.2 TCP_MISS/200 1757 GET http://cdn.content.prod.cms.msn.com/singletile/summary/alias/experiencebyname/today? - ORIGINAL_DST/2.21.178.106 text/xml
1528362727.333 46 192.168.1.2 TCP_MISS/200 1666 GET http://cdn.content.prod.cms.msn.com/singletile/summary/alias/experiencebyname/today? - ORIGINAL_DST/2.21.178.106 text/xml
1528362871.930 68 192.168.1.2 TCP_MISS/200 245 GET http://ncc.avast.com/ncc.txt - ORIGINAL_DST/200.143.247.16 text/html
1528363182.538 896988 192.168.1.2 TCP_MISS/200 311 GET http://su.ff.avast.com/R/A3kKIDQ0NzhlNGNhNjE4MzQyNmNiMDQ2YWI1ZTRkZThlMjhkEgQABwYYGLwHIgECKgcIBBCmpOVgKgcIAxCo14ZdMgoIABDFq-VgGIACOKKSkJABQiCTz0gZi7kd90OW8GIZphTR4939b-VE5k2JfgQW7Y5QAUiAgpgI - ORIGINAL_DST/77.234.42.239 application/octet-stream
1528363472.061 74 192.168.1.2 TCP_MISS/200 245 GET http://ncc.avast.com/ncc.txt - ORIGINAL_DST/200.143.247.16 text/html
Perceba que cada campo corresponde a uma informação, você pode ver os detalhes aqui sobre elas. Mas, as principais para o script eram: o Timestamp para pegar a data (1º campo), o IP do host local (3º campo), e a quantidade de Bytes consumida por aquela requisição (2º campo).
Na época fiz um primeiro script que utilizava 'loop dentro de loop' (complexidade quadrática), ou seja, eu varria o arquivo diversas vezes para fazer a soma dos Bytes para cada IP encontrado. Gerou um tempo de execução que ultrapassou os 10 minutos!
Foi quando utilizei o 'awk' (que é uma linguagem interpretada, muito utilizada para processamento de dados em arquivos de texto) e, além disso, utilizei Array Associativo. Enfim... eu tinha que varrer o arquivo apenas 1 vez, e concatenar os valores dos Bytes ao meu array que tinha como índice o IP do host.
Após essas modificações, o script passou a executar com menos de 2 segundos. Uma grande diferença, de 10 minutos para 2 segundos, não é mesmo?!
Vamos ao script... a parte inicial dele é toda de validação de formatos. O interessante está no final, quando é utilizado o AWK:
#!/bin/bash
#por Salim Aouar

#Validação de quantidade de parâmetros
if [ $# != 2 ]; then
 echo "erro 001 - Número incorreto de parâmetros (./script aaaammdd arquivoLog)"
 exit 1
fi

#Validação de existência de arquivo com log
if [ ! -f $2 ]; then
 echo "erro 002 - $2 não é um arquivo válido"
 exit 2
fi

#Validação de permissão de leitura no arquivo de log
if [ ! -r $2 ]; then
 echo "erro 003 - Não se tem permissão de leitura em $2"
 exit 3
fi

#Validação da data digitada pelo usuário, utilizando expressão regular
data=$(echo $1 | egrep '^([0-9]{8})')
if [ -z $data ]; then
 echo "erro 004 - Formato da data inválido (./script aaaammdd arquivoLog)"
 exit 4
fi

#Separando a data em ano, mês e dia
a=$(echo $data | cut -c1-4)
m=$(echo $data | cut -c5-6)
d=$(echo $data | cut -c7-8)

#Definindo os timestamps para o início e fim, pois serão comparados
dataInicial=$(date -d "$a-$m-$d" "+%s")
dataFinal=$(date -d "$a-$m-$d 23:59:59" "+%s")

#awk imprimindo o cabeçalho, somando os Bytes utilizando array associativo e, imprimindo o ranking
awk -F" " '
BEGIN { 
print "IP\t\tCONSUMO" 
}
{ 
if($1 >= '$dataInicial' && $1 <= '$dataFinal')
 consumos[$3] += $2
}
END { 
for (x in consumos)
 print x"\t"consumos[x] | "sort -k2 -nr | head"
}
' $2
Vou destacar aqui apenas 2 trechos deste script para fins de explicação. O primeiro deles é referente a soma dos Bytes consumidos e ligando ao IP no Array:
if($1 >= '$dataInicial' && $1 <= '$dataFinal')
 consumos[$3] += $2
}
Repare acima que estamos verificando se o Timestamp do arquivo de log (1º campo - $1) está dentro da data desejada e, caso esteja, acumulamos a soma dos Números de Bytes (2º campo - $2) no nosso vetor, cujo índice/posição é o número do IP do Host (3º campo - $3). Dessa forma, quando terminamos de percorrer todo o arquivo, temos um vetor com vários IPs de índices e seus respectivos consumos em Bytes salvos.
Aí podemos mandar exibir na tela os 10 IPs que mais consumiram dados. Fazendo um loop para percorrer nosso array 'consumos':
for (x in consumos)
 print x"\t"consumos[x] | "sort -k2 -nr | head"
}
Aí vem mais um detalhe... no trecho 'sort -k2 -nr | head' estamos pedindo para ordenar pelo segundo campo que é numérico (comando sort), e depois chamamos o 'head' para exibir somente os 10 primeiros da lista!
Enfim... neste script aí tem muita informação bacana, que você pode estudar e testar! Veja as partes de validações de data, parâmetros, etc... isto é muito importante também!
Gostaria de enfatizar que como bons profissionais, devemos sempre estudar e procurar aperfeiçoamento. Já escutei muitos falarem que vão trabalhar com redes ou servidores, pois não gostam de programação, e vice-versa. Entretanto, saibamos reconhecer que as áreas não são isoladas, e devemos sempre pensar de forma integrada!

Abraços,
Salim Aouar.

Fonte: https://www.salimaouar.com.br/artigo/shell-scripting-para-analise-de-log-do-squid

Leave a Reply