Formatos e fontes de dados 4 min de leitura

Parsing de logs: analisar logs de servidores e aplicações com exemplos

Parsing de logs de servidores e aplicações: formatos dos logs de acesso e de erros, expressões regulares, ferramentas prontas e exemplos de análise.

EW
Equipe Web-Scraping.biz
Coleta de dados para as demandas do negócio
Publicado: 22 março 2025

Logs são os registros de eventos de servidores, aplicações e serviços: cada requisição a um site, cada erro e cada ação do usuário deixam neles uma linha. O parsing de logs serve para analisar o tráfego, localizar erros, investigar incidentes de segurança e monitorar sistemas. Formalmente, logs são arquivos de texto, e as técnicas gerais para analisá-los são as mesmas; mas eles têm especificidade suficiente para merecer um artigo próprio dentro do nosso panorama do parsing de documentos.

Em que os logs diferem do texto comum

A particularidade principal é que, dentro de uma mesma fonte, o formato da linha é estritamente estável. O servidor web escreve cada linha com o mesmo template, o que permite analisar milhões de linhas com uma única expressão regular. Mas também há complicações. Os logs podem ser enormes (gigabytes por dia), então não dá para lê-los inteiros na memória, apenas em fluxo. Eles rotacionam: os arquivos antigos são renomeados e comprimidos em .gz, e o parser precisa saber ler os arquivos comprimidos. E contêm entradas multilinha: por exemplo, o rastro de uma exceção (stack trace) ocupa dezenas de linhas que pertencem a um mesmo evento.

Formatos padrão dos logs web

O mais comum é analisar os access logs do Nginx e do Apache no formato «combined». Uma linha tem este aspecto:

code
192.168.1.10 - - [01/Jun/2026:13:55:36 -0300] "GET /product/101 HTTP/1.1" 200 4523 "https://example.com/" "Mozilla/5.0 ..."

Na ordem: endereço IP, data e hora, método e caminho da requisição, código de resposta, tamanho da resposta, referer e User-Agent. Como o formato é fixo, é prático processá-lo com uma única expressão regular com grupos nomeados.

Python

A abordagem básica e mais flexível: uma expressão regular mais leitura em fluxo. Os grupos nomeados tornam o resultado legível.

python
import re
import gzip

LOG_RE = re.compile(
    r'(?P<ip>\S+) \S+ \S+ \[(?P<time>[^\]]+)\] '
    r'"(?P<method>\S+) (?P<path>\S+) [^"]*" '
    r'(?P<status>\d{3}) (?P<size>\S+) '
    r'"(?P<referer>[^"]*)" "(?P<agent>[^"]*)"'
)

def open_log(path):
    # lê de forma transparente tanto arquivos normais quanto comprimidos
    return gzip.open(path, "rt") if path.endswith(".gz") else open(path, "r")

with open_log("access.log") as f:
    for line in f:
        m = LOG_RE.match(line)
        if not m:
            continue
        row = m.groupdict()
        if row["status"] != "200":
            print(row["status"], row["method"], row["path"], row["ip"])

Quando parsear não basta e é preciso analisar (contar o top de páginas, a distribuição de códigos de resposta, o tráfego por hora), o prático é despejar as linhas já parseadas no pandas e trabalhar com elas como com uma tabela.

python
import pandas as pd

records = [m.groupdict() for line in open_log("access.log")
           if (m := LOG_RE.match(line))]
df = pd.DataFrame(records)

# top 10 de páginas mais requisitadas
print(df["path"].value_counts().head(10))

# proporção de erros 5xx
errors = df[df["status"].str.startswith("5")]
print(len(errors) / len(df))

Daqui, o resultado se exporta facilmente para CSV ou Excel para um relatório.

Linha de comando: análise rápida sem código

Para uma tarefa pontual, o mais rápido costuma ser recorrer aos utilitários Unix. O awk divide a linha em campos e calcula na hora.

bash
# top 10 de IPs por número de requisições
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head

# todas as requisições que terminaram em erro 404
awk '$9 == 404 {print $7}' access.log | sort | uniq -c | sort -rn

# volume total de tráfego servido (o campo 10 é o tamanho)
awk '{sum += $10} END {print sum/1024/1024 " MB"}' access.log

A combinação grep + awk + sort + uniq resolve a maioria das perguntas ad hoc sobre os logs sem escrever nada.

Analisadores prontos para usar

Se a tarefa é a analítica geral do tráfego web, e não extrair campos específicos, não é preciso inventar um parser. O GoAccess lê os access logs de Nginx/Apache e constrói um relatório interativo no terminal ou no navegador em tempo real. Para pipelines complexos com fontes heterogêneas, emprega-se o stack baseado no Logstash, com seus conjuntos de templates grok: na essência, uma biblioteca de expressões regulares nomeadas já prontas para os formatos de log mais populares.

Logs estruturados (logs em JSON)

As aplicações modernas escrevem cada vez mais seus logs não como linhas de texto livre, mas em JSON: um objeto por linha (formato JSON Lines). Um log assim é analisado de forma muito mais simples e confiável: não são necessárias expressões regulares frágeis, cada linha é simplesmente desserializada.

python
import json

with open("app.log") as f:
    for line in f:
        event = json.loads(line)
        if event.get("level") == "ERROR":
            print(event["timestamp"], event["message"])

Se você pode influenciar a forma como os logs são escritos, migrar para JSON Lines simplifica radicalmente a análise posterior: vale a pena prever isso no projeto desde o início.

Onde surgem as complicações

Os problemas mais frequentes são o formato de data e hora (logs de sistemas diferentes usam fusos horários e templates de data distintos que precisam ser unificados), os erros multilinha (o stack trace quebra a análise linha a linha e as entradas precisam ser «coladas» detectando o início de cada registro novo) e o volume (analisar um log diário de gigabytes exige processamento em fluxo e filtragem na hora, não carregá-lo inteiro na memória).

Se você precisa analisar com regularidade os logs de vários servidores, consolidá-los em uma analítica única ou monitorar erros e atividade suspeita, configuramos a coleta e o parsing de logs adaptados aos seus formatos, com a exportação das métricas para o sistema que for mais conveniente. As técnicas gerais de trabalho com texto arbitrário sobre as quais tudo isso se apoia estão no artigo sobre o parsing de TXT.