1. Querying Loki #
Faro signals are structured logs.
LogQL queries #
Each Faro signal arrives in Loki as a logfmt line, with a kind field (measurement, exception, log, event) and metadata (application, version, page, browser, session).
# Erreurs JavaScript par message
sum by (value) (count_over_time({app_name="boutique"} | logfmt | kind="exception" [1h]))
# LCP au 75e percentile par page
quantile_over_time(0.75, {app_name="boutique"} | logfmt | kind="measurement" | lcp != "" | unwrap lcp [15m]) by (page_url)
# Événements « ajout_panier » par version
sum by (app_version) (count_over_time({app_name="boutique"} | logfmt | kind="event" | event_name="ajout_panier" [1h]))Good to know: The exact label and field names depend on the Alloy version and your configuration: open a line in Explore to check them before writing a dashboard.
2. Dashboards and alerts #
Tracking the user experience.
A front-end dashboard #
| Panel | Content |
|---|---|
| Web Vitals p75 | LCP, INP, CLS per page and per version |
| Errors | Number of exceptions, top 10 messages, affected browsers |
| Volume | Sessions, page views |
| Slow requests | fetch traces > 1 s |
| Conversion | Business event funnel |
Alerts #
# Règle Loki (ruler) ou Grafana : pic d'erreurs JavaScript après un déploiement
sum(count_over_time({app_name="boutique"} | logfmt | kind="exception" [10m])) > 50Good practice: Always compare by app_version: an error increase limited to the latest version immediately points to the faulty deployment.3. GDPR and privacy #
Measuring without tracking.
Rules #
| Principle | Implementation |
|---|---|
| Consent | If collection is not strictly necessary, initialize Faro only after consent (consent banner) |
| Minimization | No email, name or form content; URLs cleaned of sensitive parameters |
| Pseudonymization | Technical user identifier, never a direct one |
| Sampling | samplingRate to collect only part of the sessions |
| Retention | Short Loki retention for this data (30 days for example) |
initializeFaro({
// ...
beforeSend: (item) => {
// retirer les paramètres d'URL des métadonnées de page
if (item.meta?.page?.url) item.meta.page.url = item.meta.page.url.split('?')[0];
return item;
},
ignoreErrors: [/ResizeObserver loop/, /Script error\./],
});