Grafana Loki + Promtail: Logs Centralizados para PME em 2026
Neste artigo
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.
Artigos relacionados no kbase.pt