You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(sampling): add head-based session sampling support (#139)
## Description
Add head-based session sampling support to control what percentage of
sessions send telemetry data. The sampling decision is made once at SDK
initialization and remains consistent for the entire session. When a
session is not sampled, all telemetry is silently dropped with zero
processing overhead using the Null Object Pattern.
This aligns with Faro Web SDK sampling behavior.
## Related Issue(s)
Fixes#89
## Type of Change
- [ ] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [x] 🚀 New feature (non-breaking change which adds functionality)
- [ ] 💥 Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [x] 📝 Documentation
- [ ] 📈 Performance improvement
- [x] 🏗️ Code refactoring
- [ ] 🧹 Chore / Housekeeping
## Checklist
- [x] I have made corresponding changes to the documentation
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] I have updated the CHANGELOG.md under the "Unreleased" section
## Screenshots (if applicable)
N/A
## Additional Notes
**Key changes:**
- **FaroConfig**: Added `samplingRate` parameter (0.0 to 1.0, default
1.0)
- **SessionSamplingProvider**: Handles one-time sampling decision at
init
- **RandomValueProvider**: Injectable randomness for testable sampling
logic
- **NoOpBatchTransport**: Null object pattern for unsampled sessions
(zero overhead)
- **BatchTransport**: Refactored with private fields, fixed timer
cancellation bug
**Testing:**
- Unit tests for SessionSamplingProvider, RandomValueProvider,
FaroConfig
- Unit tests for BatchTransport and NoOpBatchTransport
- Integration tests verifying end-to-end sampling behavior
- Manual testing with example app at different samplingRate values
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes core telemetry initialization and transport selection, so
mis-sampling or factory singleton behavior could unintentionally drop
data across a session. Coverage is strong, but the new no-op path and
init-time decision affect all telemetry types.
>
> **Overview**
> Adds head-based **session sampling** via new `FaroConfig.samplingRate`
(0.0–1.0, default 1.0), making a one-time sampling decision at SDK init
and routing all telemetry through a `NoOpBatchTransport` when unsampled
(plus a debug log explaining dropped telemetry).
>
> Refactors `BatchTransport` internals (private fields/timer handling)
and updates `BatchTransportFactory` to select real vs no-op transport
based on `isSampled`, backed by new `SessionSamplingProvider` and
injectable `RandomValueProvider` for deterministic testing; updates
docs/CHANGELOG and adds unit + integration test coverage for sampling
and transport behavior.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
9e1f88a. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+7Lines changed: 7 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,6 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
10
10
### Added
11
11
12
+
-**Session sampling support**: New `samplingRate` configuration option allows controlling what percentage of sessions send telemetry data. This enables cost management and traffic reduction for high-volume applications. (Resolves #89)
13
+
14
+
-`samplingRate` accepts a value from `0.0` (no sessions sampled) to `1.0` (all sessions sampled, default)
15
+
- Sampling decision is made once per session at initialization and applies to all telemetry types (events, logs, exceptions, measurements, traces)
16
+
- A debug log is emitted when a session is not sampled (visible in debug builds only)
17
+
- Aligns with [Faro Web SDK sampling behavior](https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/sampling/)
18
+
12
19
-**ContextScope for span context lifetime control**: New `contextScope` parameter on `startSpan()` controls how long a span remains active in zone context for auto-assignment. `ContextScope.callback` (default) deactivates the span when the callback completes, preventing timer/stream callbacks from inheriting it. `ContextScope.zone` keeps the span active for the entire zone lifetime, useful when you want timer callbacks to be children of the parent span. (Resolves #105)
13
20
14
21
-**Span.noParent sentinel**: New `Span.noParent` static constant allows explicitly starting a span with no parent, ignoring the active span in zone context. Useful for timer callbacks or event-driven scenarios where you want to start a fresh, independent trace. (Resolves #105)
|`1.0`| All sessions sampled (default - send all data) |
529
+
|`0.5`| 50% of sessions sampled |
530
+
|`0.1`| 10% of sessions sampled |
531
+
|`0.0`| No sessions sampled (no data sent) |
532
+
533
+
**Notes:**
534
+
535
+
- Sampling is head-based: the decision is made at SDK initialization and remains consistent for the entire session
536
+
- This aligns with [Faro Web SDK sampling behavior](https://grafana.com/docs/grafana-cloud/monitor-applications/frontend-observability/instrument/sampling/)
537
+
502
538
### Data Collection Control
503
539
504
540
Faro provides the ability to enable or disable data collection at runtime. This setting is automatically persisted across app restarts, so you don't need to set it every time your app starts.
0 commit comments