Grafana Loki + Promtail: Logs Centralizados para PME em 2026

1. Introdução

Gerir registos de múltiplos servidores, contentores e aplicações é um desafio crescente para qualquer PME. Quando um serviço falha, saltar de servidor em servidor com ssh e tail -f consome tempo valioso. Uma solução de registos centralizados resolve este problema agregando todos os registos num único local, pesquisável e visualizável.

O Grafana Loki é um sistema de agregação de registos open-source, criado pela equipa do Grafana, desenhado especificamente para ser económico em armazenamento e simples de operar. Ao contrário de soluções tradicionais como o Elasticsearch, o Loki não indexa o conteúdo completo dos registos — indexa apenas um conjunto reduzido de labels (etiquetas) associadas a cada fluxo de registos. Este modelo reduz drasticamente o custo de infraestrutura e a complexidade de operação.

O Promtail é o agente responsável por recolher registos das máquinas, adicionar labels e enviá-los para o Loki. Juntos, Loki + Promtail formam uma stack leve que se integra nativamente com o Grafana, permitindo visualizar registos lado a lado com métricas e traces.

Neste guia prático, vamos instalar a stack completa via Docker Compose, configurar o Promtail para recolher registos de contentores Docker, escrever consultas LogQL, definir alertas no Grafana e partilhar boas práticas validadas em produção.

ℹ Para quem é este guia

Administradores de sistemas e equipas de operações em PME que já usam Grafana para métricas (Prometheus) e querem adicionar registos centralizados sem multiplicar a infraestrutura nem o custo.

2. Loki vs ELK

A stack ELK (Elasticsearch, Logstash, Kibana) tem sido a solução de referência para registos centralizados durante uma década. No entanto, para PME com orçamentos limitados, o ELK tornou-se caro: o Elasticsearch indexa texto completo, o que consome muito CPU, muita memória RAM e armazenamento significativo. Cada nó de Elasticsearch típico precisa de 8 GB a 16 GB de RAM apenas para funcionar de forma estável.

O Loki segue uma filosofia radicalmente diferente. Em vez de indexar cada palavra de cada linha de registo, comprime os registos em blocos e indexa apenas os labels — pares chave-valor como job=nginx ou env=produção. A pesquisa de texto completo é feita na altura da consulta, percorrendo os blocos comprimidos. Isto troca velocidade de pesquisa por custo de armazenamento drasticamente menor.

Critério Loki ELK (Elasticsearch)
Indexação Só labels (leve) Texto completo (pesado)
RAM mínima recomendada 1-2 GB 8-16 GB por nó
Armazenamento ~1-5% do ELK Elevado (índice invertido)
Pesquisa de texto Mais lenta (regex em blocos) Rápida (índice invertido)
Integração Nativa com Grafana Kibana (separado)
Linguagem de consulta LogQL (simples) KQL / Lucene
Licença AGPLv3 (gratuito) Elastic License (restritivo)

Em resumo: se precisa de pesquisa de texto completo instantânea sobre terabytes de registos e tem orçamento para tal, o ELK continua a ser uma opção válida. Se quer registos centralizados a custo reduzido, com boa integração no Grafana e operação simples, o Loki é a escolha natural para PME em 2026.

ℹ Loki não substitui pesquisa full-text em tempo real

O Loki foi desenhado para inspecionar registos filtrados por labels — não para buscar palavras em terabytes de histórico instantaneamente. Para esse caso de uso, o ELK ou ClickHouse são mais adequados.

3. Instalação via Docker Compose

A forma mais rápida de colocar a stack a funcionar é com Docker Compose. Vamos correr três serviços: o Loki (armazenamento e consulta), o Promtail (recolha de registos) e o Grafana (visualização). O Grafana já inclui suporte nativo para Loki como fonte de dados, sem necessidade de plugins adicionais.

Crie um directório de projecto e o ficheiro de configuração do Loki:

mkdir -p loki-stack/config && cd loki-stack

Crie o ficheiro config/loki-config.yaml com a configuração mínima do Loki em modo monolítico, que é suficiente para começar:

auth_enabled: false

server:
  http_listen_port: 3100

common:
  path_prefix: /loki
  storage:
    filesystem:
      chunks_directory: /loki/chunks
      rules_directory: /loki/rules
  replication_factor: 1
  ring:
    kvstore:
      store: inmemory

schema_config:
  configs:
    - from: 2024-01-01
      store: tsdb
      object_store: filesystem
      schema: v13
      index:
        prefix: index_
        period: 24h

limits_config:
  retention_period: 720h
  max_query_length: 721h
  allow_structured_metadata: true

analytics:
  reporting_enabled: false

Agora crie o docker-compose.yml que orquestra os três serviços:

services:
  loki:
    image: grafana/loki:3.5.5
    container_name: loki
    ports:
      - "3100:3100"
    volumes:
      - ./config/loki-config.yaml:/etc/loki/local-config.yaml
      - loki-data:/loki
    command: -config.file=/etc/loki/local-config.yaml
    restart: unless-stopped

  promtail:
    image: grafana/promtail:3.5.5
    container_name: promtail
    volumes:
      - /var/log:/var/log:ro
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - ./config/promtail-config.yaml:/etc/promtail/config.yml
    command: -config.file=/etc/promtail/config.yml
    restart: unless-stopped
    depends_on:
      - loki

  grafana:
    image: grafana/grafana:12.2.2
    container_name: grafana
    ports:
      - "3000:3000"
    volumes:
      - grafana-data:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=mudar-esta-password
    restart: unless-stopped
    depends_on:
      - loki

volumes:
  loki-data:
  grafana-data:

Inicie a stack:

docker compose up -d

Verifique que o Loki está pronto a receber registos:

curl -s http://localhost:3100/ready

A resposta deve ser ready for 1m0s. Aceda ao Grafana em http://localhost:3000 com o utilizador admin e a palavra-passe definida no Compose.

⚠ Não exponha o Grafana sem HTTPS em produção

A porta 3000 do exemplo usa HTTP simples. Em produção, coloque um proxy inverso (Nginx, Caddy, Traefik) à frente com TLS e, idealmente, autenticação OIDC ou LDAP. Nunca exponha o Grafana diretamente na Internet sem encriptação.

4. Configurar Promtail

O Promtail é o agente que lê ficheiros de registo, extrai labels, filtra conteúdo e envia tudo para o Loki. A sua configuração divide-se em quatro secções: server (porta e endpoints), positions (registo de até onde leu cada ficheiro), clients (para onde enviar) e scrape_configs (o que recolher e como processar).

Crie o ficheiro config/promtail-config.yaml:

server:
  http_listen_port: 9080

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  # Registos de contentores Docker
  - job_name: docker
    docker_sd_configs:
      - host: unix:///var/run/docker.sock
        refresh_interval: 5s
    relabel_configs:
      - source_labels: [__meta_docker_container_name]
        target_label: container
        regex: '/(.*)'
      - source_labels: [__meta_docker_container_log_stream]
        target_label: stream
      - source_labels: [__meta_docker_container_label_com_docker_compose_service]
        target_label: job
    pipeline_stages:
      - docker:
      - labels:
          stream:

  # Registos de sistema (journald)
  - job_name: journald
    journal:
      path: /var/log/journal
      labels:
        job: journald
        env: producao
    relabel_configs:
      - source_labels: [__journal__systemd_unit]
        target_label: unit

Para que o Promtail consiga aceder ao socket do Docker, adicione o volume do socket no docker-compose.yml sob o serviço promtail:

    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /var/log:/var/log:ro
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - ./config/promtail-config.yaml:/etc/promtail/config.yml

Reinicie o Promtail para aplicar a nova configuração:

docker compose restart promtail

O Promtail descobre automaticamente todos os contentores em execução via docker_sd_configs e cria um label container com o nome de cada contentor. O label job é extraído da label Docker Compose com.docker.compose.service, permitindo filtrar por serviço (ex: nginx, postgres).

ℹ Pipeline stages permitem extrair campos

A secção pipeline_stages pode usar regex, json e timestamp para parsear linhas de registo, extrair variáveis e normalizar datas. Isto é útil para converter registos não estruturados em dados pesquisáveis.

5. Queries LogQL

O LogQL é a linguagem de consulta do Loki, inspirada no PromQL do Prometheus. Uma consulta LogQL tem duas partes: o selector de labels (que fluxos de registos seleccionar) e opcionalmente uma pipeline de processamento (filtrar, extrair ou agregar linhas dentro desses fluxos). A sintaxe básica é {label="valor"}.

Consultas básicas — seleccionar registos por labels:

# Todos os registos do container nginx
{container="nginx"}

# Registos de erro do nginx em producao
{container="nginx", stream="stderr"}

# Todos os registos de um job especifico
{job="postgres"}

Filtrar linhas com |= (contém), != (não contém) e |~ (regex):

# Erros que contem "timeout"
{container="nginx"} |= "timeout"

# Erros 500 sem requisicoes de health check
{container="nginx"} |= " 500 " != "healthcheck"

# Linhas que coincidem com uma expressao regular
{job="app"} |~ "ERRO|ERROR|FATAL"

Agregações e métricas — contar registos por minuto, por exemplo, permite criar gráficos de taxa de erros no Grafana:

# Contar linhas nos ultimos 5 minutos, por job
sum by (job) (count_over_time({job=~".+"}[5m]))

# Taxa de erros 500 por minuto no nginx
sum by (container) (
  rate({container="nginx"} |= " 500 " [1m])
)

# Top 10 containers com mais registos nos ultimos 15m
topk(10,
  sum by (container) (count_over_time({container=~".+"}[15m]))
)

No Grafana, estas consultas são usadas em painéis do tipo Logs (para ver linhas individuais) ou Time series (para gráficos de agregação). A integração Loki + Grafana permite combinar métricas do Prometheus e registos do Loki no mesmo dashboard, correlacionando picos de CPU com mensagens de erro em tempo real.

6. Alertas no Grafana

O Grafana inclui um motor de alertas unificado que funciona com qualquer fonte de dados, incluindo o Loki. Os alertas são definidos em regras que avaliam consultas LogQL periodicamente e dispara notificações quando as condições são satisfeitas. As notificações são enviadas via contact points — canais como e-mail, Slack, Discord, Telegram ou webhooks personalizados.

Para criar um alerta de taxa de erros elevada, aceda a Alerting no menu lateral do Grafana e siga estes passos:

Passo Acção
1 Ir a Alerting → Alert rules → New alert rule
2 Definir a consulta LogQL, ex: sum(rate({container="nginx"} |= " 500 " [1m]))
3 Definir a condição: WHEN last() IS ABOVE 5
4 Configurar evaluation group (intervalo de 1m) e evaluation delay (1m)
5 Associar um contact point (Slack, e-mail, Telegram) e uma notification policy
6 Guardar e testar com o botão Test rule

Exemplos de regras de alerta úteis para registos:

# Alerta: nenhum registo recebido nos ultimos 5 minutos
count_over_time({job="nginx"}[5m]) == 0

# Alerta: pico de mensagens de erro na aplicacao
sum by (container) (
  rate({container="app"} |~ "ERROR|FATAL" [2m])
) > 10

# Alerta: contentor parou de enviar registos
absent_over_time({container="critical-service"}[10m])

⚠ Evite alertas sobre janelas demasiado curtas

Janelas de 30 segundos geram falsos positivos quando há latência no envio de registos. Use no mínimo 2 a 5 minutos para alertas de taxa e 10 minutos para alertas de ausência de registos.

7. Boas Práticas

Após instalar e configurar a stack, há decisões de operação que diferenciam uma implementação estável de uma que degrada com o tempo. Estas recomendações baseiam-se na documentação oficial do Loki e em experiência prática em ambientes de PME.

1. Use poucos labels com cardinalidade baixa. O Loki indexa labels — se cada contentor tiver labels únicos (ex: request_id com milhares de valores), o índice cresce explosivamente. Limite-se a labels como job, container, env e instance. Campos variáveis como IDs de requisição devem ser extraídos no momento da consulta com | json ou | regexp, não armazenados como labels.

2. Configure retenção adequada. O parâmetro retention_period no limits_config define quanto tempo os registos são guardados. Para PME, 30 dias (720h) é um equilíbrio razoável entre diagnóstico e armazenamento. Para auditorias legais, considere 90 dias (2160h), mas avalie o espaço em disco necessário.

3. Monitore o próprio Loki. O Loki expõe métricas Prometheus em /metrics. Adicione o Loki como target no Prometheus e acompanhe indicadores como loki_distributor_lines_received_total (taxa de entrada), loki_ingester_chunks_stored_total (chunks criados) e loki_request_duration_seconds (latência de consultas).

4. Faça backup do volume do Loki. Os dados do Loki ficam no volume loki-data. Inclua este volume na estratégia de backup regular. Para ambientes maiores, considere migrar o armazenamento de filesystem para S3 ou MinIO — o Loki suporta object stores compatíveis com S3 nativamente.

5. Estruture os registos das aplicações. Registos em JSON são muito mais fáceis de processar no Loki. Configure as aplicações para emitir JSON com campos consistentes (level, timestamp, service, message). O Promtail pode então usar o json pipeline stage para extrair campos automaticamente, tornando-os pesquisáveis sem regex.

6. Use query limits e caching. O Loki suporta max_query_length para limitar o intervalo máximo de uma consulta e caching de resultados via memcached ou Redis. Para PME com consultas repetitivas (dashboards partilhados), o caching reduz drasticamente a carga no Loki.

ℹ Recursos oficiais

A documentação completa do Loki, incluindo configuração de armazenamento em S3, modos de escalabilidade (microservices), e referência completa do LogQL, está disponível em grafana.com/docs/loki/latest.

A stack Grafana Loki + Promtail oferece a PME uma solução de registos centralizados que é económica, simples de operar e integra-se nativamente com dashboards e alertas que já existem no Grafana. Comece com a configuração monolítica apresentada aqui e evolua para modos distribuídos à medida que o volume de registos crescer.