Truques para filtrar e transformar logs com awk em tempo real

1. Fundamentos do awk para processamento de logs em tempo real

O awk é uma ferramenta de processamento de texto que opera no modelo padrão { ação }, onde o padrão define quais linhas processar e a ação determina o que fazer com elas. Em pipelines Unix, o awk brilha ao receber dados via pipe, permitindo filtragem e transformação contínua de logs.

Variáveis internas essenciais incluem:
- NF (Number of Fields): total de campos na linha atual
- NR (Number of Record): número da linha processada
- FS (Field Separator): separador de campos (padrão: espaço/tab)
- OFS (Output Field Separator): separador na saída
- $0: linha completa, $1, $2...$n: campos individuais

A diferença principal entre awk tradicional e GNU awk (gawk) está no suporte a funções como strftime(), arrays multidimensionais e expressões regulares estendidas, recursos cruciais para logs modernos.

2. Filtrando linhas de log por padrões específicos

Expressões regulares selecionam eventos críticos com precisão:

# Selecionar apenas erros fatais
tail -f /var/log/syslog | awk '/FATAL|CRITICAL/ { print }'

# Combinar padrões com operadores lógicos
awk '/ERROR/ && /database/ { print NR": "$0 }' /var/log/app.log

# Ignorar cabeçalhos e linhas vazias
tail -f access.log | awk 'NR > 1 && NF > 0 { print $0 }'

Para logs com múltiplos níveis de severidade:

# Filtrar warnings e erros, ignorando debug
journalctl -f | awk '$3 ~ /WARNING|ERROR/ && $0 !~ /DEBUG/ { print }'

3. Extração e formatação de campos em tempo real

Delimitadores personalizados são fundamentais para logs estruturados:

# Log CSV: timestamp,level,message,user
tail -f app.csv | awk -F',' '{ print $1, $3, $4 }'

# Reordenar colunas com OFS
awk -F':' '{ OFS=" | "; print $3, $1, $2 }' /var/log/auth.log

Concatenando campos para resumos compactos:

# Compactar log de acesso: IP + URL + status
tail -f access.log | awk '{ print $1 " -> " $7 " [" $9 "]" }'

4. Transformação de timestamps e dados numéricos

Convertendo timestamps Unix para formato legível:

# Log com timestamp Unix no campo 1
tail -f metrics.log | awk '{
    $1 = strftime("%Y-%m-%d %H:%M:%S", $1)
    print
}'

Cálculos aritméticos em campos numéricos:

# Calcular latência média a cada 10 requisições
tail -f latency.log | awk '{
    soma += $2
    count++
    if (count % 10 == 0) {
        printf "Média latência: %.2f ms\n", soma/count
    }
}'

Arredondamento com printf():

# Formatar tempo de resposta com 2 casas decimais
awk '{ printf "%-15s %8.2f ms\n", $1, $3 }' response_times.log

5. Agregação e sumarização ao vivo com arrays associativos

Contagem de ocorrências por tipo:

# Contar erros por código HTTP em tempo real
tail -f access.log | awk '{
    erros[$9]++
    printf "\033[2J\033[H"  # limpa terminal
    for (cod in erros) print cod, erros[cod]
}'

Cálculo de métricas em fluxo contínuo:

# Média, máximo e mínimo de latência por IP
tail -f api.log | awk '{
    ip = $1
    lat = $5
    soma[ip] += lat
    count[ip]++
    if (lat > max[ip]) max[ip] = lat
    if (min[ip] == "" || lat < min[ip]) min[ip] = lat
    printf "%s: média=%.1f max=%.1f min=%.1f\n", ip, soma[ip]/count[ip], max[ip], min[ip]
}'

6. Uso de awk em pipelines com comandos do sistema

Monitoramento contínuo com tail -f:

# Pipeline completo: filtrar, transformar e exibir
tail -f /var/log/nginx/access.log | \
    grep -v "127.0.0.1" | \
    awk '$9 ~ /5[0-9][0-9]/ { print strftime("%T"), $1, $7, $9 }'

Redirecionamento para arquivos rotativos:

# Salvar apenas erros 5xx em arquivo separado
tail -f access.log | awk '$9 ~ /5[0-9][0-9]/ { print > "errors_5xx.log"}'

Integração com watch e tee:

# Monitorar a cada 2 segundos com resumo
watch -n 2 'tail -100 access.log | awk '\''{stats[$9]++} END {for(s in stats) print s, stats[s]}'\'

7. Scripts awk reutilizáveis para logs comuns

Filtro para logs Apache

# apache_filter.awk
# Uso: tail -f access.log | awk -f apache_filter.awk
{
    ip = $1
    data = $4
    url = $7
    status = $9
    tempo = $NF

    # Remover colchetes da data
    gsub(/\[|\]/, "", data)

    if (status ~ /5[0-9][0-9]/) {
        printf "ERRO %s | %s | %s | %s ms\n", status, ip, url, tempo
    }
}

Parsing de syslog

# syslog_parser.awk
# Uso: tail -f /var/log/syslog | awk -f syslog_parser.awk
{
    mes = $1
    dia = $2
    hora = $3
    servico = $5
    mensagem = $0

    # Extrair facilidade e prioridade (formato: facility.priority)
    if (servico ~ /.*\..*/) {
        split(servico, partes, ".")
        facilidade = partes[1]
        prioridade = partes[2]

        if (prioridade <= 3)  # emerg, alert, crit, err
            printf "CRITICO: %s %s %s\n", hora, servico, mensagem
    }
}

Extração de journalctl

# Extrair campos específicos do journald
journalctl -f -o json | awk '{
    # Para logs JSON, usar gawk com suporte JSON
    # Exemplo simplificado para logs estruturados
    if ($0 ~ /"_PID") {
        gsub(/[{},"]/, "")
        split($0, campos, " ")
        for (i in campos) {
            if (campos[i] ~ /MESSAGE=/) print campos[i]
        }
    }
}'

8. Otimização e boas práticas para logs de alto volume

Uso de next para pular linhas irrelevantes:

# Pular rapidamente linhas de debug e health checks
tail -f app.log | awk '{
    if ($0 ~ /healthcheck|heartbeat/) next
    if ($0 ~ /DEBUG/) next
    if (NF < 5) next

    # Processamento principal
    print
}'

Limitação de memória com arrays associativos:

# Limitar array a 1000 entradas, descartando as mais antigas
awk '{
    if (length(ips) >= 1000) {
        delete ips[oldest_ip]
    }
    ips[$1]++
    if (ips[$1] > max_count) {
        max_count = ips[$1]
        max_ip = $1
    }
} END { print max_ip, max_count }' access.log

Evitando expansão desnecessária:

# Ineficiente: expande $0 para cada campo
awk '{ for (i=1; i<=NF; i++) if ($i ~ /ERROR/) print }'

# Eficiente: testa $0 uma vez
awk '/ERROR/ { for (i=1; i<=NF; i++) print $i }'

Para logs de alto volume, prefira:
- Usar -F em vez de definir FS dentro do script
- Evitar gsub() e split() em cada linha
- Utilizar printf() em vez de concatenação com print
- Processar em lotes com getline para reduzir chamadas de sistema

Referências