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:
- O browser envia
POST /collect/<tenant>/<jwt-token>. - O collector valida o JWT HS256 usando
SECRET_KEY. - O body é decodificado como payload Faro 2.3.1.
- O payload é normalizado em logs, observações de métricas e traces.
- Os sinks habilitados recebem o lote em filas internas.
- O request retorna
200quando todos os sinks habilitados aceitarem o lote na fila. - Se uma fila obrigatória estiver cheia ou indisponível para enqueue, o request retorna
503. - 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.
FIDnão é premissa da instrumentação atual. A métrica moderna de interação éINP.tracessó chegam quando o frontend instala@grafana/faro-web-tracinge registraTracingInstrumentation.- Custom measurements sem regra explícita geram contagem em
faro_frontend_custom_measurements_total; séries semânticas detalhadas exigemMETRICS_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, prefiraJWT_VALIDATE_EXP=truecom 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/pushdeLOKI_URLse alguém informar a URL antiga. - Permite habilitar Mimir e Tempo no modo interativo.
- Cria
/etc/collector-fe-instrumentation/envcom permissão600. - 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_TOKENMETRICS_REMOTE_WRITE_AUTH_USERNAME/METRICS_REMOTE_WRITE_AUTH_PASSWORDMETRICS_REMOTE_WRITE_AUTH_HEADER_NAME/METRICS_REMOTE_WRITE_AUTH_HEADER_VALUETRACES_OTLP_AUTH_BEARER_TOKENTRACES_OTLP_AUTH_USERNAME/TRACES_OTLP_AUTH_PASSWORDTRACES_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_idspan_idaction_namesession_iduser_idpage_urlevent_namemeasurement_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=trueeTRACES_OTLP_HTTP_URLconfigurado. - Frontend usando
@grafana/faro-web-tracing@2.3.1comTracingInstrumentation.
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".
- Subir o collector novo primeiro. Habilite Loki e, se possível, Mimir/Tempo já na primeira janela.
- Manter o caminho antigo em paralelo. Não remova o fluxo antigo até validar que o frontend novo está mandando Faro JSON.
- Atualizar o frontend. Instale
@grafana/faro-web-sdk@2.3.1e, para traces,@grafana/faro-web-tracing@2.3.1. - Configurar
VITE_FARO_URLou equivalente. A URL deve apontar para/collect/<tenant>/<jwt>. - Canariar por uma aplicação ou ambiente. Comece por staging ou uma porcentagem baixa de tráfego.
- Validar os três sinais. Confirme logs no Loki, métricas no Mimir e traces no Tempo.
- Comparar com o dashboard antigo por 24h. Verifique se erros, sessões e tráfego batem.
- Cortar o caminho antigo. Só depois de confirmar que não há perda de dados relevantes.
Critérios de aceite:
POST /collect/:tenant/:tokenretorna200.- Loki mostra logs/events/exceptions com
appeenvironmentcorretos. - Mimir recebe
faro_frontend_signals_total. - Web Vitals incluem
INPe não dependem deFID. - 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éadminouuser.issé igual aJWT_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_SIZEouTRACES_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_URLaponta 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_URLaponta para endpoint completo OTLP/HTTP.- Frontend instalou
@grafana/faro-web-tracing. TracingInstrumentationfoi 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.