Logs são uma das fontes mais honestas de informação operacional, mas também uma das mais difíceis de tratar quando o volume cresce. Um arquivo de alguns megabytes pode ser aberto em qualquer editor. Um diretório com dezenas de gigabytes, logs rotacionados, linhas parcialmente corrompidas, timestamps inconsistentes e mensagens repetidas já exige outra postura. É nesse ponto que Zig começa a fazer sentido como linguagem para parsers de logs.
O objetivo não é substituir plataformas de observabilidade, bancos colunares ou pipelines profissionais. O objetivo é resolver o intervalo que aparece em quase todo time: você precisa analisar um incidente, conferir um deploy, gerar um relatório curto, transformar logs em JSON ou extrair padrões antes de decidir se vale criar uma solução maior. Shell e Python funcionam muito bem no começo, mas podem ficar lentos, frágeis ou difíceis de distribuir para outras pessoas do time.
Zig combina binário único, leitura eficiente de arquivos, controle explícito de alocação e bom suporte a CLIs. Um parser bem escrito pode ler entrada de stdin, processar arquivos grandes linha por linha, aplicar filtros, agrupar erros e produzir saída em texto, Markdown ou JSON sem carregar o arquivo inteiro na memória. Este artigo complementa os guias de ferramentas internas em Zig, processamento de dados e serialização, file I/O em Zig e benchmarking de performance.
Quando Zig vale para logs
Use Zig quando o parser precisa de pelo menos uma destas características:
- ler arquivos grandes sem estourar memória;
- rodar rápido em CI, container, notebook de plantão ou servidor antigo;
- ser distribuído como binário único para Linux, macOS e Windows;
- ter saída estável para automação;
- controlar erros de parsing sem esconder linhas problemáticas;
- integrar com ferramentas como
journalctl,kubectl logs,grep,awk,jqou APIs internas; - virar uma ferramenta operacional que será usada mais de uma vez.
Se a tarefa é única e simples, um comando de shell ainda é melhor. Se você precisa consultar meses de logs com agregações complexas, use uma ferramenta própria para isso. Zig entra bem no meio: parsers pequenos, rápidos, auditáveis e fáceis de repetir.
Desenho de uma CLI de análise
Uma CLI prática para logs pode começar com três modos:
logscan erros app.log
logscan janela --desde 2026-05-29T10:00:00Z --ate 2026-05-29T10:30:00Z app.log
logscan resumo --formato markdown logs/*.log
O comando erros filtra linhas com severidade alta. O comando janela restringe por tempo. O comando resumo agrupa mensagens repetidas, conta ocorrências e imprime um relatório curto para colar em um postmortem ou ticket.
A estrutura do projeto pode ser pequena:
logscan/
build.zig
src/
main.zig
cli.zig
scanner.zig
parser.zig
report.zig
O ponto importante é separar leitura, parsing e relatório. A leitura entrega linhas. O parser transforma cada linha em um evento parcial. O relatório decide o que somar, agrupar ou imprimir. Essa divisão evita que a regra de negócio fique presa em stdout e facilita testes.
Leia em streaming, não em bloco gigante
O erro mais comum em parsers caseiros é carregar o arquivo inteiro para depois dividir em linhas. Isso parece simples, mas falha justamente quando a ferramenta se torna necessária: logs enormes, arquivos rotacionados ou execução dentro de um container com pouca memória.
Prefira leitura incremental. Em Zig, você pode usar um buffer fixo e processar linha por linha. O detalhe operacional é aceitar que linhas podem ser maiores que o buffer escolhido. Para logs controlados, um limite documentado pode ser aceitável. Para logs de terceiros, trate linha longa como erro recuperável: registre que ela foi truncada ou rejeitada, mas continue processando o restante.
Uma política razoável:
- limite padrão de linha, por exemplo 64 KiB;
- flag
--max-line-bytespara ambientes especiais; - contador de linhas inválidas;
- saída final indicando quantas linhas foram ignoradas;
- exit code diferente quando houve erro crítico de leitura.
Essa transparência é mais importante do que tentar ser mágico. Em incidente, uma ferramenta que diz “li 2.391.044 linhas e ignorei 17 inválidas” é mais confiável do que uma que simplesmente imprime um resumo bonito.
Modele eventos mínimos
Nem todo log precisa virar uma estrutura completa. Comece com um evento mínimo:
const Level = enum { debug, info, warn, error, unknown };
const LogEvent = struct {
timestamp: ?i64,
level: Level,
service: ?[]const u8,
message: []const u8,
};
O timestamp pode ser opcional porque nem todo arquivo terá formato perfeito. O service pode ser extraído de um campo JSON, prefixo textual ou argumento de linha de comando. A mensagem deve apontar para dados válidos apenas enquanto a linha atual existir; se você precisa guardar a mensagem para agrupamento posterior, duplique a fatia com o allocator certo.
Essa decisão evita alocações desnecessárias. Muitos parsers só precisam olhar a linha, atualizar contadores e descartá-la. Guardar todas as mensagens é caro e raramente necessário.
Agrupe sem perder contexto
Relatório de logs não deve ser apenas uma contagem bruta. Se você agrupa demais, perde causa. Se agrupa de menos, entrega ruído. Uma abordagem prática é normalizar partes variáveis da mensagem:
- IDs UUID viram
{uuid}; - números longos viram
{num}; - emails viram
{email}; - caminhos temporários viram
{path}; - hashes longos viram
{hash}.
Assim, mensagens como pedido 123 falhou e pedido 456 falhou entram no mesmo grupo. Mas cuidado para não remover informação essencial. Em erro de banco, por exemplo, o nome da tabela pode ser mais importante que o código numérico.
Uma saída útil para plantão inclui:
- assinatura normalizada da mensagem;
- quantidade de ocorrências;
- primeiro e último timestamp;
- três exemplos reais;
- serviço ou host mais frequente;
- indicação de linhas inválidas ou truncadas.
Esse formato já ajuda a decidir se o problema é regressão de deploy, pico de tráfego, dependência externa ou dado específico.
JSON logs e texto livre
Muitos sistemas modernos emitem logs JSON, mas a realidade costuma ser híbrida. Um serviço escreve JSON estruturado, outro imprime texto livre, uma biblioteca manda stack trace multilinha e um sidecar adiciona prefixo próprio. O parser deve ser honesto sobre o que entende.
Para JSON, extraia poucos campos estáveis: time, level, msg, service, trace_id, span_id, status e error. Para texto livre, use regras conservadoras: timestamp no início da linha, severidade por palavra-chave e mensagem restante. Não tente inferir demais no primeiro release.
Se o time já usa OpenTelemetry, conecte o relatório com o vocabulário existente. O artigo de OpenTelemetry em Zig mostra como traces, métricas e logs se relacionam. Mesmo que sua CLI não envie nada para um collector, ela pode preservar trace_id e span_id para a pessoa cruzar dados em outra ferramenta.
Erros recuperáveis e exit codes
Parser de logs precisa diferenciar falha operacional de dado ruim. Não é a mesma coisa não conseguir abrir o arquivo e encontrar uma linha malformada no meio do arquivo.
Uma política simples de exit codes:
0: leitura concluída e nenhum problema relevante;1: encontrou erro crítico definido pelo filtro, por exemplo mensagensERRORacima do limite;2: uso incorreto da CLI;3: arquivo inacessível, permissão negada ou entrada externa indisponível;4: parsing teve perdas acima do limite configurado.
Essa distinção permite usar a ferramenta em CI. Um smoke test pode falhar se houver erros no log depois de um deploy, mas pode fazer retry se a fonte externa estava indisponível. Para tratamento idiomático, veja também boas práticas de error handling em Zig.
Performance: meça antes de complicar
Zig facilita otimizações, mas parser de logs costuma ganhar mais com decisões simples: não carregar tudo em memória, evitar cópias, reduzir alocações por linha e manter saída enxuta. Antes de criar uma tabela hash sofisticada ou um parser SIMD, meça.
Use um conjunto de arquivos representativo: log pequeno para testes rápidos, log médio para desenvolvimento e log grande para benchmark. Registre throughput em MiB/s, pico de memória e tempo total. Se o gargalo é disco ou descompressão, micro-otimizar parsing não vai mudar muito.
Também pense no modo de distribuição. Para uma ferramenta interna, ReleaseSafe costuma ser um bom padrão: rápida o suficiente e ainda com verificações úteis. Se você publicar binários para o time, siga as recomendações de release multiplataforma com GitHub Actions e gere checksums.
Integração com o ecossistema do time
Um parser Zig não precisa viver isolado. Ele pode aceitar stdin para funcionar em pipes:
journalctl -u minha-api --since "30 min ago" | logscan resumo --formato markdown
Pode também produzir JSON para jq, Markdown para incidentes e texto compacto para terminal. Essa flexibilidade aumenta adoção porque a ferramenta se encaixa nos hábitos existentes.
Se o time já usa Go para serviços e Zig para utilitários de baixo nível, a integração é natural. O material de Go no Brasil é uma boa referência para cultura de CLIs operacionais, enquanto Zig pode assumir os trechos em que binário único, controle de memória e cross-compilation pesam mais.
Checklist para seu primeiro logscan
Antes de chamar a ferramenta de pronta, confira:
- lê arquivo e
stdin; - não carrega o arquivo inteiro em memória;
- tem limite explícito de tamanho de linha;
- conta linhas lidas, inválidas e truncadas;
- diferencia erro de uso, erro de leitura e erro encontrado no log;
- suporta saída humana e saída de máquina;
- tem testes com linhas válidas, inválidas e grandes;
- documenta formatos de timestamp aceitos;
- não imprime segredos presentes em variáveis de ambiente;
- inclui exemplos de uso em incidentes reais ou simulados.
Comece pequeno: um comando que lê linha por linha, filtra ERROR e conta ocorrências já pode economizar tempo no próximo deploy. Depois adicione janela de tempo, agrupamento, JSON e relatórios. Zig não precisa transformar análise de logs em arquitetura pesada. A força está em criar uma ferramenta previsível, rápida e honesta sobre seus limites.