Linux: Fase 3 Mini Projeto — Análise de Log do Servidor
O projeto de conclusão da Fase 3. Você vai combinar redirecionamento, pipes, grep, awk, sed, sort, uniq e todas as suas habilidades de processamento de dados para fazer uma análise completa dos logs de acesso do Nginx e gerar um relatório.
📋 Pré-requisitos: Você já deve conhecer
- Aula 12: Streams Padrão e Redirecionamento
- Aula 13: Pipes e Filtros
- Aula 14: Busca em Texto
- Aula 15: Os Três Mosqueteiros do Processamento de Texto
1. O Que Você Vai Aprender
- Análise do formato de log do Nginx
- Análise de volume de requisições por período
- Ranking das URLs mais populares
- Estatísticas da distribuição de códigos de status
- Análise de desempenho por tempo de resposta
2. O Site do Xiaoming Ficou Lento
(1) O Problema: O Chefe Pede Dados, Mas o Log de 10GB é Impossível de Navegar
O site do Xiaoming estava lento nos últimos tempos, e o chefe exigiu "me traga dados". O access.log do Nginx gera centenas de MB por dia — abrir é impossível, e procurar palavras-chave é como achar agulha no palheiro.
(2) Cadeia de Pipeline Para Análise
Xiaoming usou as habilidades da Fase 3, extraindo métricas-chave linha por linha com comandos de pipe:
# Top 10 IPs por número de acessos
cat access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
# Top 10 URLs mais requisitadas
cat access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -10
> 💡 Dica: O padrão de pipeline para análise de logs é fixo: `awk extrai campos → sort → uniq -c deduplica e conta → sort -rn ordena por contagem em ordem decrescente → head pega o Top N`. Decore esse modelo — qualquer análise Top N de logs pode usá-lo.
# Requisições por hora
cat access.log | awk -F: '{print $2":00"}' | sort | uniq -c | sort -rn
Saída:
TEXT210 192.168.1.100 198 10.0.0.45 195 192.168.1.200 190 10.0.0.88 180 172.16.0.50 150 /index.html 120 /api/users 110 /api/login 100 /about 90 /contact 85 10:00 80 14:00 75 09:00 70 22:00 65 18:00
(3) O Benefício: Relatório em 5 Minutos
Xiaoming juntou os resultados da análise em um relatório — tendências de acesso, top 10 requisições lentas, páginas com mais erros 404. O chefe disse: "Bom, vamos adicionar mais servidores."
3. Pontos-Chave
(1) Formato Padrão de Log do Nginx (combined)
192.168.1.100 - - [07/Jul/2026:10:15:30 +0000] "GET /index.html HTTP/1.1" 200 1234 "-" "Mozilla/5.0"
│ │ │ │ │ │ │ │
│ │ │ └─ User-Agent │ │ │ └─ Bytes da resposta
│ │ └───────────────────────────── Referer │ └────── Código de status
│ └─────────────────────────────────────────────────────────── Linha da requisição (método/URL/protocolo)
└─── IP do cliente Timestamp
ℹ️ Nota: No formato de log combined do Nginx, os campos são separados por espaço, mas alguns campos contêm espaços (por exemplo, a linha da requisição e o timestamp vêm entre colchetes e aspas). Quando o awk divide por espaço,
$1é o IP,$7é a URL,$9é o código de status,$10são os bytes — essas posições são estáveis e podem ser extraídas diretamente pelo awk.
Posições dos campos (separados por espaço):
| Campo | Significado |
|---|---|
$1 |
IP do cliente |
$4 |
Início do timestamp [ |
$7 |
URL requisitada |
$9 |
Código de status HTTP |
$10 |
Bytes da resposta |
(2) ▶ Exemplo: Gerar Logs de Amostra
Primeiro, gere um arquivo de log de amostra para análise:
#!/bin/bash
# generate-sample-log.sh
IPS=("192.168.1.100" "10.0.0.45" "192.168.1.200" "10.0.0.88" "172.16.0.50")
URLS=("/index.html" "/api/users" "/api/login" "/about" "/contact" "/images/logo.png" "/api/orders" "/products" "/blog")
STATUSES=(200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200 201 301 302 400 401 403 404 500 502 503)
rm -f /tmp/access.log
for i in {1..1000}; do
IP=${IPS[$RANDOM % ${#IPS[@]}]}
URL=${URLS[$RANDOM % ${#URLS[@]}]}
STATUS=${STATUSES[$RANDOM % ${#STATUSES[@]}]}
SIZE=$((RANDOM % 10000 + 100))
HOUR=$((RANDOM % 24))
MIN=$((RANDOM % 60))
SEC=$((RANDOM % 60))
echo "$IP - - [07/Jul/2026:$HOUR:$MIN:$SEC +0000] \"GET $URL HTTP/1.1\" $STATUS $SIZE \"-\" \"Mozilla/5.0\"" >> /tmp/access.log
done
echo "Generated 1000 sample log entries to /tmp/access.log"
chmod +x generate-sample-log.sh
./generate-sample-log.sh
Saída:
TEXTGenerated 1000 sample log entries to /tmp/access.log
(3) ▶ Exemplo: Estatísticas de Volume de Requisições
echo "=== Total Requests ==="
wc -l /tmp/access.log
echo ""
echo "=== Requests per IP ==="
cat /tmp/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
echo ""
echo "=== Request Method Distribution ==="
cat /tmp/access.log | awk '{print $6}' | tr -d '"' | sort | uniq -c | sort -rn
Saída:
TEXT=== Total Requests === 1000 /tmp/access.log === Requests per IP === 210 192.168.1.100 198 10.0.0.45 195 192.168.1.200 190 10.0.0.88 180 172.16.0.50 === Request Method Distribution === 1000 GET
(4) ▶ Exemplo: Análise de Códigos de Status
echo "=== Status Code Distribution ==="
cat /tmp/access.log | awk '{print $9}' | sort | uniq -c | sort -rn
echo ""
echo "=== 4xx Errors (Client Errors) ==="
cat /tmp/access.log | awk '$9 ~ /^4/ {print $9, $7, $1}' | sort | uniq -c | sort -rn | head -10
echo ""
echo "=== 5xx Errors (Server Errors) ==="
cat /tmp/access.log | awk '$9 ~ /^5/ {print $9, $7, $1}' | sort | uniq -c | sort -rn | head -10
echo ""
echo "=== Error Rate ==="
TOTAL=$(wc -l < /tmp/access.log)
ERRORS=$(awk '$9 ~ /^[45]/ {count++} END {print count}' /tmp/access.log)
echo "Total requests: $TOTAL"
echo "Errors: $ERRORS"
echo "Error rate: $(echo "scale=2; $ERRORS * 100 / $TOTAL" | bc)%"
Saída:
TEXT=== Status Code Distribution === 960 200 5 201 3 301 2 302 8 400 2 401 3 403 10 404 4 500 2 502 1 503 === 4xx Errors (Client Errors) === 3 404 /api/login 192.168.1.100 2 404 /about 10.0.0.45 2 403 /api/admin 192.168.1.200 === 5xx Errors (Server Errors) === 2 500 /api/data 10.0.0.88 2 502 /api/users 172.16.0.50 1 503 /api/health 192.168.1.100 === Error Rate === Total requests: 1000 Errors: 32 Error rate: 3.20%
(5) ▶ Exemplo: Análise de URLs Mais Acessadas
echo "=== Most Popular URLs ==="
cat /tmp/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -10
echo ""
echo "=== URLs with Most 404s ==="
cat /tmp/access.log | awk '$9 == 404 {print $7}' | sort | uniq -c | sort -rn | head -10
echo ""
echo "=== URLs with Largest Response Size ==="
cat /tmp/access.log | awk '{print $10, $7}' | sort -rn | head -10
Saída:
TEXT=== Most Popular URLs === 150 /index.html 120 /api/users 110 /api/login 100 /about 90 /contact === URLs with Most 404s === 5 /api/login 3 /old-page 2 /favicon.ico === URLs with Largest Response Size === 10098 /images/logo.png 9876 /api/users 8765 /api/orders 7654 /products 6543 /blog
(6) ▶ Exemplo: Análise por Dimensão de Tempo
echo "=== Requests per Hour ==="
cat /tmp/access.log | awk -F: '{print $2":00"}' | sort | uniq -c | sort -rn
echo ""
echo "=== Peak Traffic Hours ==="
cat /tmp/access.log | awk -F: '{print $2}' | sort | uniq -c | sort -rn | head -5
echo ""
echo "=== Requests per Minute (Top 10) ==="
cat /tmp/access.log | awk -F: '{print $2":"$3}' | sort | uniq -c | sort -rn | head -10
Saída:
TEXT=== Requests per Hour === 85 10:00 80 14:00 75 09:00 70 22:00 65 18:00 === Peak Traffic Hours === 85 10 80 14 75 09 70 22 65 18 === Requests per Minute (Top 10) === 5 10:15 4 14:30 4 09:45 3 22:10 3 18:05
(7) ▶ Exemplo: Análise de Desempenho
echo "=== Response Size Analysis (using $10 as simulated response size) ==="
echo "Largest response: $(awk '{print $10}' /tmp/access.log | sort -rn | head -1) bytes"
echo "Average response: $(awk '{sum += $10; count++} END {printf "%.0f", sum/count}' /tmp/access.log) bytes"
echo ""
echo "=== Large Responses (> 5KB) ==="
cat /tmp/access.log | awk '$10 > 5000 {print $10, $7}' | sort -rn | head -10
Saída:
TEXT=== Response Size Analysis (using $10 as simulated response size) === Largest response: 10098 bytes Average response: 5142 bytes === Large Responses (> 5KB) === 10098 /images/logo.png 9876 /api/users 8765 /api/orders 7654 /products 6543 /blog 5678 /api/data
(8) ▶ Exemplo Abrangente: Script Completo de Relatório de Análise de Log
#!/bin/bash
# log-report.sh - Gera relatório de análise de log do Nginx
LOG_FILE="${1:-/tmp/access.log}"
if [ ! -f "$LOG_FILE" ]; then
echo "Error: Log file $LOG_FILE does not exist"
exit 1
fi
TOTAL=$(wc -l < "$LOG_FILE")
echo "========================================="
echo " Nginx Access Log Analysis Report"
echo " File: $LOG_FILE"
echo " Total requests: $TOTAL"
echo " Generated at: $(date)"
echo "========================================="
echo ""
# 1. Visão geral dos códigos de status
echo "1. HTTP Status Code Distribution"
echo "--------------------"
cat "$LOG_FILE" | awk '{print $9}' | sort | uniq -c | sort -rn | \
awk '{printf " %-5s %s\n", $2, $1}'
echo ""
# 2. TOP 10 IPs
echo "2. Top 10 IPs by Access Count"
echo "---------------------"
cat "$LOG_FILE" | awk '{print $1}' | sort | uniq -c | sort -rn | head -10 | \
awk '{printf " %-20s %s\n", $2, $1}'
echo ""
# 3. TOP 10 URLs
echo "3. Top 10 URLs by Request Count"
echo "-----------------------"
cat "$LOG_FILE" | awk '{print $7}' | sort | uniq -c | sort -rn | head -10 | \
awk '{printf " %-30s %s\n", $1, $2}'
echo ""
# 4. Erros 4xx/5xx
echo "4. Error Requests"
echo "-------------"
FOURXX=$(awk '$9 ~ /^4/ {count++} END {print count}' "$LOG_FILE")
FIVEXX=$(awk '$9 ~ /^5/ {count++} END {print count}' "$LOG_FILE")
echo " 4xx errors: $FOURXX ($(echo "scale=1; $FOURXX*100/$TOTAL" | bc)%)"
echo " 5xx errors: $FIVEXX ($(echo "scale=1; $FIVEXX*100/$TOTAL" | bc)%)"
echo ""
# 5. Requisições por hora
echo "5. Requests per Hour"
echo "--------------------"
cat "$LOG_FILE" | awk -F: '{print $2":00"}' | sort | uniq -c | sort -rn | head -10 | \
awk '{printf " %s: %s\n", $2, $1}'
echo ""
# 6. Maiores respostas
echo "6. Largest Responses (Top 5)"
echo "----------------------"
cat "$LOG_FILE" | awk '{print $10, $7}' | sort -rn | head -5 | \
awk '{printf " %s bytes: %s\n", $1, $2}'
echo ""
echo "========================================="
echo "End of Report"
# Executa o script de relatório
chmod +x log-report.sh
./log-report.sh /tmp/access.log
❓ Perguntas Frequentes
P: O que significam os campos no formato de log do Nginx? R: Formato combined padrão: IP - usuário [horário] "método URL protocolo" código_status bytes "Referer" "User-Agent". Você pode personalizar pela diretiva
log_formatdo Nginx.
P: Pipelines travam com arquivos de log enormes (na casa de GB)? R: Não muito. O streaming de pipeline significa que os dados são processados à medida que são lidos. Porém, o
sortbloqueia porque precisa ler todos os dados antes. Para arquivos muito grandes, usesort -S 1Gpara limitar a memória, ou use arrays do awk para as estatísticas.
P: Como contar PV (page views) e UV (unique visitors) reais? R: PV =
wc -l(excluindo requisições que não são páginas, como imagens/CSS/API). UV =awk '{print $1}' access.log | sort -u | wc -l.
P: Como monitorar mudanças no log continuamente? R:
tail -facompanha novas linhas em tempo real. Combine com grep:tail -f access.log | grep " 500 "— mostra todos os erros 500 em tempo real.
P: Como calcular o tempo de resposta P99 de endpoints de API? R: O Nginx precisa ser configurado com
$upstream_response_timeou$request_time. Depois:awk '{print $NF}' access.log | sort -n | awk '{all[NR]=$1} END{p99=int(NR*0.99); print all[p99]}'.
📖 Resumo
- Formato padrão de log do Nginx: IP/horário/URL/código de status/tamanho
awk '{print $1}'extrai o IP,$7extrai a URL,$9extrai o código de statussort | uniq -c | sort -rn | headencontra o Top Nawk -F: '{print $2":00"}'agrega por hora- Um script completo de análise de log leva apenas 30-50 linhas de bash
📝 Exercícios
- Básico (⭐): Rode o script de geração de logs de amostra para criar 1000 entradas de teste, depois escreva uma cadeia de pipeline para encontrar os top 5 IPs por número de acessos e suas quantidades de requisições
- Intermediário (⭐⭐): Conte o número e o percentual dos códigos de status 404 e 500, depois use
awk '{print $7}'para extrair as URLs e encontrar as 5 páginas mais populares - Avançado (⭐⭐⭐): Crie um script de análise de log que gere um relatório formatado em Markdown incluindo: total de requisições, Top 10 IPs, distribuição de códigos de status, tendências de requisições por hora, Top 5 URLs acessadas