Skip to content

feat(observability): frontend Faro RUM + worker→Loki log shipping (ships dark)#86

Open
xBalbinus wants to merge 6 commits into
mainfrom
feat/faro-frontend-observability
Open

feat(observability): frontend Faro RUM + worker→Loki log shipping (ships dark)#86
xBalbinus wants to merge 6 commits into
mainfrom
feat/faro-frontend-observability

Conversation

@xBalbinus

@xBalbinus xBalbinus commented Jul 6, 2026

Copy link
Copy Markdown

Observability rails: frontend Faro RUM + worker→Loki logs — both shipped dark

Two halves of the same story — closing the frontend fog-of-war and making backend log lines Grafana-visible — combined per review-flow preference. Each half is a hard no-op until its env var is set, so this merges safely with zero behavior change.


Part 1 — Grafana Faro frontend observability (packages/client)

RUM + product events, shipped dark (hard no-op unless VITE_FARO_URL is set; dark builds contain zero Faro code via dynamic import, verified by grepping emitted chunks). Implements slice 5 of Carey's observability roadmap (#24): the "Grafana Cloud equivalent of Sentry/PostHog".

Captured when enabled

  • Web vitals, uncaught errors/unhandled rejections, React error-boundary catches, session tracking (console capture off)
  • Route transitions (TanStack onResolved, pathname only)
  • Product events via trackEvent: prompt_submitted (counts/ids only), session_created, workflow_run, approval_resolved, api_error (status/code/query-stripped path), ws_connected/disconnected/error
  • Backend correlation: TracingInstrumentation propagates traceparent only to the API origin (anchored-regex match; worker CORS now allows traceparent/tracestate)

Privacy: beforeSend deep-scrubs every beacon — query strings stripped from all URLs (incl. stack frames + fetch spans), glc_/gh*_/Bearer/api-token shapes redacted, identity is the opaque user id only (synced from the auth store, cleared on logout). No prompts, no message content, no emails.

Part 2 — Worker/DO log shipping to Loki (packages/worker)

Today log.warn/log.info lines are visible only in wrangler tail (verified 2026-07-06: no valet service in Loki, so #70/#82's integration auth/refresh failed warns are invisible in Grafana). Dark by default.

  • lib/log.ts emit() additionally buffers each line (bounded 500; oldest dropped + drop-count reported as a synthetic warn on next flush) when LOKI_PUSH_URL is configured; unconfigured cost = one boolean check, console output byte-identical (tail unaffected)
  • flushLogs() POSTs Loki push payloads (service_name + level labels, ns timestamps, line = the exact JSON incl. trace_id/span_id → trace↔log correlation)
  • Flush drivers: Hono middleware (waitUntil after response), the DOs' existing trace-flush drivers, and the scheduled handler
  • Failed pushes drop the batch (counted), mirroring the fire-and-forget OTLP trace exporter

Evidence

  • Client: 189/189 tests (15 new), incl. a real-SDK beacon test (local HTTP collector receives the actual Faro POST with ?code=oauth-secret stripped and glc_ token [REDACTED]); dark build has zero Faro symbols in dist; enabled build emits Faro chunks cleanly
  • Worker: 95 files / 1245 tests green (2 new for drop accounting); live e2e against local grafana/otel-lgtm — real worker under wrangler dev/health traffic → Loki query_range returns the lines with trace_id present
  • Root + client typecheck green re-verified on the combined branch (full worker + client suites re-run post-merge)

To turn it on (independent switches)

  1. Faro: enable Frontend Observability on the dev-valet Grafana Cloud stack → set FARO_URL in the deploy env (plumbed through build_client as VITE_FARO_URL)
  2. Loki: needs a logs:write token + the stack's Loki instance ID from Paul (not the traces token — 410780 is the Tempo push instance) → set LOKI_PUSH_URL + LOKI_BASIC_AUTH as worker secrets

Until then every deploy stays dark. Docs: docs/observability.md §§ Frontend observability / Log shipping to Loki.

Absorbed #87 (feat/worker-loki-logs) as a signed merge — full history preserved.

xBalbinus added 3 commits July 6, 2026 16:32
RUM + product events for the client (Carey's frontend-observability spec,
slice 5): web vitals, uncaught errors/unhandled rejections, React
error-boundary catches, TanStack route transitions, sanitized API
failures, WS lifecycle, and a small set of product events
(prompt_submitted, session_created, workflow_run, approval_resolved).

Hard no-op unless VITE_FARO_URL is set at build time — mirrors the
worker's OTLP-endpoint gate, and the SDK is dynamically imported so dark
builds contain zero Faro code (verified against the emitted chunks).

Privacy: a beforeSend hook strips query strings from every URL in every
beacon and redacts token-shaped values (glc_/gh*_/Bearer/api-token);
identity is the opaque user id only (set on login, reset on logout);
event attributes are ids/enums/counts, never free text; console capture
is off. Verified end-to-end by a real-SDK beacon test (jsdom + local
HTTP collector).

traceparent/tracestate are propagated only to the API origin (derived
from VITE_API_URL) and now allowed by the worker's CORS preflight so
frontend spans correlate with worker traces in Tempo.
Structured log lines from lib/log.ts were console-only — visible in
wrangler tail but absent from Grafana, so trace<->log pivoting on
trace_id never worked in Loki. Buffer each emitted line (bounded at 500,
oldest dropped and reported as dropped_total on the next flush) and push
batches to {LOKI_PUSH_URL}/loki/api/v1/push labeled {service_name,
level}, keeping console output byte-identical.

Workers have no cross-request timers, so flushing rides existing
lifecycle hooks via waitUntil: a Hono middleware on the fetch path, the
end of the scheduled tick, and the DOs' flushTraces drivers
(alarm/close/hibernate) — no new flush sites. Unset LOKI_PUSH_URL costs
one boolean per line and zero network; push failures are swallowed
(single console.warn, batch counted dropped) like the trace exporter.
…nting

Review fixes on the Loki log-shipping branch:
- log.test.ts cast '[]' -> tuple via unknown (TS2352 failed 'tsc --build',
  the CI typecheck gate, even though vitest passed)
- flushLogs now ships a pending drop report even when the buffer is empty,
  so drops from a failed final push are visible in Loki, not just console
- a failed push no longer re-counts the synthetic report line in
  dropped_total, and drops whose report line was lost are re-reported
- docs: drop the verbatim Tempo push instance ID; the warning reads the
  same without committing an internal account identifier
@socket-security

socket-security Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​grafana/​faro-web-tracing@​2.8.2991007597100
Addednpm/​jsdom@​29.1.18110010090100
Addednpm/​@​grafana/​faro-web-sdk@​2.8.2991009996100

View full report

@socket-security

socket-security Bot commented Jul 6, 2026

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm css-tree is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/jsdom@29.1.1npm/css-tree@3.2.1

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/css-tree@3.2.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm data-urls is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/jsdom@29.1.1npm/data-urls@7.0.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/data-urls@7.0.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm jsdom is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: packages/client/package.jsonnpm/jsdom@29.1.1

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/jsdom@29.1.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm jsdom is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: packages/client/package.jsonnpm/jsdom@29.1.1

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/jsdom@29.1.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm web-vitals is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/@grafana/faro-web-sdk@2.8.2npm/web-vitals@5.3.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/web-vitals@5.3.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Preview deployment: https://pr-86.dev-valet-turnkey-client.pages.dev

@xBalbinus xBalbinus changed the title feat(client): Grafana Faro frontend observability (RUM + product events), shipped dark feat(observability): frontend Faro RUM + worker→Loki log shipping (ships dark) Jul 6, 2026
@xBalbinus xBalbinus marked this pull request as ready for review July 8, 2026 17:49
@xBalbinus xBalbinus requested a review from a team July 8, 2026 17:49
# Conflicts:
#	packages/client/package.json
#	pnpm-lock.yaml
…in ids

- route_change now carries the from pathname (pathname only) so route
  funnels get edges, not just nodes
- ws_disconnected carries the close code and wasClean flag;
  ws_connected carries the reconnect attempt count that got the
  connection up
- approval_resolved carries valet.session.id and valet.invocation.id
  so approval behavior joins to server-side action_invocations

All attributes stay ids/enums/numbers — no free text.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant