Skip to content

Exposure Logging

An exposure is a record that a unit was actually exposed to a given experiment group. The statistics engine reads exposures, not assignments — if you assign a unit but never log an exposure, that unit does not count in the experiment results.

This page describes the two logging modes the SDKs offer, when to pick which, and the common mistakes that dilute results.

The Go and HTTP code paths below are generally available today. The Android, iOS, JavaScript, and C++ snippets are a shape preview — those SDKs are tagged Coming soon under Settings → SDK&Key.

Two modes

ModeBehaviourWhen to use
Automatic (default)The SDK logs an exposure inside getExperiment / getFeatureFlag every time the call returns a group.The call site is the same place the experiment behavior actually runs.
ManualThe SDK returns the assignment without logging. You call LogExperimentExposure later.The call site precomputes assignments long before the user sees the variant (e.g. batch jobs, fan-out APIs, "warm caches" at boot).

Manual mode is what stops exposure dilution — the situation where the platform records that a unit saw a treatment, but the user-visible code path never ran (so the experiment looks bigger than it is and effects look smaller).

How to switch modes

You can switch modes at two granularities:

  • Globally, at SDK init: pass enableAutomaticExposure: false (JavaScript), WithDisableReport(true) (Go), or autoReport(false) (Android).
  • Per call, when the SDK exposes a per-call flag: JavaScript's getExperiment(layer, autoLog) takes a second boolean, Go's WithAutomatic(false) does the same.

The per-call flag has higher priority than the global default.

Manual logging API

go
// Go
exp, _ := userCtx.GetExperiment(ctx, "6666", "abc_layer_name", abc.WithAutomatic(false))
// ... do work that exposes the variant ...
abc.LogExperimentExposure(ctx, "6666", exp)
cpp
// C++
auto exp = abc_cpp_sdk::AbcService::GetInstance()
              ->GetExperiment("6666", "abc_layer_name", abc_cpp_sdk::WithSetUnitID(uid));
// ... later ...
exp.LogExposure();
java
// Android
AbcExpInfo info = mAbcExperiment.getExperiment("abc_layer_name");
boolean ok = mAbcExperiment.LogExperimentExposure(info);
objective-c
// iOS
[abcSdk logExperimentExposure:expInfo];
javascript
// JavaScript
const exp = abc.getExperiment("abc_layer_name", false);  // do not auto-log
abc.logExperimentExposure(exp);                            // log when the variant actually runs

Server SDK delivery

Server SDKs accumulate exposures locally and flush them periodically to the message queue. The default cadence is fast enough for most workloads — you do not need to call a flush method on the hot path. On graceful shutdown, call Release (Go) so the SDK drains the buffer before the process exits.

Client SDK delivery

Client SDKs batch exposures and flush them to the platform on a timer or when the buffer fills. Network failures keep the batch in memory until the next attempt; on app exit some events may be dropped if the OS terminates the process before the next flush — design for at-least-once semantics on the platform side.

Common pitfalls

  • GetExperiments (batch) with auto-log on: the batch API returns assignments for every layer at once. With auto-logging, every layer logs an exposure, which dilutes layers whose variants were never user-visible. Pass WithAutomatic(false) (Go) / WithSetDisableReport(true) (C++) and log selectively.
  • Logging exposures from background warm-up code: a service that pre-fetches assignments at boot for "performance" will fire one exposure per unit before the user has done anything. Use manual mode for warm-up paths.
  • Skipping exposure entirely to "keep results clean": now you have a sample-ratio mismatch — the assignment exists but the engine sees no exposure. Always log when the variant actually runs.