Collector FE — Instrumentação Frontend com Grafana Faro

Ele recebe payloads do Faro Web SDK pelo endpoint POST /collect/:tenant/:token e faz fan-out para os três sinais da stack LGTM:

  • Loki: logs estruturados, eventos, medições e exceções.
  • Mimir: métricas RUM via Prometheus remote_write.
  • Tempo: traces via OTLP/HTTP protobuf a partir de traces.resourceSpans.

O Collector FE é implantado na infraestrutura do cliente ou no cluster Elven dedicado ao cliente. A Elven fornece os endpoints e tokens de escrita. Para Elven Cloud, normalmente o mesmo tenant/token usado em Loki também é reutilizado em Mimir e Tempo.


Índice

  • Visão geral
  • Arquitetura
  • Contrato de payload
  • Compatibilidade com clientes antigos
  • Autenticação e multi-tenancy
  • Variáveis de ambiente
  • Instalação Linux via script
  • Deploy Docker
  • Deploy Kubernetes
  • Configuração do Faro no frontend
  • Métricas geradas
  • Logs estruturados
  • Traces
  • Migração de clientes antigos
  • Health check e validação
  • Troubleshooting
  • FAQ

Visão geral

O Collector FE é um serviço HTTP em Go especializado em payloads do Grafana Faro Web SDK 2.3.1. Ele não substitui o OpenTelemetry Collector de backend; ele existe para receber telemetria de navegador de forma segura, validar o tenant/token e converter os sinais para a stack LGTM.

Fluxo de request:

  1. O browser envia POST /collect/<tenant>/<jwt-token>.
  2. O collector valida o JWT HS256 usando SECRET_KEY.
  3. O body é decodificado como payload Faro 2.3.1.
  4. O payload é normalizado em logs, observações de métricas e traces.
  5. Os sinks habilitados recebem o lote em filas internas.
  6. O request retorna 200 quando todos os sinks habilitados aceitarem o lote na fila.
  7. Se uma fila obrigatória estiver cheia ou indisponível para enqueue, o request retorna 503.
  8. Falhas depois do enqueue viram logs/métricas internas do collector e não mudam a resposta já enviada ao browser.

Arquitetura

Browser / SPA
  Grafana Faro Web SDK 2.3.1
  @grafana/faro-web-tracing 2.3.1 opcional para traces
        |
        | POST /collect/:tenant/:jwt
        v
Collector FE
  decode -> normalize -> dispatch
        |
        +--> Loki  /loki/api/v1/push
        |
        +--> Mimir /api/v1/push          Prometheus remote_write
        |
        +--> Tempo /v1/traces            OTLP/HTTP protobuf

Separação de responsabilidades:

Camada Responsabilidade
Frontend Inicializar Faro, definir app/environment/release, habilitar tracing quando necessário e enviar payloads.
Collector FE Validar JWT, preservar contexto rico, controlar cardinalidade, gerar métricas RUM e despachar para sinks.
Loki Armazenar sinais ricos e investigáveis como logs estruturados.
Mimir Armazenar séries de métricas agregadas para dashboards e alertas.
Tempo Armazenar spans enviados pelo Faro tracing.

Contrato de payload

O contrato primário é o TransportBody do Faro 2.3.1.

Campos de topo suportados:

Campo Uso
meta Metadados de app, browser, usuário, sessão, página e view.
logs Logs capturados ou enviados manualmente pelo Faro.
events Eventos de navegação, recurso, user action, tracing sidecar e eventos customizados.
measurements Web Vitals e medições customizadas.
exceptions Exceções JavaScript com stacktrace, contexto e fingerprint.
traces Objeto OTLP contendo resourceSpans.

Metadados relevantes:

Campo Exemplos
meta.app name, namespace, release, version, environment, bundleId
meta.user id, email, username, fullName, roles, hash, attributes
meta.session id, attributes, overrides
meta.page id, url, attributes
meta.browser name, version, os, mobile, userAgent, language, brands, viewportWidth, viewportHeight

Sinais nativos tratados pelo collector:

Sinal Faro Uso no collector
measurements.type="web-vitals" Gera métricas para CLS, FCP, INP, LCP e TTFB.
events.name="faro.performance.navigation" Gera métricas de navegação e fases de navegação.
events.name="faro.performance.resource" Gera métricas de recursos, duração e bytes transferidos.
events.name="faro.user.action" Fonte de verdade para métricas de user action.
events.name="faro.tracing.fetch" Gera métricas HTTP client para fetch.
events.name="faro.tracing.xml-http-request" Gera métricas HTTP client para XHR.
events.name="faro.navigation" Opcional; só gera métricas se o evento existir no payload.

O collector não deriva métricas de traces brutos para evitar dupla contagem. Para HTTP client, use os eventos sidecar faro.tracing.*.


Compatibilidade com clientes antigos

O collector mantém compatibilidade com o payload JSON legado já aceito pela versão anterior quando ele representa logs, eventos, medições ou exceções do Faro. Esse payload é normalizado internamente para o modelo atual.

Limites importantes:

  • Payload Faro antigo em JSON continua aceito quando contém os campos estruturados que o collector já suportava.
  • Clientes que enviavam raw logfmt puro não devem ser migrados trocando apenas a URL. Eles precisam passar a enviar payload Faro JSON via SDK.
  • FID não é premissa da instrumentação atual. A métrica moderna de interação é INP.
  • traces só chegam quando o frontend instala @grafana/faro-web-tracing e registra TracingInstrumentation.
  • Custom measurements sem regra explícita geram contagem em faro_frontend_custom_measurements_total; séries semânticas detalhadas exigem METRICS_CUSTOM_MEASUREMENT_RULES.

Autenticação e multi-tenancy

Endpoint

POST /collect/:tenant/:token
Parte Descrição
tenant Identifica cliente/produto. Por padrão vira X-Scope-OrgID nos sinks multi-tenant.
token JWT HS256 assinado com a SECRET_KEY configurada no collector.

JWT

Claims obrigatórias:

Claim Regra
role Deve ser admin ou user.
iss Deve ser igual a JWT_ISSUER.

Claim opcional:

Claim Regra
exp Só é validada quando JWT_VALIDATE_EXP=true.

Exemplo:

{
  "role": "user",
  "iss": "trusted-issuer",
  "exp": 1770000000,
  "sub": "minha-app-frontend"
}

O JWT fica visível no browser porque faz parte da URL do Faro. Use role: user, limite CORS por domínio e, em produção, prefira JWT_VALIDATE_EXP=true com rotação planejada.


Variáveis de ambiente

Essenciais

Variável Obrigatória Default Descrição
SECRET_KEY Sim - Chave HS256 para validar JWTs. Mínimo de 64 caracteres.
LOKI_URL Sim - URL base do Loki. O collector adiciona /loki/api/v1/push.
LOKI_API_TOKEN Sim - Token enviado como Authorization: Bearer ... para Loki.
ALLOW_ORIGINS Sim - Origens CORS separadas por vírgula. Aceita * e https://*.example.com.
PORT Não 3000 Porta HTTP do collector.
JWT_ISSUER Não trusted-issuer Valor esperado na claim iss.
JWT_VALIDATE_EXP Não false Rejeita JWT expirado quando true.

LOKI_URL deve ser base, por exemplo:

LOKI_URL="https://loki.elvenobservability.com"

Não use:

LOKI_URL="https://loki.elvenobservability.com/loki/api/v1/push"

Loki

Variável Default Descrição
LOKI_TIMEOUT 15s Timeout por chamada ao Loki.
LOKI_QUEUE_SIZE 256 Tamanho da fila interna de streams.
LOKI_ENQUEUE_TIMEOUT 2s Tempo máximo esperando espaço na fila.
LOKI_RETRY_ATTEMPTS 3 Tentativas de exportação por lote.
LOKI_RETRY_BACKOFF 500ms Intervalo entre tentativas.
LOKI_MAX_EVENT_AGE 55m Eventos mais antigos têm timestamp ajustado para evitar rejeição.
LOKI_MAX_FUTURE_SKEW 10m Eventos muito no futuro têm timestamp ajustado.

Mimir remote_write

Métricas são opcionais e ficam desligadas por padrão.

Variável Default Descrição
METRICS_ENABLED false Habilita exportação Prometheus remote_write.
METRICS_REMOTE_WRITE_URL - Endpoint completo do remote_write. Obrigatório quando habilitado.
METRICS_REMOTE_WRITE_TIMEOUT 10s Timeout do cliente remote_write.
METRICS_REMOTE_WRITE_FLUSH_INTERVAL 10s Intervalo de flush das séries agregadas.
METRICS_QUEUE_SIZE 256 Tamanho da fila de observações.
METRICS_ENQUEUE_TIMEOUT 2s Tempo máximo esperando espaço na fila.
METRICS_TENANT_MODE request request, fixed ou none.
METRICS_TENANT_ID - Tenant usado quando METRICS_TENANT_MODE=fixed.
METRICS_LABEL_ALLOWLIST allowlist interna Substitui a allowlist padrão de labels.
METRICS_CUSTOM_MEASUREMENT_RULES - JSON no formato {"tipo":["campo"]} para medições customizadas detalhadas.
METRICS_RETRY_ATTEMPTS 3 Tentativas de envio remote_write.
METRICS_RETRY_BACKOFF 500ms Intervalo entre tentativas.

Auth do remote_write:

Variável Descrição
METRICS_REMOTE_WRITE_AUTH_BEARER_TOKEN Envia Authorization: Bearer ....
METRICS_REMOTE_WRITE_AUTH_USERNAME / METRICS_REMOTE_WRITE_AUTH_PASSWORD Envia Basic Auth.
METRICS_REMOTE_WRITE_AUTH_HEADER_NAME / METRICS_REMOTE_WRITE_AUTH_HEADER_VALUE Envia header customizado. As duas variáveis precisam estar preenchidas juntas.

Tempo OTLP/HTTP

Traces são opcionais e ficam desligados por padrão.

Variável Default Descrição
TRACES_ENABLED false Habilita exportação de traces.resourceSpans.
TRACES_OTLP_HTTP_URL - Endpoint completo OTLP/HTTP. Obrigatório quando habilitado.
TRACES_OTLP_HTTP_TIMEOUT 10s Timeout do cliente OTLP/HTTP.
TRACES_OTLP_HTTP_COMPRESSION none no binário, gzip no script none ou gzip.
TRACES_FLUSH_INTERVAL 2s Intervalo de flush dos batches.
TRACES_QUEUE_SIZE 256 Tamanho da fila de traces.
TRACES_ENQUEUE_TIMEOUT 2s Tempo máximo esperando espaço na fila.
TRACES_BATCH_SIZE 20 Quantidade de resource spans para flush antecipado.
TRACES_TENANT_MODE request request, fixed ou none.
TRACES_TENANT_ID - Tenant usado quando TRACES_TENANT_MODE=fixed.
TRACES_TENANT_HEADER X-Scope-OrgID Header usado para propagar tenant.
TRACES_RETRY_ATTEMPTS 3 Tentativas de envio OTLP/HTTP.
TRACES_RETRY_BACKOFF 500ms Intervalo entre tentativas.

Auth do OTLP/HTTP:

Variável Descrição
TRACES_OTLP_AUTH_BEARER_TOKEN Envia Authorization: Bearer ....
TRACES_OTLP_AUTH_USERNAME / TRACES_OTLP_AUTH_PASSWORD Envia Basic Auth.
TRACES_OTLP_AUTH_HEADER_NAME / TRACES_OTLP_AUTH_HEADER_VALUE Envia header customizado. As duas variáveis precisam estar preenchidas juntas.

Instalação Linux via script

O caminho recomendado para VM, EC2, bare metal e servidores Linux com systemd é o script:

curl -sSL https://raw.githubusercontent.com/elven-observability/scripts/main/linux/collector-fe/install.sh | sudo bash

O instalador:

  • Detecta a distribuição Linux.
  • Baixa o binário de release.
  • Valida as variáveis obrigatórias.
  • Remove /loki/api/v1/push de LOKI_URL se alguém informar a URL antiga.
  • Permite habilitar Mimir e Tempo no modo interativo.
  • Cria /etc/collector-fe-instrumentation/env com permissão 600.
  • Cria e inicia o serviço collector-fe-instrumentation.
  • Opcionalmente instala Caddy para HTTPS automático.

Script com envs: Loki somente

sudo SECRET_KEY="sua-chave-com-pelo-menos-64-caracteres-aqui-coloque-algo-bem-longo" <br>
     LOKI_URL="https://loki.elvenobservability.com" <br>
     LOKI_API_TOKEN="token-fornecido-pela-elven" <br>
     ALLOW_ORIGINS="https://app.meusite.com.br,https://*.meusite.com.br" <br>
     bash <(curl -sSL https://raw.githubusercontent.com/elven-observability/scripts/main/linux/collector-fe/install.sh)

Para produção, prefira pinagem explícita da release do binário:

COLLECTOR_VERSION="<release-tag>"

Script com envs: Loki + Mimir + Tempo

sudo SECRET_KEY="sua-chave-com-pelo-menos-64-caracteres-aqui-coloque-algo-bem-longo" <br>
     LOKI_URL="https://loki.elvenobservability.com" <br>
     LOKI_API_TOKEN="token-fornecido-pela-elven" <br>
     ALLOW_ORIGINS="https://app.meusite.com.br,https://*.meusite.com.br" <br>
     METRICS_ENABLED="true" <br>
     METRICS_REMOTE_WRITE_URL="https://mimir.elvenobservability.com/api/v1/push" <br>
     TRACES_ENABLED="true" <br>
     TRACES_OTLP_HTTP_URL="https://tempo.elvenobservability.com/http/v1/traces" <br>
     bash <(curl -sSL https://raw.githubusercontent.com/elven-observability/scripts/main/linux/collector-fe/install.sh)

Por padrão, quando Mimir/Tempo estão habilitados no script, LOKI_API_TOKEN é reaproveitado como bearer token desses sinks. Para desabilitar esse comportamento:

REUSE_LOKI_TOKEN_FOR_SINK_AUTH=false

Depois configure uma destas opções:

  • METRICS_REMOTE_WRITE_AUTH_BEARER_TOKEN
  • METRICS_REMOTE_WRITE_AUTH_USERNAME / METRICS_REMOTE_WRITE_AUTH_PASSWORD
  • METRICS_REMOTE_WRITE_AUTH_HEADER_NAME / METRICS_REMOTE_WRITE_AUTH_HEADER_VALUE
  • TRACES_OTLP_AUTH_BEARER_TOKEN
  • TRACES_OTLP_AUTH_USERNAME / TRACES_OTLP_AUTH_PASSWORD
  • TRACES_OTLP_AUTH_HEADER_NAME / TRACES_OTLP_AUTH_HEADER_VALUE

Instalação com Caddy

sudo SECRET_KEY="sua-chave-com-pelo-menos-64-caracteres-aqui-coloque-algo-bem-longo" <br>
     LOKI_URL="https://loki.elvenobservability.com" <br>
     LOKI_API_TOKEN="token-fornecido-pela-elven" <br>
     ALLOW_ORIGINS="https://app.meusite.com.br" <br>
     INSTALL_CADDY=true <br>
     CADDY_DOMAIN="collector.meusite.com.br" <br>
     bash <(curl -sSL https://raw.githubusercontent.com/elven-observability/scripts/main/linux/collector-fe/install.sh)

O DNS de CADDY_DOMAIN precisa apontar para a VM antes da instalação.

Arquivos instalados

Item Caminho
Binário /opt/collector-fe-instrumentation/collector-fe-instrumentation
Env /etc/collector-fe-instrumentation/env
Serviço /etc/systemd/system/collector-fe-instrumentation.service

Deploy Docker

Loki somente

Em produção, use uma tag explícita no lugar de <release-tag>.

docker run -d <br>
  --name collector-fe <br>
  --restart unless-stopped <br>
  -p 3000:3000 <br>
  -e SECRET_KEY="sua-chave-com-pelo-menos-64-caracteres-aqui-coloque-algo-bem-longo" <br>
  -e LOKI_URL="https://loki.elvenobservability.com" <br>
  -e LOKI_API_TOKEN="token-fornecido-pela-elven" <br>
  -e ALLOW_ORIGINS="https://app.meusite.com.br,https://*.meusite.com.br" <br>
  -e JWT_ISSUER="trusted-issuer" <br>
  -e JWT_VALIDATE_EXP="false" <br>
  elvenobservability/collector-fe-instrumentation:<release-tag>

Loki + Mimir + Tempo

docker run -d <br>
  --name collector-fe <br>
  --restart unless-stopped <br>
  -p 3000:3000 <br>
  -e SECRET_KEY="sua-chave-com-pelo-menos-64-caracteres-aqui-coloque-algo-bem-longo" <br>
  -e LOKI_URL="https://loki.elvenobservability.com" <br>
  -e LOKI_API_TOKEN="token-fornecido-pela-elven" <br>
  -e ALLOW_ORIGINS="https://app.meusite.com.br,https://*.meusite.com.br" <br>
  -e METRICS_ENABLED="true" <br>
  -e METRICS_REMOTE_WRITE_URL="https://mimir.elvenobservability.com/api/v1/push" <br>
  -e METRICS_REMOTE_WRITE_AUTH_BEARER_TOKEN="token-fornecido-pela-elven" <br>
  -e TRACES_ENABLED="true" <br>
  -e TRACES_OTLP_HTTP_URL="https://tempo.elvenobservability.com/http/v1/traces" <br>
  -e TRACES_OTLP_HTTP_COMPRESSION="gzip" <br>
  -e TRACES_OTLP_AUTH_BEARER_TOKEN="token-fornecido-pela-elven" <br>
  elvenobservability/collector-fe-instrumentation:<release-tag>

Deploy Kubernetes

Exemplo mínimo para clusters Elven ou clusters do cliente.

Secret

apiVersion: v1
kind: Secret
metadata:
  name: collector-fe-env-secret
  namespace: monitoring
type: Opaque
stringData:
  SECRET_KEY: "sua-chave-com-pelo-menos-64-caracteres-aqui-coloque-algo-bem-longo"
  LOKI_URL: "http://loki-gateway.loki.svc.cluster.local"
  LOKI_API_TOKEN: "token-fornecido-pela-elven"
  ALLOW_ORIGINS: "https://app.meusite.com.br,https://*.meusite.com.br"
  JWT_ISSUER: "trusted-issuer"
  JWT_VALIDATE_EXP: "false"

  LOKI_QUEUE_SIZE: "2048"
  LOKI_ENQUEUE_TIMEOUT: "2s"
  LOKI_RETRY_ATTEMPTS: "3"
  LOKI_RETRY_BACKOFF: "500ms"
  LOKI_MAX_EVENT_AGE: "55m"
  LOKI_MAX_FUTURE_SKEW: "10m"

  METRICS_ENABLED: "true"
  METRICS_REMOTE_WRITE_URL: "http://mimir-gateway.mimir.svc.cluster.local:8080/api/v1/push"
  METRICS_REMOTE_WRITE_FLUSH_INTERVAL: "10s"
  METRICS_QUEUE_SIZE: "2048"
  METRICS_ENQUEUE_TIMEOUT: "2s"
  METRICS_TENANT_MODE: "request"
  METRICS_RETRY_ATTEMPTS: "3"
  METRICS_RETRY_BACKOFF: "500ms"

  TRACES_ENABLED: "true"
  TRACES_OTLP_HTTP_URL: "http://tempo-distributor.tempo.svc.cluster.local:4318/v1/traces"
  TRACES_OTLP_HTTP_COMPRESSION: "gzip"
  TRACES_QUEUE_SIZE: "2048"
  TRACES_ENQUEUE_TIMEOUT: "2s"
  TRACES_BATCH_SIZE: "20"
  TRACES_TENANT_MODE: "request"
  TRACES_TENANT_HEADER: "X-Scope-OrgID"
  TRACES_RETRY_ATTEMPTS: "3"
  TRACES_RETRY_BACKOFF: "500ms"

Em cluster interno, prefira endpoints internos de Loki/Mimir/Tempo para evitar autenticação de borda entre componentes do mesmo cluster. Em endpoint público, configure auth bearer, basic ou header customizado conforme fornecido pela Elven.

values.yaml

replicaCount: 2

image:
  registry: docker.io
  repository: elvenobservability/collector-fe-instrumentation
  pullPolicy: IfNotPresent
  tag: "<release-tag>"

envFrom:
  - secretRef:
      name: collector-fe-env-secret

extraEnvs:
  - name: METRICS_REMOTE_WRITE_AUTH_BEARER_TOKEN
    valueFrom:
      secretKeyRef:
        name: collector-fe-env-secret
        key: LOKI_API_TOKEN
  - name: TRACES_OTLP_AUTH_BEARER_TOKEN
    valueFrom:
      secretKeyRef:
        name: collector-fe-env-secret
        key: LOKI_API_TOKEN

containerPorts:
  - name: http
    containerPort: 3000
    protocol: TCP

service:
  type: ClusterIP
  servicePorts:
    - name: http
      port: 3000
      targetPort: 3000

resources:
  requests:
    cpu: 50m
    memory: 64Mi
  limits:
    cpu: 500m
    memory: 256Mi

autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilizationPercentage: 70

Validação:

kubectl -n monitoring rollout status deploy/collector-fe --timeout=180s
kubectl -n monitoring logs deploy/collector-fe --tail=100
kubectl -n monitoring port-forward svc/collector-fe-service 3000:3000
curl http://localhost:3000/health

Configuração do Faro no frontend

Instale as versões da família Faro 2.3.1:

npm install @grafana/faro-web-sdk@2.3.1 @grafana/faro-web-tracing@2.3.1

Inicialização base:

import { initializeFaro, getWebInstrumentations } from '@grafana/faro-web-sdk';
import { TracingInstrumentation } from '@grafana/faro-web-tracing';

export const faro = initializeFaro({
  url: import.meta.env.VITE_FARO_URL,
  app: {
    name: 'minha-app-frontend',
    namespace: 'web',
    version: import.meta.env.VITE_APP_VERSION || '0.0.0',
    release: import.meta.env.VITE_APP_RELEASE || 'local',
    environment: import.meta.env.MODE,
  },
  sessionTracking: {
    enabled: true,
    persistent: true,
    samplingRate: 1,
  },
  instrumentations: [
    ...getWebInstrumentations({
      captureConsole: true,
      captureConsoleDisabledLevels: [],
    }),
    new TracingInstrumentation({
      instrumentationOptions: {
        propagateTraceHeaderCorsUrls: [
          /https:\/\/api\.meusite\.com\.br\/.*/,
        ],
      },
    }),
  ],
});

Env de frontend:

VITE_FARO_URL="https://collector.meusite.com.br/collect/meu-tenant/eyJhbGciOiJIUzI1NiIs..."
VITE_APP_VERSION="1.4.2"
VITE_APP_RELEASE="2026.05.18-1"

Sem @grafana/faro-web-tracing, o collector continua recebendo logs, events, measurements e exceptions, mas não terá traces.resourceSpans para enviar ao Tempo.


Métricas geradas

Quando METRICS_ENABLED=true, o collector exporta histogramas clássicos Prometheus e contadores via remote_write.

Famílias principais:

Métrica Uso
faro_collector_requests_total Requests recebidos pelo collector.
faro_collector_request_duration_seconds Latência do endpoint /collect.
faro_collector_payload_bytes_total Volume recebido.
faro_collector_sink_queue_length Tamanho das filas por sink.
faro_collector_sink_enqueue_failures_total Falhas para aceitar lote em fila.
faro_collector_sink_export_failures_total Falhas de export pós-enqueue.
faro_collector_sink_export_duration_seconds Duração dos exports por sink.
faro_frontend_signals_total Total de sinais normalizados.
faro_frontend_logs_total Logs por app/level.
faro_frontend_exceptions_total Exceções por app/browser.
faro_frontend_web_vital_seconds fcp, inp, lcp, ttfb.
faro_frontend_web_vital_ratio cls.
faro_frontend_web_vital_phase_seconds Campos numéricos de attribution.
faro_frontend_navigation_duration_seconds Duração de navegação.
faro_frontend_navigation_phase_seconds Fases de navegação.
faro_frontend_resource_duration_seconds Duração de recursos.
faro_frontend_resource_transfer_bytes Bytes transferidos por recurso.
faro_frontend_user_actions_total Ações de usuário.
faro_frontend_user_action_duration_seconds Duração de ações.
faro_frontend_http_client_requests_total Requests fetch/XHR derivados de faro.tracing.*.
faro_frontend_http_client_duration_seconds Latência fetch/XHR.
faro_frontend_custom_measurements_total Contagem de custom measurements.
faro_frontend_custom_measurement_value Valores detalhados apenas por regra explícita.

Labels de baixa cardinalidade promovidas por padrão:

tenant, app, namespace, release, environment, browser_name, browser_os, view_name,
vital, rating, navigation_type, initiator_type, cache_hit_status, render_blocking_status,
action_name, importance, trigger, transport, method, status_class, host

Nunca promova como label:

session.id, page.url, trace_id, span_id, user.*, fingerprint, element,
interaction_target, URL completa, IDs arbitrários, atributos livres

Logs estruturados

Loki mantém os dados ricos para investigação. Os campos úteis incluem:

  • trace_id
  • span_id
  • action_name
  • session_id
  • user_id
  • page_url
  • event_name
  • measurement_type
  • atributos originais relevantes

Exemplo de consulta:

{app="minha-app-frontend", environment="production"}

Para investigar erro:

{app="minha-app-frontend", kind="exception"} | json

Traces

Para traces funcionarem, duas coisas precisam estar verdadeiras:

  • Collector com TRACES_ENABLED=true e TRACES_OTLP_HTTP_URL configurado.
  • Frontend usando @grafana/faro-web-tracing@2.3.1 com TracingInstrumentation.

O collector exporta o subobjeto traces como ExportTraceServiceRequest OTLP/HTTP protobuf. O endpoint deve ser completo para evitar ambiguidade:

TRACES_OTLP_HTTP_URL="https://tempo.example.com/v1/traces"

Em alguns ingresses da Elven, o endpoint público HTTP usa prefixo:

TRACES_OTLP_HTTP_URL="https://tempo.elvenobservability.com/http/v1/traces"

Em Kubernetes interno:

TRACES_OTLP_HTTP_URL="http://tempo-distributor.tempo.svc.cluster.local:4318/v1/traces"

Migração de clientes antigos

Use este fluxo para clientes que estavam com versão antiga ou integração "só logfmt".

  1. Subir o collector novo primeiro. Habilite Loki e, se possível, Mimir/Tempo já na primeira janela.
  2. Manter o caminho antigo em paralelo. Não remova o fluxo antigo até validar que o frontend novo está mandando Faro JSON.
  3. Atualizar o frontend. Instale @grafana/faro-web-sdk@2.3.1 e, para traces, @grafana/faro-web-tracing@2.3.1.
  4. Configurar VITE_FARO_URL ou equivalente. A URL deve apontar para /collect/<tenant>/<jwt>.
  5. Canariar por uma aplicação ou ambiente. Comece por staging ou uma porcentagem baixa de tráfego.
  6. Validar os três sinais. Confirme logs no Loki, métricas no Mimir e traces no Tempo.
  7. Comparar com o dashboard antigo por 24h. Verifique se erros, sessões e tráfego batem.
  8. Cortar o caminho antigo. Só depois de confirmar que não há perda de dados relevantes.

Critérios de aceite:

  • POST /collect/:tenant/:token retorna 200.
  • Loki mostra logs/events/exceptions com app e environment corretos.
  • Mimir recebe faro_frontend_signals_total.
  • Web Vitals incluem INP e não dependem de FID.
  • Tempo mostra traces para a app quando tracing está habilitado no frontend.
  • Não há erro de export nos logs do collector.

Health check e validação

Endpoint:

GET /health

Resposta esperada:

{"status":"ok"}

Comandos Linux:

systemctl status collector-fe-instrumentation
journalctl -u collector-fe-instrumentation -f
curl http://localhost:3000/health

Comandos Kubernetes:

kubectl -n monitoring get pods -l app.kubernetes.io/name=collector-fe
kubectl -n monitoring logs deploy/collector-fe --tail=100
kubectl -n monitoring port-forward svc/collector-fe-service 3000:3000
curl http://localhost:3000/health

Consultas úteis:

sum(rate(faro_collector_requests_total[5m]))
sum(rate(faro_frontend_signals_total[5m])) by (app, environment)
{app="minha-app-frontend"} | json

Troubleshooting

Serviço não inicia

Verifique:

journalctl -u collector-fe-instrumentation -n 80 --no-pager

Erros comuns:

Erro Correção
missing required env: SECRET_KEY Defina SECRET_KEY.
SECRET_KEY must be at least 64 characters Use uma chave maior.
missing required env: LOKI_URL Configure a URL base do Loki.
missing required env: LOKI_API_TOKEN Configure o token.
missing required env: ALLOW_ORIGINS Configure CORS.
missing required env: METRICS_REMOTE_WRITE_URL Defina a URL ou desabilite METRICS_ENABLED.
missing required env: TRACES_OTLP_HTTP_URL Defina a URL ou desabilite TRACES_ENABLED.

Faro retorna 401

Verifique:

  • O JWT foi assinado com a mesma SECRET_KEY.
  • O algoritmo é HS256.
  • role é admin ou user.
  • iss é igual a JWT_ISSUER.
  • Se JWT_VALIDATE_EXP=true, o token não expirou.

Faro retorna 503

503 indica que pelo menos um sink habilitado não conseguiu aceitar o lote na fila dentro de *_ENQUEUE_TIMEOUT.

Correções:

  • Aumente LOKI_QUEUE_SIZE, METRICS_QUEUE_SIZE ou TRACES_QUEUE_SIZE.
  • Verifique se o destino está respondendo.
  • Aumente réplicas do collector.
  • Verifique CPU/memória do pod ou da VM.

Logs chegam, mas métricas não

Verifique:

  • METRICS_ENABLED=true.
  • METRICS_REMOTE_WRITE_URL aponta para endpoint completo /api/v1/push.
  • Auth do Mimir está correta.
  • Não há erro em faro_collector_sink_export_failures_total{sink="metrics"}.
  • O payload contém eventos/measurements que geram métricas.

Logs e métricas chegam, mas traces não

Verifique:

  • TRACES_ENABLED=true.
  • TRACES_OTLP_HTTP_URL aponta para endpoint completo OTLP/HTTP.
  • Frontend instalou @grafana/faro-web-tracing.
  • TracingInstrumentation foi registrado.
  • O payload tem traces.resourceSpans.
  • Não há erro em faro_collector_sink_export_failures_total{sink="traces"}.

URL do Loki com path antigo

Use somente a base:

LOKI_URL="https://loki.elvenobservability.com"

O collector adiciona /loki/api/v1/push. Informar o path completo pode gerar URL duplicada.


FAQ

O Collector FE envia traces também?

Sim, quando TRACES_ENABLED=true e o payload contém traces.resourceSpans. O frontend precisa usar @grafana/faro-web-tracing e TracingInstrumentation.

Preciso da env do Mimir?

Sim, se quiser métricas: METRICS_ENABLED=true e METRICS_REMOTE_WRITE_URL. Para Elven Cloud, normalmente o token pode ser o mesmo do Loki em METRICS_REMOTE_WRITE_AUTH_BEARER_TOKEN.

Preciso da env do Tempo?

Sim, se quiser traces: TRACES_ENABLED=true e TRACES_OTLP_HTTP_URL. Para Elven Cloud, normalmente o token pode ser o mesmo do Loki em TRACES_OTLP_AUTH_BEARER_TOKEN.

Posso usar o mesmo tenant para Loki, Mimir e Tempo?

Sim. O default é request, ou seja, o tenant da rota /collect/<tenant>/<jwt> é propagado para os sinks multi-tenant.

O collector aceita clientes antigos?

Aceita payload JSON legado já compatível com o collector anterior. Não trate raw logfmt puro como compatível com o endpoint Faro novo.

Qual é o risco de cardinalidade?

O collector promove somente labels de baixa cardinalidade para métricas. Dados ricos como URL completa, user id, session id, trace id e span id ficam em Loki, não em labels de Mimir.

Como rodo uma atualização segura?

Reexecute o script ou faça rollout Kubernetes com a nova imagem. Valide /health, Loki, Mimir e Tempo antes de remover o caminho antigo.