Administração Linux

Logs e troubleshooting no Linux: journalctl, rotação, OOM, carga e strace

Avançado12 min de leituraAtualizado

Esta página foi traduzida do inglês com a ajuda de IA e conferida com o original. Ler a versão em inglês

Sob pressão, a maioria das pessoas investiga no chute e reinicia. Este guia troca esse hábito por um método: descobrir o que mudou, ler o log certo e medir um recurso de cada vez. Ele cobre o journald e os arquivos em /var/log, rotação e retenção de logs, e as falhas recorrentes — disco cheio, OOM killer, carga alta com CPUs ociosas, processos travados e DNS — com o comando que identifica cada uma.

O que você vai aprender

  • Aplicar uma sequência de triagem repetível em vez de reiniciar serviços e torcer
  • Consultar o journal por unidade, prioridade, boot e intervalo de tempo, e saber o que ainda grava em /var/log
  • Configurar a rotação de logs e a retenção do journal para que o histórico nunca encha o disco
  • Diferenciar saturação de CPU, espera de I/O, pressão de memória e OOM kills
  • Inspecionar um processo em execução ou travado com lsof, strace, /proc e dmesg

Fazer troubleshooting no Linux é uma habilidade com estrutura. Quem é rápido nisso não decorou mais comandos que os outros: segue uma sequência, descarta uma camada de cada vez e lê o log antes de formular uma teoria. Quem é lento reinicia o serviço, vê o sintoma voltar, reinicia de novo e só vai olhar os logs depois de uma hora.

Este guia é sobre essa sequência. Ele mostra para onde a saída de log realmente vai em um host com systemd, como consultá-la e como impedir que ela encha o disco, e depois passa pelas falhas que se repetem em todo servidor: falta de espaço, falta de memória, carga sem CPU, um processo que parou de responder e uma dependência que sumiu. Parte do princípio de que você já conhece processos e systemd e a ordem de diagnóstico de fundamentos de rede.

Triagem antes das ferramentas

Antes de digitar qualquer coisa, responda a três perguntas. Qual é o sintoma real — um erro específico, um timeout, um resultado errado — e não "o site caiu"? Quando começou, e isso coincide com um deploy, uma mudança de configuração, a expiração de um certificado, um job agendado ou um aumento de tráfego? Qual é o raio de impacto — um endpoint, um host, um cliente, tudo?

Depois, percorra as camadas em uma ordem fixa e pare na primeira que estiver errada:

  1. O serviço está rodando e reiniciou recentemente? systemctl status, systemctl show -p NRestarts.
  2. O que o log dele diz perto do horário de início? journalctl -u … --since.
  3. Algum recurso se esgotou? Disco, inodes, memória, descritores de arquivo, conexões.
  4. As dependências estão saudáveis? Banco de dados, cache, API upstream, DNS.
  5. Alguma coisa mudou? journalctl -u … --since, histórico de pacotes, configuração no controle de versão.

Anote o que você verifica e o que encontra conforme avança. Dez minutos depois do início de um incidente, ninguém lembra se o disco já foi verificado, e uma nota compartilhada é a diferença entre duas pessoas investigando e duas pessoas refazendo o trabalho uma da outra.

Para onde vai a saída

Em um host com systemd existem dois mundos de logs, e você precisa dos dois.

O journal coleta tudo o que uma unidade escreve na saída padrão e na saída de erro, além das mensagens do kernel e de tudo o que chega pela API do syslog. É um armazenamento binário estruturado: cada entrada carrega campos como _SYSTEMD_UNIT, _PID, _UID, PRIORITY, _BOOT_ID e MESSAGE, e é isso que permite filtrar com precisão. Ele fica em /run/log/journal (volátil, perdido no reboot) ou em /var/log/journal (persistente), dependendo de Storage= em /etc/systemd/journald.conf. Se journalctl -b -1 não retornar nenhuma entrada, o armazenamento persistente está desligado; crie /var/log/journal e reinicie o systemd-journald para ativá-lo.

Os arquivos de texto em /var/log são gravados por programas que cuidam do próprio logging: /var/log/apache2/, /var/log/nginx/, /var/log/mysql/, /var/log/audit/audit.log. Distribuições que ainda rodam o rsyslog também recebem uma cópia do journal, gerando /var/log/syslog e /var/log/messages, além de /var/log/auth.log ou /var/log/secure para eventos de autenticação.

Saber o que fica onde economiza tempo. Uma página PHP com erro aparece no log de erros do pool do FPM e no log de erros do virtual host do Apache, não no journal; um serviço que não sobe aparece no journal, não em algum arquivo controlado pela aplicação.

Consultando o journal

journalctl -u nginx.service -n 100 --no-pager
journalctl -u orders-api -f                       # follow
journalctl -u orders-api --since "2026-09-07 14:00" --until "2026-09-07 14:30"
journalctl -p err -b                              # this boot, error and worse
journalctl -b -1 -p warning                       # previous boot
journalctl -k --since "30 min ago"                # kernel ring buffer, time-filtered
journalctl _UID=33 --since today                  # everything by one user id
journalctl _PID=1842
journalctl -u orders-api -o json-pretty | head -40
journalctl --list-boots

As prioridades vão de emerg (0) a debug (7), e -p err significa "err e mais grave". As expressões de tempo aceitam "10 min ago", yesterday, today e timestamps absolutos. -o cat reduz a saída às mensagens puras, sem metadados, que é o que você quer antes de mandá-las para os pipelines de grep, sed e awk:

journalctl -u orders-api --since today -o cat \
  | grep -oE 'status=[0-9]{3}' | sort | uniq -c | sort -rn

Duas opções valem a pena em incidentes. journalctl -u a -u b --since … junta várias unidades em um único fluxo ordenado no tempo, e é assim que você vê, na mesma tela, uma requisição chegar ao proxy e falhar no backend. E journalctl -f -u a -u b -p warning dá um feed filtrado ao vivo durante um deploy.

Usuários que não são root só veem as próprias entradas, a menos que estejam no grupo systemd-journal — vale providenciar isso para quem precisa depurar sem sudo completo.

Rotação e retenção

Logs sem limite enchem discos, e um disco cheio derruba de uma vez todos os serviços da máquina. Os dois mundos de logs precisam de um teto.

Para o journal, defina o teto em /etc/systemd/journald.conf:

[Journal]
Storage=persistent
SystemMaxUse=2G
SystemKeepFree=1G
MaxRetentionSec=30day
Compress=yes
sudo systemctl restart systemd-journald
journalctl --disk-usage
sudo journalctl --vacuum-size=1G       # reclaim now
sudo journalctl --vacuum-time=14d

Para arquivos, o logrotate roda a partir de um timer do systemd e processa as configurações colocadas em /etc/logrotate.d/:

/var/log/myapp/*.log {
    daily
    rotate 14
    missingok
    notifempty
    compress
    delaycompress
    create 0640 myapp adm
    sharedscripts
    postrotate
        systemctl reload myapp.service > /dev/null 2>&1 || true
    endscript
}

O mecanismo importa. O logrotate renomeia o arquivo atual e cria um novo, mas um processo que já estava com o arquivo antigo aberto continua escrevendo nele — o arquivo renomeado cresce enquanto o novo fica vazio. Há duas soluções: o postrotate sinaliza o serviço para reabrir os arquivos (é o que o reload acima faz), ou você usa copytruncate, que copia o conteúdo e trunca o original no lugar. O copytruncate perde as linhas escritas durante a cópia, então prefira o sinal quando a aplicação suportar isso.

Teste uma configuração antes de confiar nela:

sudo logrotate -d /etc/logrotate.d/myapp     # debug: say what would happen
sudo logrotate -f /etc/logrotate.d/myapp     # force a rotation now
cat /var/lib/logrotate/status | grep myapp

Os primeiros sessenta segundos em uma máquina doente

Uma varredura rápida que quase não custa nada e muitas vezes encerra a investigação:

uptime                                   # load average: 1, 5, 15 minutes
free -h                                  # available memory, swap in use
df -h; df -i                             # space and inodes
systemctl --failed                       # units that gave up
journalctl -p err -b --since "1 hour ago" | tail -50
dmesg -T --level=err,warn | tail -30     # kernel complaints, human timestamps
sudo ss -s                               # socket counts by state
vmstat 1 5                               # r, b, si/so, us/sy/id/wa over five seconds

As colunas do vmstat já são um diagnóstico: um r alto indica processos na fila esperando CPU; um b alto, processos bloqueados em I/O; si/so diferentes de zero, swap ativo; e um wa alto no grupo de CPU, processadores ociosos porque estão esperando o armazenamento.

Disco e inodes esgotados

df -h mostra bytes e df -i mostra inodes; um sistema de arquivos pode encher em qualquer uma das duas dimensões, e uma partição com 100 % dos inodes ocupados e 40 % de espaço livre confunde todo mundo, sempre. A causa comum são milhões de arquivos pequenos de sessão, de cache ou da fila de e-mail.

Quando o problema são os bytes, procure o que cresceu, não o que é grande — a maior parte do disco de um sistema está ocupada legitimamente, e o que você quer saber é o que mudou:

sudo du -x --max-depth=2 /var 2>/dev/null | sort -n | tail -15
sudo find /var/log -type f -size +200M -printf '%s\t%p\n' | sort -rn | head
sudo find / -xdev -type f -mtime -1 -size +100M -printf '%TF %s %p\n' 2>/dev/null | sort -rn | head

Recuperar espaço com segurança significa truncar, e não apagar, qualquer arquivo que um serviço mantenha aberto — sudo truncate -s 0 /var/log/myapp/huge.log devolve os blocos na hora e deixa o descritor válido. Depois, elimine a causa: um nível de log debug esquecido após um deploy, uma regra de rotação que nunca foi escrita, um padrão de core dump gravando gigabytes no diretório de trabalho ou um journal sem SystemMaxUse.

Mantenha de propósito alguns por cento de folga. Bancos de dados, gerenciadores de pacotes e o journal se comportam mal com exatamente zero byte livre, e recuperar uma máquina sem espaço nem para gravar um arquivo temporário é bem mais difícil do que parece.

Pressão de memória e o OOM killer

No free -h, só uma coluna importa: available. Ela estima quanta memória um novo processo conseguiria obter, contando o page cache recuperável. O Linux usa de propósito toda a memória sobrando como cache, então um valor de "free" baixo com um "available" confortável é um sistema funcionando corretamente, não um problema.

Quando a memória disponível realmente acaba e o swap não dá conta, o kernel aciona o OOM killer, que escolhe uma vítima por uma pontuação de "badness" que penaliza processos grandes, iniciados recentemente e sem privilégios, e a mata com um sinal que não pode ser capturado. A evidência está no log do kernel:

$ sudo dmesg -T | grep -i -A2 'out of memory'
[Sun Sep  7 03:14:22 2026] Out of memory: Killed process 4821 (java) total-vm:6291456kB,
  anon-rss:3874112kB, file-rss:0kB, shmem-rss:0kB, UID:1001 pgtables:8192kB oom_score_adj:0
journalctl -k --since today | grep -i 'killed process'
systemctl status orders-api | grep -E 'Active|Main PID|status'
cat /sys/fs/cgroup/system.slice/orders-api.service/memory.peak

A assinatura é inconfundível: um serviço que some sem nenhuma mensagem de desligamento no próprio log e uma linha do kernel que cita o nome dele. O systemctl status mostra a unidade no estado failed, com o sinal KILL.

As correções estão em ordem de eficácia. Limite o culpado na própria unidade — MemoryMax=2G e MemoryHigh=1600M no arquivo do serviço — para que o kernel faça throttling e mate dentro daquele cgroup em vez de escolher uma vítima no sistema inteiro. Corrija os limites da própria aplicação, como um heap da JVM maior que a máquina ou um pool do FPM com workers demais. Adicione swap, que transforma um kill seco em degradação lenta e dá tempo para reagir. Ajuste o oom_score_adj de um processo que precisa sobreviver, como o banco de dados em um host de banco de dados. Colocar mais RAM sem entender qual desses casos se aplica normalmente só adia a falha.

Carga, CPU e I/O

O load average conta os processos prontos para rodar e os que estão em sono não interrompível. É por causa desse segundo grupo que, no Linux, carga não é uma métrica de CPU.

uptime
nproc                                    # compare load against this
top -b -n1 | head -15
pidstat -u -p ALL 2 3                    # per-process CPU over three samples
iostat -xz 2 3                           # per-device utilisation, queue depth, await
ps -eo pid,stat,wchan:24,cmd | awk '$2 ~ /^D/'   # what the blocked processes are waiting on

Interprete a combinação:

  • Carga alta, us alto, wa baixo: saturação real de CPU. Encontre o processo com pidstat; faça profiling ou escale horizontalmente.
  • Carga alta, wa alto, processos em D: armazenamento ou sistema de arquivos em rede. iostat -x mostra await e %util por dispositivo; um disco saturado ou um mount NFS travado produz exatamente esse quadro.
  • Carga alta, sy alto: tempo de kernel — muitas vezes uma tempestade de interrupções, um número absurdo de trocas de contexto ou um programa chamando o kernel com frequência demais. vmstat 1 mostra cs e in.
  • Carga baixa, aplicação lenta: o gargalo está em outro lugar. Um lock, uma query no banco, um serviço upstream. Quem responde isso são as métricas da aplicação, não as do host — e esse é o argumento a favor da instrumentação descrita em actuator e observabilidade.

Olhando dentro de um processo

Quando um processo específico está se comportando mal, quatro ferramentas revelam o que ele está fazendo sem precisar de um depurador.

sudo lsof -p 4821 | head -30              # open files, sockets, libraries
sudo lsof -i :8080                        # who holds a port
sudo lsof /var/lib/mysql                  # who is using a path
ls -l /proc/4821/fd | wc -l               # file descriptor count
cat /proc/4821/limits | grep 'open files' # against the limit
cat /proc/4821/status | grep -E 'State|Threads|VmRSS'
sudo cat /proc/4821/stack                 # kernel stack of a hung task

O esgotamento de descritores é uma falha comum e confusa: a aplicação registra "too many open files", para de aceitar conexões novas e parece saudável em todas as métricas do host. Compare a contagem com o limite e aumente-o na unidade com LimitNOFILE=, e não em um profile de shell, que um serviço nunca lê.

O strace mostra as chamadas de sistema que um processo faz. Anexe-o a um processo em execução e comece pelo resumo, porque a saída bruta é avassaladora:

sudo strace -c -p 4821                    # counts and time per syscall; Ctrl-C to report
sudo strace -f -e trace=openat,connect -p 4821
sudo strace -f -T -o /tmp/trace.txt -p 4821

Um resumo dominado por futex sugere contenção de locks; um dominado por openat retornando ENOENT geralmente indica um caminho de busca errado; chamadas connect repetidas retornando ECONNREFUSED ou ETIMEDOUT apontam a dependência que sumiu. Anexar o strace pausa o processo-alvo por um instante e o deixa visivelmente mais lento, então tome cuidado com um processo de produção sob muita carga e prefira uma captura curta. O ltrace faz o mesmo para chamadas de biblioteca; o perf top amostra onde o tempo de CPU está realmente sendo gasto.

Mensagens do kernel

O dmesg lê o ring buffer do kernel, onde eventos de hardware e de drivers chegam antes que qualquer daemon de log os veja. dmesg -T adiciona timestamps legíveis e dmesg -w acompanha ao vivo.

Mensagens que vale a pena reconhecer: EXT4-fs error e I/O error, dev sda indicam armazenamento falhando — rode smartctl -a /dev/sda imediatamente. nf_conntrack: table full, dropping packet significa que a tabela de rastreamento de conexões está cheia, o que aparece como falhas de rede aleatórias sob carga. TCP: request_sock_TCP: Possible SYN flooding significa que a fila de accept estourou. segfault at … ip … registra um binário que caiu. Under memory pressure e oom-kill são o problema de memória descrito acima.

Como o buffer tem tamanho fixo, as mensagens antigas são sobrescritas. journalctl -k -b -1 recupera as mensagens do kernel do boot anterior quando o ring buffer já não as tem, o que é essencial depois de um reboot inexplicado.

Verificando um serviço de ponta a ponta

Uma passada estruturada por um serviço, de fora para dentro:

systemctl status orders-api                        # running? restarts? since when?
systemctl show orders-api -p NRestarts -p ExecMainStartTimestamp
journalctl -u orders-api -p warning --since "1 hour ago"
sudo ss -ltnp | grep 8080                          # listening, and on what address
curl -sS -o /dev/null -w '%{http_code} %{time_total}\n' http://127.0.0.1:8080/healthz
getent hosts db.internal && nc -vz db.internal 5432 # dependency reachable
sudo lsof -p "$(systemctl show -p MainPID --value orders-api)" | wc -l
journalctl -u orders-api --since "1 hour ago" -o cat | grep -c 'ERROR'

Cada linha descarta alguma coisa. Um NRestarts subindo mostra que a unidade está em loop de crash e que o log interessante está logo antes de cada reinício. Um curl local que responde bem enquanto as requisições externas falham leva a investigação para o proxy ou para o firewall. Uma dependência que não resolve ou não aceita conexão encerra a busca na hora.

Depois do incidente

Dois hábitos se pagam sozinhos. Primeiro, capture evidências antes de reiniciar: um thread dump, as últimas centenas de linhas do journal, a saída de ps, free, df. Um reinício costuma apagar o sintoma e destruir a explicação, e a próxima ocorrência fica tão misteriosa quanto a primeira.

Segundo, escreva uma nota curta e sem caça a culpados: linha do tempo, sintoma, causa raiz, o que dificultou o diagnóstico e uma mudança concreta. O resultado mais valioso de um incidente muitas vezes não é a correção, e sim o alerta que faltava, a linha de log que deveria existir ou a configuração de retenção por causa da qual as evidências de ontem já tinham sumido.

Erros comuns

  • Reiniciar primeiro. Você perde as evidências e não aprende nada; a falha volta às 3 da manhã.
  • Ler só a última linha do log. A entrada interessante costuma ser o primeiro erro da sequência, antes da cascata de consequências.
  • Apagar um arquivo de log grande que ainda está aberto. Nenhum espaço é liberado. Trunque e depois corrija a rotação.
  • Tratar load average como uso de CPU. Confira o wa e os processos em estado D antes de concluir que você precisa de mais cores.
  • Entrar em pânico com pouca memória "free". Leia a coluna available; o cache está fazendo o trabalho dele.
  • Deixar o log em debug depois de uma investigação. Ele enche o disco silenciosamente uma semana depois.
  • Não ativar o armazenamento persistente do journal. Tudo sobre a falha que causou o reboot se perdeu.
  • Rodar strace -f por minutos em um processo de produção sob carga. A lentidão vira o novo incidente.

Principais pontos

  • Siga uma ordem fixa — sintoma, momento, raio de impacto, depois serviço, log, recurso, dependência, mudança — e anote o que você descartou.
  • O journal guarda a saída das unidades e do kernel; aplicações que cuidam dos próprios arquivos continuam gravando em /var/log. Verifique os dois.
  • journalctl -u … -p err --since, juntando várias unidades e com -o cat para encadear comandos, responde à maioria das perguntas sobre logs.
  • Limite os dois armazenamentos de log: SystemMaxUse para o journal e uma regra de logrotate com um reload no postrotate para os arquivos.
  • Diferencie bytes esgotados de inodes esgotados, e trunque arquivos abertos em vez de apagá-los.
  • Um OOM kill aparece no dmesg; contenha-o com limites de cgroup na unidade em vez de adicionar RAM às cegas.
  • Carga alta com CPUs ociosas e processos em estado D é problema de armazenamento ou de rede — iostat -x e wchan vão apontar a causa.
  • lsof, /proc/<pid>/, strace -c e dmesg -T explicam o que um processo está fazendo; capture evidências antes de reiniciar qualquer coisa.

Fontes e leituras complementares

Perguntas frequentes

Onde os logs ficam de fato em um sistema moderno: no journal ou em /var/log?
Nos dois. Tudo o que o systemd inicia envia a saída padrão e a saída de erro para o journal, um armazenamento binário e indexado que você lê com journalctl. Aplicações que abrem seus próprios arquivos de log continuam gravando texto puro em /var/log; normalmente são servidores web, bancos de dados e sistemas de e-mail. Muitas distribuições também repassam o journal para o rsyslog, então a mesma mensagem aparece em /var/log/syslog. Verifique os dois antes de concluir que uma mensagem não existe.
Por que meu disco enche de novo no dia seguinte depois que apago um arquivo de log grande?
Apagar um arquivo que um processo ainda mantém aberto não libera espaço até que o processo o feche, e o serviço continua gravando no mesmo descritor. O correto é truncar o arquivo no lugar e depois resolver a causa com rotação de logs e um limite de retenção. Se o que está crescendo é o journal, defina SystemMaxUse no journald.conf e recupere espaço na hora com journalctl --vacuum-size.
O load average está em 20, mas o top mostra as CPUs quase ociosas. O que isso significa?
No Linux, o load average conta tanto os processos prontos para rodar quanto os que estão em sono não interrompível, então I/O de disco ou de rede bloqueado faz o número subir sem usar CPU. Confira a coluna wa no top ou no iostat, procure processos no estado D e meça a latência dos dispositivos com iostat -x. Uma carga de 20 com 90 % de CPU ociosa é problema de armazenamento ou de sistema de arquivos em rede, não falta de poder de processamento.
Como saber se o kernel matou meu processo ou se ele terminou sozinho?
Procure uma linha Out of memory no dmesg ou no journalctl -k perto do momento em que ele sumiu; o kernel imprime a vítima escolhida, a pontuação dela e uma tabela dos maiores consumidores de memória. O systemctl status também informa o motivo da saída e mostra o sinal 9 quando o processo principal de uma unidade foi morto pelo OOM killer. Um código de saída diferente de zero, limpo e sem mensagem do kernel, significa que a própria aplicação decidiu parar, então a resposta está no log dela.