farol
A lightweight, pure-Dart Real User Monitoring (RUM) client that speaks the
Grafana Faro collector wire format (logs,
events, measurements, exceptions batched to a JSON HTTP endpoint). It works
with any Faro-compatible receiver, including Grafana Cloud's Faro endpoint,
Grafana Alloy's faro.receiver, or the OpenTelemetry Collector
faroreceiver (contrib).
Implements the Grafana Faro wire protocol. Not affiliated with or endorsed by Grafana Labs.
Install
dependencies:
farol: ^0.1.0
Or, to track this repo directly instead of pub.dev:
dependencies:
farol:
git:
url: https://github.com/thegorangers/farol.git
path: packages/farol
Usage
import 'package:farol/farol.dart';
void main() async {
Faro.initialize(FaroConfig(
collectorUrl: Uri.parse('https://faro-collector.example.com/collect'),
app: const FaroApp(name: 'my-app', version: '1.0.0'),
headersProvider: () async => {'x-api-key': await readApiKeyFromStorage()},
));
Faro.instance.pushEvent('app_started');
Faro.instance.pushMeasurement('startup', {'duration_ms': 420});
Faro.instance.setUser(id: 'user-123');
await Faro.shutdown(); // flushes pending signals before process exit
}
Endpoint & auth model
collectorUrl: any Faro-compatible collector endpoint.headersProvider: an optionalFuture<Map<String, String>> Function()called before every batch POST. Use it to attach whatever auth your collector expects (API key, bearer token, signed header) — the client itself has no built-in auth scheme, and reads no credentials from the environment.transport: swap the defaultHttpFaroTransportfor aFakeTransport(tests) or your ownFaroTransportimplementation (e.g. to redirect to a local debug sink).- Delivery is best-effort and fire-and-forget:
send()swallows all transport errors and never surfaces them to the app. A dropped batch does not affect app behavior; RUM data may be lost silently under network failure — this is intentional for telemetry.
No-PII rule
farol never inspects payload contents for personal data — that
responsibility is the caller's. Concretely:
- Don't put raw PII (names, emails, phone numbers, precise geolocation,
free-text user input) into
pushEvent/pushLog/pushMeasurementcontext/attributes, or intosetUser'sattributes. - Use
setUser(id: ...)with an opaque, non-reversible identifier, not an email or username. - Use the
beforeSendhook onFaroConfigto scrub or drop payloads centrally before they leave the device, if you need a last-resort guard.
Design notes
- Sampling:
sampleRateonFaroConfig(default1.0) is applied per session, not per event. - Batching: signals are buffered and flushed on
batchInterval, whenmaxBatchSizeis hit, or explicitly viaFaro.instance.flush()/Faro.shutdown().