Grafana Loki no Node.js é uma combinação usada para centralizar logs de aplicações, consultar eventos com LogQL e correlacionar erros com métricas e traces. Loki não funciona como um banco de texto completo tradicional: ele indexa principalmente labels e mantém o conteúdo dos logs em armazenamento otimizado, reduzindo custo quando a estratégia de labels é bem planejada.
Uma integração eficiente começa na aplicação. Logs precisam ser estruturados, conter timestamp, nível, serviço, ambiente e contexto de correlação, mas não devem incluir senhas, tokens ou dados pessoais. Depois, um agente ou collector encaminha os eventos para Loki, onde Grafana permite pesquisa, dashboards e alertas.
Neste guia, você aprenderá a produzir logs JSON em Node.js, enviar com Grafana Alloy ou OpenTelemetry Collector, planejar labels, usar structured metadata, consultar com LogQL, criar alertas e evitar cardinalidade alta.
O que é Grafana Loki?
Loki é um sistema de agregação de logs desenvolvido pela Grafana Labs. A documentação de envio de logs ao Loki recomenda clientes como Grafana Alloy e OpenTelemetry Collector, além de integrações comunitárias.
Em uma arquitetura comum:
- a aplicação Node.js escreve JSON em stdout;
- o runtime ou container coleta a saída;
- Alloy, OTel Collector, Fluent Bit ou outro agente processa os eventos;
- o agente envia para Loki;
- Grafana consulta e visualiza.
Essa separação evita que a aplicação dependa diretamente da disponibilidade do backend de logs.
Por que não enviar diretamente da aplicação?
Enviar cada log por HTTP dentro do processo adiciona latência, retries, buffers e risco de perder eventos durante falhas. Uma biblioteca de transporte pode funcionar em cenários simples, mas stdout com collector externo costuma ser mais resiliente em containers e Kubernetes.
Para logs estruturados, consulte Logs com Pino no Node.js e Logs com Winston no Node.js.
Logs JSON com Pino
import pino from 'pino';
export const logger = pino({
level: process.env.LOG_LEVEL ?? 'info',
base: {
service: 'orders-api',
environment: process.env.NODE_ENV ?? 'development',
version: process.env.APP_VERSION ?? 'unknown'
},
redact: {
paths: [
'req.headers.authorization',
'password',
'*.token',
'*.secret'
],
censor: '[REDACTED]'
}
});Escrever um objeto cria campos consultáveis sem depender de regex frágil.
logger.info({
operation: 'order.create',
orderId: order.id,
customerId: customer.id,
durationMs
}, 'Pedido criado');Contexto por requisição
Inclua request ID e trace ID:
logger.info({
requestId,
traceId,
method: req.method,
route: req.route?.path,
statusCode: res.statusCode,
durationMs
}, 'request_completed');Use nomes de rota normalizados, não URLs com IDs, para evitar cardinalidade e exposição. Para propagar contexto, veja AsyncLocalStorage no Node.js.
Labels no Loki
Labels são indexadas e definem streams. Use valores de baixa cardinalidade:
service;environment;namespace;cluster;level, com cuidado;containeroupod, dependendo da retenção.
Não use como labels:
- request ID;
- user ID;
- order ID;
- trace ID;
- URL completa;
- mensagem de erro;
- timestamp.
Esses campos possuem alta cardinalidade e devem permanecer no conteúdo ou em structured metadata.
Structured metadata
Structured metadata permite armazenar campos pesquisáveis junto ao log sem transformá-los em labels indexadas. Trace IDs e request IDs são bons candidatos. Verifique compatibilidade da versão do Loki, collector e formato de ingestão.
Grafana Alloy
Grafana Alloy é uma distribuição do OpenTelemetry Collector com componentes para logs, métricas, traces e profiles. Um fluxo típico lê stdout de containers, decodifica JSON, promove poucos campos a labels e envia para Loki.
Exemplo conceitual:
loki.source.file "node_logs" {
targets = [{ __path__ = "/var/log/app/*.log" }]
forward_to = [loki.process.node.receiver]
}
loki.process "node" {
stage.json {
expressions = {
level = "level",
service = "service",
environment = "environment"
}
}
stage.labels {
values = {
level = "",
service = "",
environment = ""
}
}
forward_to = [loki.write.default.receiver]
}A sintaxe exata depende da versão. Valide com a documentação atual do Alloy.
OpenTelemetry Collector
Loki suporta ingestão de logs OpenTelemetry por HTTP. A aplicação pode usar logging comum em stdout e o collector receber via filelog, ou pode emitir logs pelo SDK quando a arquitetura justificar.
Para traces, consulte OpenTelemetry no Node.js.
Docker
Em Docker, escreva logs em stdout e configure um agente no host. Evite gravar arquivos dentro do container sem rotação e volume.
docker logs -f orders-apiO Docker logging driver para Loki existe, mas um collector central oferece processamento e roteamento mais flexíveis.
Kubernetes
Em Kubernetes, um DaemonSet de Alloy ou collector pode ler arquivos de logs dos containers. Labels do cluster, namespace, workload e container são adicionadas pelo pipeline.
Não transforme todas as labels do Kubernetes em labels do Loki. Selecione as necessárias para reduzir cardinalidade e custo.
Primeiras consultas LogQL
{service="orders-api", environment="production"}Filtrar texto:
{service="orders-api"} |= "Pedido criado"Excluir health checks:
{service="orders-api"} != "/health"Parse de JSON
{service="orders-api"}
| json
| level = "error"Depois do json, campos podem ser usados em filtros e formatação.
{service="orders-api"}
| json
| statusCode >= 500
| line_format "{{.method}} {{.route}} {{.statusCode}} {{.durationMs}}ms"Erros por intervalo
sum by (service) (
count_over_time(
{environment="production"}
| json
| level="error"
[5m]
)
)Consultas métricas permitem dashboards e alertas baseados em logs.
Latência a partir de logs
quantile_over_time(
0.99,
{service="orders-api"}
| json
| unwrap durationMs
[5m]
) by (route)Para SLOs, métricas nativas geralmente são mais eficientes. Logs são úteis para investigação e validação.
Alertas
Um alerta pode detectar aumento de erros:
sum(
count_over_time(
{service="orders-api", environment="production"}
| json
| level="error"
[5m]
)
) > 20Evite alertar por uma única mensagem. Use taxa, janela, severidade e impacto. Consulte Métricas Prometheus no Node.js para combinar sinais.
Correlação com traces
Inclua traceId no log. No Grafana, derived fields podem transformar o valor em link para Tempo ou Jaeger. Assim, um erro leva ao trace correspondente.
logger.error({
traceId,
requestId,
error: {
name: err.name,
message: err.message,
stack: err.stack
}
}, 'Falha ao criar pedido');Serialização de erros
O objeto Error possui propriedades não enumeráveis. Pino oferece serializadores; Winston exige configuração. Garanta que nome, mensagem e stack sejam preservados, com redação de dados sensíveis.
Não registre tudo
Logs excessivos aumentam custo e ruído. Defina:
- DEBUG para diagnóstico temporário;
- INFO para eventos operacionais;
- WARN para degradação recuperável;
- ERROR para falhas que exigem atenção;
- FATAL para encerramento do processo.
Evite um log INFO para cada item de loops grandes.
Sampling
Eventos de alto volume podem usar sampling:
if (Math.random() < 0.01) {
logger.info({ operation: 'cache.hit' }, 'Amostra de cache hit');
}Não aplique sampling indiscriminado a erros críticos ou auditoria.
Rate limiting de logs
Uma dependência indisponível pode gerar milhares de mensagens idênticas. Agregue ou limite:
logger.warn({
dependency: 'payments',
suppressedCount
}, 'Falhas repetidas suprimidas');Retenção
Defina retenção por necessidade operacional e legal. Logs DEBUG podem ter prazo curto; auditoria pode exigir política separada e armazenamento imutável. Loki não substitui automaticamente um sistema de compliance.
Segurança
- Use TLS na ingestão.
- Autentique tenants e collectors.
- Restrinja consulta por equipe.
- Redija segredos antes do envio.
- Evite payloads completos.
- Audite acesso aos logs.
- Separe ambientes.
Veja Gestão de Segredos no Node.js.
Multi-tenancy
Loki suporta separação por tenant. Em plataformas SaaS, não use IDs de clientes como labels sem avaliar cardinalidade e isolamento. O tenant do backend e os controles de acesso devem garantir separação.
Backpressure e indisponibilidade
O collector deve possuir fila, retry e limites. Se Loki estiver indisponível, o agente não pode consumir todo o disco. Defina buffers, persistência, política de descarte e alertas.
Teste local
Suba Loki e Grafana com Docker Compose, execute a API e confirme:
- JSON válido;
- labels esperadas;
- campos sensíveis redigidos;
- timestamps corretos;
- consulta por request ID;
- alerta de erro;
- retenção e rotação.
Dashboards úteis
- volume por serviço;
- erros por nível;
- top mensagens de erro;
- rotas com status 5xx;
- logs por versão;
- latência extraída;
- restarts e shutdowns;
- dependências indisponíveis.
Erros comuns
- IDs como labels: cardinalidade explode.
- Aplicação envia síncrono: logging afeta latência.
- Texto sem estrutura: consultas dependem de regex.
- Segredos nos logs: vazamento de dados.
- Sem limites: disco ou fila do collector cresce.
- Alertar por mensagem isolada: gera ruído.
- Guardar tudo para sempre: custo e risco aumentam.
Conclusão
Grafana Loki no Node.js funciona melhor quando a aplicação produz logs JSON limpos e um collector externo controla ingestão. Labels de baixa cardinalidade tornam consultas eficientes, enquanto campos dinâmicos permanecem no conteúdo ou structured metadata.
Combine Pino ou Winston, AsyncLocalStorage, Grafana Alloy ou OTel Collector, LogQL, alertas e políticas de segurança. Com estrutura, correlação e retenção planejadas, Loki transforma stdout disperso em uma fonte prática para investigar incidentes e acompanhar o comportamento de serviços Node.js.



