Logs e monitoramento do Nginx

Logs do Nginx são a primeira fonte de informação quando algo dá errado, 502 de um backend fora do ar, pico de tráfego ou ataque de brute force. Com formato de log estruturado, GoAccess para análise em tempo real e Prometheus para métricas históricas, você tem visibilidade completa do tráfego da VPS.

Formato de log JSON para análise fácil

Configure o Nginx para logar em JSON para facilitar parsing:

nginx
# /etc/nginx/nginx.conf — dentro do bloco http {}

log_format json_combined escape=json
    '{'
        '"time_local":"$time_local",'
        '"remote_addr":"$remote_addr",'
        '"request":"$request",'
        '"status":$status,'
        '"body_bytes_sent":$body_bytes_sent,'
        '"request_time":$request_time,'
        '"upstream_response_time":"$upstream_response_time",'
        '"upstream_addr":"$upstream_addr",'
        '"http_referer":"$http_referer",'
        '"http_user_agent":"$http_user_agent",'
        '"http_x_forwarded_for":"$http_x_forwarded_for",'
        '"cache_status":"$upstream_cache_status"'
    '}';

access_log /var/log/nginx/access.log json_combined;
error_log  /var/log/nginx/error.log warn;

# Separar logs por virtual host:
server {
    server_name api.seudominio.com.br;
    access_log /var/log/nginx/api.access.log json_combined;
    error_log  /var/log/nginx/api.error.log warn;
}

Análise em tempo real com GoAccess

GoAccess processa logs do Nginx e gera relatório HTML ou dashboard no terminal:

bash
# Instalar GoAccess
sudo apt install -y goaccess

# Análise interativa no terminal (tempo real):
sudo goaccess /var/log/nginx/access.log -c

# Gerar relatório HTML estático:
sudo goaccess /var/log/nginx/access.log \
  --log-format=COMBINED \
  -o /var/www/html/report.html \
  --real-time-html

# Para logs em formato JSON customizado:
sudo goaccess /var/log/nginx/access.log \
  --log-format='{ "time_local":"%d", "remote_addr":"%h", "request":"%r", "status":%s, "body_bytes_sent":%b, "request_time":%T }' \
  --date-format='%d/%b/%Y:%H:%M:%S %z' \
  -o /var/www/html/report.html

# Dashboard em tempo real (WebSocket):
sudo goaccess /var/log/nginx/access.log \
  --real-time-html \
  --ws-url=ws://seudominio.com.br:7890 \
  -o /var/www/html/nginx-report.html &

Exportar métricas para Prometheus

nginx-prometheus-exporter coleta métricas do Nginx e as expõe para o Prometheus:

yaml
# 1. Habilitar stub_status no Nginx:
server {
    listen 127.0.0.1:8080;
    location /nginx-status {
        stub_status;
        allow 127.0.0.1;
        deny all;
    }
}

# 2. Docker Compose com nginx-prometheus-exporter:
services:
  nginx-exporter:
    image: nginx/nginx-prometheus-exporter:latest
    restart: always
    command:
      - "--nginx.scrape-uri=http://127.0.0.1:8080/nginx-status"
    ports:
      - "127.0.0.1:9113:9113"
    extra_hosts:
      - "localhost:host-gateway"

# 3. prometheus.yml:
scrape_configs:
  - job_name: 'nginx'
    static_configs:
      - targets: ['localhost:9113']

# Métricas disponíveis:
# nginx_connections_active
# nginx_connections_waiting
# nginx_http_requests_total
# nginx_connections_accepted_total

Alertas para erros 5xx e latência alta

Regras de alerta no Prometheus para problemas críticos do Nginx:

yaml
# prometheus-rules-nginx.yml
groups:
  - name: nginx
    rules:
      # Taxa de erros 5xx > 5% por 5 minutos
      - alert: NginxHigh5xxRate
        expr: |
          sum(rate(nginx_http_requests_total{status=~"5.."}[5m]))
          / sum(rate(nginx_http_requests_total[5m])) * 100 > 5
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Nginx: taxa de erros 5xx > 5% nos últimos 5 minutos"

      # Nginx sem receber requisições (possível queda)
      - alert: NginxDown
        expr: nginx_up == 0
        for: 1m
        labels:
          severity: critical

      # Muitas conexões em espera (possível gargalo no backend)
      - alert: NginxHighWaitingConnections
        expr: nginx_connections_waiting > 100
        for: 5m
        labels:
          severity: warning

# Para latência: use ngx_http_vhost_traffic_status_module
# ou analise os logs via Loki + Grafana LogQL:
# avg by (host) (request_time) > 2

Rotação de logs e retenção

Logrotate evita que os logs ocupem todo o disco:

bash
# /etc/logrotate.d/nginx (geralmente já existe — verificar e ajustar)
/var/log/nginx/*.log {
    daily              # rotacionar diariamente
    missingok          # não errar se o arquivo não existir
    rotate 30          # manter 30 arquivos (30 dias)
    compress           # comprimir logs antigos com gzip
    delaycompress      # não comprimir o arquivo mais recente (ainda pode ser escrito)
    notifempty         # não rotacionar arquivos vazios
    create 0640 www-data adm    # permissões do novo arquivo
    sharedscripts
    postrotate
        # Sinalizar ao Nginx para reabrir os arquivos de log
        if [ -f /var/run/nginx.pid ]; then
            kill -USR1 $(cat /var/run/nginx.pid)
        fi
    endscript
}

# Testar a configuração:
sudo logrotate -d /etc/logrotate.d/nginx   # debug (não executa)
sudo logrotate -f /etc/logrotate.d/nginx   # forçar rotação

# Verificar espaço usado pelos logs:
sudo du -sh /var/log/nginx/

Não quer configurar manualmente?

Implante o VPS em menos de 5 minutos com a Runstack. Infraestrutura da OPEN Datacenter, com servidores em São Paulo.

Perguntas frequentes

$remote_addr: IP do cliente (ou do proxy). $request: método + URL + versão HTTP. $status: código HTTP da resposta. $body_bytes_sent: bytes enviados (sem headers). $request_time: tempo total da requisição em segundos. $upstream_response_time: tempo que o backend levou para responder. $upstream_addr: IP:porta do backend que processou.

502 Bad Gateway no Nginx significa que ele não conseguiu resposta válida do backend. Verifique: (1) se o backend está rodando: curl http://127.0.0.1:PORTA/health; (2) error.log do Nginx: sudo tail -f /var/log/nginx/error.log, mostrará "connect() failed" ou "upstream prematurely closed"; (3) logs do backend: journalctl -u minha-app ou docker logs container.

Combined: formato padrão legível por humanos, compatível com GoAccess sem config extra. JSON: melhor para ingestão em sistemas como Loki, Elasticsearch ou Datadog, cada campo é um atributo separado, sem parsing de regex. Para VPS simples com GoAccess: Combined. Para infraestrutura com centralizador de logs: JSON.

awk e grep são poderosos: awk "$4 >= "[07/Jun/2026:" && $4 <= "[07/Jun/2026:23"" /var/log/nginx/access.log. Para logs JSON: jq 'select(.time_local >= "07/Jun/2026:10")' /var/log/nginx/access.log. GoAccess: goaccess access.log --date-spec=hr para análise por hora.

Quando há Cloudflare ou outro CDN: o $remote_addr é o IP do CDN, não do usuário. O IP real está em $http_cf_connecting_ip (Cloudflare) ou $http_x_forwarded_for. Configure: set_real_ip_from 103.21.244.0/22; (range Cloudflare) e real_ip_header CF-Connecting-IP; para que $remote_addr reflita o IP real.