1use crate::evaluation::cmab_evaluator::{get_cmab_ranked_list, CMABRankedGroup};
2use crate::evaluation::country_lookup::CountryLookup;
3use crate::evaluation::dynamic_value::DynamicValue;
4use crate::evaluation::evaluation_details::EvaluationDetails;
5use crate::evaluation::evaluation_types::GateEvaluation;
6use crate::evaluation::evaluator::{Evaluator, SpecType};
7use crate::evaluation::evaluator_context::EvaluatorContext;
8use crate::evaluation::evaluator_result::{
9 result_to_dynamic_config_eval, result_to_experiment_eval, result_to_gate_eval,
10 result_to_layer_eval, EvaluatorResult,
11};
12use crate::evaluation::ua_parser::UserAgentParser;
13use crate::event_logging::event_logger::{EventLogger, ExposureTrigger};
14use crate::event_logging::event_queue::queued_config_expo::EnqueueConfigExpoOp;
15use crate::event_logging::event_queue::queued_experiment_expo::EnqueueExperimentExpoOp;
16use crate::event_logging::event_queue::queued_gate_expo::EnqueueGateExpoOp;
17use crate::event_logging::event_queue::queued_layer_param_expo::EnqueueLayerParamExpoOp;
18use crate::event_logging::event_queue::queued_passthrough::EnqueuePassthroughOp;
19use crate::event_logging::statsig_event_internal::StatsigEventInternal;
20use crate::event_logging_adapter::EventLoggingAdapter;
21use crate::event_logging_adapter::StatsigHttpEventLoggingAdapter;
22use crate::gcir::gcir_formatter::GCIRFormatter;
23use crate::hashing::HashUtil;
24use crate::initialize_response::InitializeResponse;
25use crate::networking::NetworkError;
26use crate::observability::diagnostics_observer::DiagnosticsObserver;
27use crate::observability::observability_client_adapter::{MetricType, ObservabilityEvent};
28use crate::observability::ops_stats::{OpsStatsForInstance, OPS_STATS};
29use crate::observability::sdk_errors_observer::{ErrorBoundaryEvent, SDKErrorsObserver};
30use crate::output_logger::{initialize_output_logger, shutdown_output_logger};
31use crate::persistent_storage::persistent_values_manager::PersistentValuesManager;
32use crate::sdk_diagnostics::diagnostics::{ContextType, Diagnostics};
33use crate::sdk_diagnostics::marker::{ActionType, KeyType, Marker};
34use crate::spec_store::SpecStore;
35use crate::specs_adapter::{StatsigCustomizedSpecsAdapter, StatsigHttpSpecsAdapter};
36use crate::statsig_err::StatsigErr;
37use crate::statsig_metadata::StatsigMetadata;
38use crate::statsig_options::StatsigOptions;
39use crate::statsig_runtime::StatsigRuntime;
40use crate::statsig_type_factories::{
41 make_dynamic_config, make_experiment, make_feature_gate, make_layer,
42};
43use crate::statsig_types::{DynamicConfig, Experiment, FeatureGate, Layer, ParameterStore};
44use crate::user::StatsigUserInternal;
45use crate::{
46 dyn_value, log_d, log_e, log_w, read_lock_or_else, ClientInitResponseOptions,
47 GCIRResponseFormat, IdListsAdapter, ObservabilityClient, OpsStatsEventObserver,
48 OverrideAdapter, SpecsAdapter, SpecsInfo, SpecsSource, SpecsUpdateListener,
49 StatsigHttpIdListsAdapter, StatsigLocalOverrideAdapter, StatsigUser,
50};
51use crate::{
52 log_error_to_statsig_and_console,
53 statsig_core_api_options::{
54 DynamicConfigEvaluationOptions, ExperimentEvaluationOptions, FeatureGateEvaluationOptions,
55 LayerEvaluationOptions, ParameterStoreEvaluationOptions,
56 },
57};
58use serde::de::DeserializeOwned;
59use serde::Serialize;
60use serde_json::json;
61use serde_json::Value;
62use std::borrow::Cow;
63use std::collections::HashMap;
64use std::sync::atomic::{AtomicBool, Ordering};
65use std::sync::Mutex;
66use std::sync::{Arc, Weak};
67use std::time::{Duration, Instant};
68use tokio::time::sleep;
69use tokio::try_join;
70
71const TAG: &str = stringify!(Statsig);
72const ERROR_SDK_KEY: &str = "__STATSIG_ERROR_SDK_KEY__";
73const INIT_IP_TAG: &str = "INIT_COUNTRY_LOOKUP";
74const INIT_UA_TAG: &str = "INIT_UA";
75
76lazy_static::lazy_static! {
77 static ref SHARED_INSTANCE: Mutex<Option<Arc<Statsig>>> = Mutex::new(None);
78}
79
80pub struct Statsig {
81 pub statsig_runtime: Arc<StatsigRuntime>,
82 pub options: Arc<StatsigOptions>,
83
84 sdk_key: String,
85 event_logger: Arc<EventLogger>,
86 specs_adapter: Arc<dyn SpecsAdapter>,
87 event_logging_adapter: Arc<dyn EventLoggingAdapter>,
88 id_lists_adapter: Option<Arc<dyn IdListsAdapter>>,
89 override_adapter: Option<Arc<dyn OverrideAdapter>>,
90 spec_store: Arc<SpecStore>,
91 hashing: Arc<HashUtil>,
92 gcir_formatter: Arc<GCIRFormatter>,
93 statsig_environment: Option<HashMap<String, DynamicValue>>,
94 fallback_environment: Mutex<Option<HashMap<String, DynamicValue>>>,
95 ops_stats: Arc<OpsStatsForInstance>,
96 error_observer: Arc<dyn OpsStatsEventObserver>,
97 diagnostics_observer: Arc<dyn OpsStatsEventObserver>,
98 background_tasks_started: Arc<AtomicBool>,
99 persistent_values_manager: Option<Arc<PersistentValuesManager>>,
100 initialize_details: Mutex<InitializeDetails>,
101}
102
103pub struct StatsigContext {
104 pub sdk_key: String,
105 pub options: Arc<StatsigOptions>,
106 pub local_override_adapter: Option<Arc<dyn OverrideAdapter>>,
107 pub error_observer: Arc<dyn OpsStatsEventObserver>,
108 pub diagnostics_observer: Arc<dyn OpsStatsEventObserver>,
109 pub spec_store: Arc<SpecStore>,
110}
111
112#[derive(Debug, Clone, Serialize)]
113pub struct FailureDetails {
114 pub reason: String,
115 pub error: Option<StatsigErr>,
116}
117
118#[derive(Debug, Clone, Serialize)]
119pub struct InitializeDetails {
120 pub duration: f64,
121 pub init_success: bool,
122 pub is_config_spec_ready: bool,
123 pub is_id_list_ready: Option<bool>,
124 pub source: SpecsSource,
125 pub failure_details: Option<FailureDetails>,
126 pub spec_source_api: Option<String>,
127}
128
129impl Default for InitializeDetails {
130 fn default() -> Self {
131 InitializeDetails {
132 duration: 0.0,
133 init_success: false,
134 is_config_spec_ready: false,
135 is_id_list_ready: None,
136 source: SpecsSource::Uninitialized,
137 failure_details: None,
138 spec_source_api: None,
139 }
140 }
141}
142
143impl InitializeDetails {
144 pub fn from_error(reason: &str, error: Option<StatsigErr>) -> Self {
145 InitializeDetails {
146 duration: 0.0,
147 init_success: false,
148 is_config_spec_ready: false,
149 is_id_list_ready: None,
150 source: SpecsSource::Uninitialized,
151 failure_details: Some(FailureDetails {
152 reason: reason.to_string(),
153 error,
154 }),
155 spec_source_api: None,
156 }
157 }
158}
159
160impl Statsig {
161 pub fn new(sdk_key: &str, options: Option<Arc<StatsigOptions>>) -> Self {
162 let statsig_runtime = StatsigRuntime::get_runtime();
163 let options = options.map(|o| o.validate_and_fix()).unwrap_or_default();
164
165 initialize_output_logger(
166 &options.output_log_level,
167 options.output_logger_provider.clone(),
168 );
169
170 let hashing = Arc::new(HashUtil::new());
171
172 let specs_adapter = initialize_specs_adapter(sdk_key, &options, &hashing);
173 let id_lists_adapter = initialize_id_lists_adapter(sdk_key, &options);
174 let event_logging_adapter = initialize_event_logging_adapter(sdk_key, &options);
175 let override_adapter = match options.override_adapter.as_ref() {
176 Some(adapter) => Some(Arc::clone(adapter)),
177 None => Some(Arc::new(StatsigLocalOverrideAdapter::new()) as Arc<dyn OverrideAdapter>),
178 };
179
180 let event_logger =
181 EventLogger::new(sdk_key, &options, &event_logging_adapter, &statsig_runtime);
182
183 let diagnostics = Arc::new(Diagnostics::new(event_logger.clone(), sdk_key));
184 let diagnostics_observer: Arc<dyn OpsStatsEventObserver> =
185 Arc::new(DiagnosticsObserver::new(diagnostics));
186 let error_observer: Arc<dyn OpsStatsEventObserver> =
187 Arc::new(SDKErrorsObserver::new(sdk_key, &options));
188
189 let ops_stats = setup_ops_stats(
190 sdk_key,
191 statsig_runtime.clone(),
192 &error_observer,
193 &diagnostics_observer,
194 &options.observability_client,
195 );
196
197 let spec_store = Arc::new(SpecStore::new(
198 sdk_key,
199 hashing.sha256(sdk_key),
200 statsig_runtime.clone(),
201 options.data_store.clone(),
202 ));
203
204 let environment = options
205 .environment
206 .as_ref()
207 .map(|env| HashMap::from([("tier".into(), dyn_value!(env.as_str()))]));
208
209 let persistent_values_manager = options.persistent_storage.clone().map(|storage| {
210 Arc::new(PersistentValuesManager {
211 persistent_storage: storage,
212 })
213 });
214
215 StatsigMetadata::update_service_name(options.service_name.clone());
216
217 Statsig {
218 sdk_key: sdk_key.to_string(),
219 options,
220 gcir_formatter: Arc::new(GCIRFormatter::new(
221 &spec_store,
222 &override_adapter,
223 &ops_stats,
224 )),
225 hashing,
226 statsig_environment: environment,
227 fallback_environment: Mutex::new(None),
228 override_adapter,
229 spec_store,
230 specs_adapter,
231 event_logging_adapter,
232 event_logger,
233 id_lists_adapter,
234 statsig_runtime,
235 ops_stats,
236 error_observer,
237 diagnostics_observer,
238 background_tasks_started: Arc::new(AtomicBool::new(false)),
239 persistent_values_manager,
240 initialize_details: Mutex::new(InitializeDetails::default()),
241 }
242 }
243
244 pub async fn initialize(&self) -> Result<(), StatsigErr> {
259 let details = self.initialize_with_details().await?;
260
261 if let Some(failure_details) = details.failure_details {
262 Err(failure_details
263 .error
264 .unwrap_or(StatsigErr::InitializationError(failure_details.reason)))
265 } else {
266 Ok(())
267 }
268 }
269
270 pub async fn initialize_with_details(&self) -> Result<InitializeDetails, StatsigErr> {
279 self.ops_stats.add_marker(
280 Marker::new(KeyType::Overall, ActionType::Start, None),
281 Some(ContextType::Initialize),
282 );
283
284 let init_details = if let Some(timeout_ms) = self.options.init_timeout_ms {
285 self.apply_timeout_to_init(timeout_ms).await
286 } else {
287 self.initialize_impl_with_details().await
288 };
289 self.log_init_details(&init_details);
290 if let Ok(details) = &init_details {
291 if let Ok(mut curr_init_details) = self.initialize_details.try_lock() {
292 *curr_init_details = details.clone();
293 }
294 }
295 init_details
296 }
297
298 pub fn get_initialize_details(&self) -> InitializeDetails {
299 match self.initialize_details.lock() {
300 Ok(details) => details.clone(),
301 Err(poison_error) => InitializeDetails::from_error(
302 "Failed to lock initialize_details",
303 Some(StatsigErr::LockFailure(poison_error.to_string())),
304 ),
305 }
306 }
307
308 pub fn is_initialized(&self) -> bool {
309 match self.initialize_details.lock() {
310 Ok(details) => details.init_success,
311 Err(_) => false,
312 }
313 }
314
315 pub async fn shutdown(&self) -> Result<(), StatsigErr> {
316 self.shutdown_with_timeout(Duration::from_secs(3)).await
317 }
318
319 pub async fn shutdown_with_timeout(&self, timeout: Duration) -> Result<(), StatsigErr> {
320 log_d!(
321 TAG,
322 "Shutting down Statsig with timeout {}ms",
323 timeout.as_millis()
324 );
325
326 let start = Instant::now();
327 let shutdown_result = tokio::select! {
328 () = tokio::time::sleep(timeout) => {
329 log_w!(TAG, "Statsig shutdown timed out. {}", start.elapsed().as_millis());
330 Err(StatsigErr::ShutdownFailure(
331 "Shutdown timed out".to_string()
332 ))
333 }
334 sub_result = async {
335 let id_list_shutdown: Pin<Box<_>> = if let Some(adapter) = &self.id_lists_adapter {
336 adapter.shutdown(timeout)
337 } else {
338 Box::pin(async { Ok(()) })
339 };
340
341 shutdown_output_logger();
342
343 try_join!(
344 id_list_shutdown,
345 self.event_logger.shutdown(),
346 self.specs_adapter.shutdown(timeout, &self.statsig_runtime),
347 )
348 } => {
349 match sub_result {
350 Ok(_) => {
351 log_d!(TAG, "All Statsig tasks shutdown successfully");
352 Ok(())
353 }
354 Err(e) => {
355 log_w!(TAG, "Error during shutdown: {:?}", e);
356 Err(e)
357 }
358 }
359 }
360 };
361
362 self.statsig_runtime.shutdown();
363 shutdown_result
364 }
365
366 async fn start_background_tasks(
367 statsig_runtime: Arc<StatsigRuntime>,
368 id_lists_adapter: Option<Arc<dyn IdListsAdapter>>,
369 specs_adapter: Arc<dyn SpecsAdapter>,
370 ops_stats: Arc<OpsStatsForInstance>,
371 bg_tasks_started: Arc<AtomicBool>,
372 ) -> bool {
373 if bg_tasks_started.load(Ordering::SeqCst) {
374 return true;
375 }
376
377 let mut success = true;
378
379 if let Some(adapter) = &id_lists_adapter {
380 if let Err(e) = adapter
381 .clone()
382 .schedule_background_sync(&statsig_runtime)
383 .await
384 {
385 success = false;
386 log_w!(TAG, "Failed to schedule idlist background job {}", e);
387 }
388 }
389
390 if let Err(e) = specs_adapter
391 .clone()
392 .schedule_background_sync(&statsig_runtime)
393 .await
394 {
395 success = false;
396 log_error_to_statsig_and_console!(
397 ops_stats,
398 TAG,
399 StatsigErr::SpecsAdapterSkipPoll(format!(
400 "Failed to schedule specs adapter background job: {}",
401 e
402 ))
403 );
404 }
405
406 bg_tasks_started.store(true, Ordering::SeqCst);
407
408 success
409 }
410
411 async fn apply_timeout_to_init(
412 &self,
413 timeout_ms: u64,
414 ) -> Result<InitializeDetails, StatsigErr> {
415 let timeout = Duration::from_millis(timeout_ms);
416
417 let init_future = self.initialize_impl_with_details();
418 let timeout_future = sleep(timeout);
419
420 let statsig_runtime = self.statsig_runtime.clone();
421 let id_lists_adapter = self.id_lists_adapter.clone();
422 let specs_adapter = self.specs_adapter.clone();
423 let ops_stats = self.ops_stats.clone();
424 let background_tasks_started = self.background_tasks_started.clone();
425 let statsig_runtime_for_closure = statsig_runtime.clone();
427
428 tokio::select! {
429 result = init_future => {
430 result
431 },
432 _ = timeout_future => {
433 statsig_runtime.spawn(
434 "start_background_tasks",
435 |_shutdown_notify| async move {
436 Self::start_background_tasks(
437 statsig_runtime_for_closure,
438 id_lists_adapter,
439 specs_adapter,
440 ops_stats,
441 background_tasks_started,
442 ).await;
443 }
444 );
445 Ok(self.timeout_failure(timeout_ms))
446 },
447 }
448 }
449
450 async fn initialize_impl_with_details(&self) -> Result<InitializeDetails, StatsigErr> {
451 let start_time = Instant::now();
452 self.spec_store.set_source(SpecsSource::Loading);
453 self.specs_adapter.initialize(self.spec_store.clone());
454
455 let mut error_message = None;
456 let mut id_list_ready = None;
457
458 let init_country_lookup = if !self.options.disable_country_lookup.unwrap_or_default() {
459 Some(self.statsig_runtime.spawn(INIT_IP_TAG, |_| async {
460 CountryLookup::load_country_lookup();
461 }))
462 } else {
463 None
464 };
465
466 let init_ua = if !self.options.disable_user_agent_parsing.unwrap_or_default() {
467 Some(self.statsig_runtime.spawn(INIT_UA_TAG, |_| async {
468 UserAgentParser::load_parser();
469 }))
470 } else {
471 None
472 };
473
474 let init_res = match self
475 .specs_adapter
476 .clone()
477 .start(&self.statsig_runtime)
478 .await
479 {
480 Ok(()) => Ok(()),
481 Err(e) => {
482 self.spec_store.set_source(SpecsSource::NoValues);
483 error_message = Some(format!("Failed to start specs adapter: {e}"));
484 Err(e)
485 }
486 };
487
488 if let Some(adapter) = &self.id_lists_adapter {
489 match adapter
490 .clone()
491 .start(&self.statsig_runtime, self.spec_store.clone())
492 .await
493 {
494 Ok(()) => {
495 id_list_ready = Some(true);
496 }
497 Err(e) => {
498 id_list_ready = Some(false);
499 error_message.get_or_insert_with(|| format!("Failed to sync ID lists: {e}"));
500 }
501 }
502 if let Err(e) = adapter
503 .clone()
504 .schedule_background_sync(&self.statsig_runtime)
505 .await
506 {
507 log_w!(TAG, "Failed to schedule id_list background job {}", e);
508 }
509 }
510
511 if let Err(e) = self
512 .event_logging_adapter
513 .clone()
514 .start(&self.statsig_runtime)
515 .await
516 {
517 log_error_to_statsig_and_console!(
518 self.ops_stats.clone(),
519 TAG,
520 StatsigErr::UnstartedAdapter(format!(
521 "Failed to start event logging adapter: {}",
522 e
523 ))
524 );
525 }
526
527 let spec_info = self.spec_store.get_current_specs_info();
528 let duration = start_time.elapsed().as_millis() as f64;
529
530 self.set_default_environment_from_server();
531
532 if self.options.wait_for_country_lookup_init.unwrap_or(false) {
533 if let Some(task_id) = init_country_lookup {
534 let _ = self
535 .statsig_runtime
536 .await_join_handle(INIT_IP_TAG, &task_id)
537 .await;
538 }
539 }
540 if self.options.wait_for_user_agent_init.unwrap_or(false) {
541 if let Some(task_id) = init_ua {
542 let _ = self
543 .statsig_runtime
544 .await_join_handle(INIT_UA_TAG, &task_id)
545 .await;
546 };
547 }
548
549 let error = init_res.clone().err();
550
551 let success = Self::start_background_tasks(
552 self.statsig_runtime.clone(),
553 self.id_lists_adapter.clone(),
554 self.specs_adapter.clone(),
555 self.ops_stats.clone(),
556 self.background_tasks_started.clone(),
557 )
558 .await;
559
560 Ok(self.construct_initialize_details(success, duration, spec_info, id_list_ready, error))
561 }
562
563 fn construct_initialize_details(
564 &self,
565 init_success: bool,
566 duration: f64,
567 specs_info: SpecsInfo,
568 is_id_list_ready: Option<bool>,
569 error: Option<StatsigErr>,
570 ) -> InitializeDetails {
571 let is_config_spec_ready = matches!(specs_info.lcut, Some(v) if v != 0);
572
573 let failure_details =
574 if let Some(StatsigErr::NetworkError(NetworkError::DisableNetworkOn(_))) = error {
575 None
576 } else {
577 error.as_ref().map(|e| FailureDetails {
578 reason: e.to_string(),
579 error: Some(e.clone()),
580 })
581 };
582
583 InitializeDetails {
584 init_success,
585 is_config_spec_ready,
586 is_id_list_ready,
587 source: specs_info.source.clone(),
588 failure_details,
589 duration,
590 spec_source_api: specs_info.source_api.clone(),
591 }
592 }
593
594 fn timeout_failure(&self, timeout_ms: u64) -> InitializeDetails {
595 InitializeDetails {
596 init_success: false,
597 is_config_spec_ready: false,
598 is_id_list_ready: None,
599 source: SpecsSource::Uninitialized,
600 failure_details: Some(FailureDetails {
601 reason: "Initialization timed out".to_string(),
602 error: None,
603 }),
604 duration: timeout_ms as f64,
605 spec_source_api: None,
606 }
607 }
608
609 fn log_init_details(&self, init_details: &Result<InitializeDetails, StatsigErr>) {
610 match init_details {
611 Ok(details) => {
612 self.log_init_finish(
613 details.init_success,
614 &None,
615 &details.duration,
616 &self.spec_store.get_current_specs_info(),
617 );
618 if let Some(failure) = &details.failure_details {
619 log_error_to_statsig_and_console!(
620 self.ops_stats,
621 TAG,
622 StatsigErr::InitializationError(failure.reason.clone())
623 );
624 }
625 }
626 Err(err) => {
627 log_w!(TAG, "Initialization error: {:?}", err);
629 }
630 }
631 }
632
633 pub fn get_context(&self) -> StatsigContext {
634 StatsigContext {
635 sdk_key: self.sdk_key.clone(),
636 options: self.options.clone(),
637 local_override_adapter: self.override_adapter.clone(),
638 error_observer: self.error_observer.clone(),
639 diagnostics_observer: self.diagnostics_observer.clone(),
640 spec_store: self.spec_store.clone(),
641 }
642 }
643
644 pub fn log_event(
645 &self,
646 user: &StatsigUser,
647 event_name: &str,
648 value: Option<String>,
649 metadata: Option<HashMap<String, String>>,
650 ) {
651 let user_internal = self.internalize_user(user);
652
653 self.event_logger.enqueue(EnqueuePassthroughOp {
654 event: StatsigEventInternal::new_custom_event(
655 user_internal.to_loggable(),
656 event_name.to_string(),
657 value.map(|v| json!(v)),
658 metadata,
659 ),
660 });
661 }
662
663 pub fn log_event_with_number(
664 &self,
665 user: &StatsigUser,
666 event_name: &str,
667 value: Option<f64>,
668 metadata: Option<HashMap<String, String>>,
669 ) {
670 let user_internal = self.internalize_user(user);
671 self.event_logger.enqueue(EnqueuePassthroughOp {
672 event: StatsigEventInternal::new_custom_event(
673 user_internal.to_loggable(),
674 event_name.to_string(),
675 value.map(|v| json!(v)),
676 metadata,
677 ),
678 });
679 }
680
681 pub fn log_layer_param_exposure_with_layer_json(
682 &self,
683 layer_json: String,
684 parameter_name: String,
685 ) {
686 let layer = match serde_json::from_str::<Layer>(&layer_json) {
687 Ok(layer) => layer,
688 Err(e) => {
689 log_error_to_statsig_and_console!(
690 self.ops_stats.clone(),
691 TAG,
692 StatsigErr::ShutdownFailure(e.to_string())
693 );
694 return;
695 }
696 };
697
698 self.log_layer_param_exposure_with_layer(layer, parameter_name);
699 }
700
701 pub fn log_layer_param_exposure_with_layer(&self, layer: Layer, parameter_name: String) {
702 if layer.__disable_exposure {
703 self.event_logger.increment_non_exposure_checks(&layer.name);
704 return;
705 }
706
707 self.event_logger
708 .enqueue(EnqueueLayerParamExpoOp::LayerOwned(
709 Box::new(layer),
710 parameter_name,
711 ExposureTrigger::Auto,
712 ));
713 }
714
715 pub async fn flush_events(&self) {
716 let _ = self.event_logger.flush_all_pending_events().await;
717 }
718
719 pub fn get_client_init_response(&self, user: &StatsigUser) -> InitializeResponse {
720 self.get_client_init_response_with_options(user, self.gcir_formatter.get_default_options())
721 }
722
723 pub fn get_client_init_response_with_options(
724 &self,
725 user: &StatsigUser,
726 options: &ClientInitResponseOptions,
727 ) -> InitializeResponse {
728 let user_internal = self.internalize_user(user);
729 self.gcir_formatter
730 .get_as_v1_format(user_internal, &self.hashing, options)
731 }
732
733 pub fn get_client_init_response_as_string(&self, user: &StatsigUser) -> String {
734 serde_json::to_string(&self.get_client_init_response(user)).unwrap_or_default()
735 }
736
737 pub fn get_client_init_response_with_options_as_string(
738 &self,
739 user: &StatsigUser,
740 options: &ClientInitResponseOptions,
741 ) -> String {
742 let user_internal = self.internalize_user(user);
743 let response = match options.response_format {
744 Some(GCIRResponseFormat::InitializeWithSecondaryExposureMapping) => {
745 json!(self
746 .gcir_formatter
747 .get_as_v2_format(user_internal, &self.hashing, options))
748 }
749 _ => json!(self
750 .gcir_formatter
751 .get_as_v1_format(user_internal, &self.hashing, options)),
752 };
753
754 json!(response).to_string()
755 }
756
757 pub fn get_string_parameter_from_store(
758 &self,
759 user: &StatsigUser,
760 parameter_store_name: &str,
761 parameter_name: &str,
762 fallback: Option<String>,
763 options: Option<ParameterStoreEvaluationOptions>,
764 ) -> Option<String> {
765 self.get_parameter_from_store(
766 user,
767 parameter_store_name,
768 parameter_name,
769 fallback,
770 options,
771 )
772 }
773
774 pub fn get_boolean_parameter_from_store(
775 &self,
776 user: &StatsigUser,
777 parameter_store_name: &str,
778 parameter_name: &str,
779 fallback: Option<bool>,
780 options: Option<ParameterStoreEvaluationOptions>,
781 ) -> Option<bool> {
782 self.get_parameter_from_store(
783 user,
784 parameter_store_name,
785 parameter_name,
786 fallback,
787 options,
788 )
789 }
790
791 pub fn get_float_parameter_from_store(
792 &self,
793 user: &StatsigUser,
794 parameter_store_name: &str,
795 parameter_name: &str,
796 fallback: Option<f64>,
797 options: Option<ParameterStoreEvaluationOptions>,
798 ) -> Option<f64> {
799 self.get_parameter_from_store(
800 user,
801 parameter_store_name,
802 parameter_name,
803 fallback,
804 options,
805 )
806 }
807
808 pub fn get_integer_parameter_from_store(
809 &self,
810 user: &StatsigUser,
811 parameter_store_name: &str,
812 parameter_name: &str,
813 fallback: Option<i64>,
814 options: Option<ParameterStoreEvaluationOptions>,
815 ) -> Option<i64> {
816 self.get_parameter_from_store(
817 user,
818 parameter_store_name,
819 parameter_name,
820 fallback,
821 options,
822 )
823 }
824
825 pub fn get_array_parameter_from_store(
826 &self,
827 user: &StatsigUser,
828 parameter_store_name: &str,
829 parameter_name: &str,
830 fallback: Option<Vec<Value>>,
831 options: Option<ParameterStoreEvaluationOptions>,
832 ) -> Option<Vec<Value>> {
833 self.get_parameter_from_store(
834 user,
835 parameter_store_name,
836 parameter_name,
837 fallback,
838 options,
839 )
840 }
841
842 pub fn get_object_parameter_from_store(
843 &self,
844 user: &StatsigUser,
845 parameter_store_name: &str,
846 parameter_name: &str,
847 fallback: Option<HashMap<String, Value>>,
848 options: Option<ParameterStoreEvaluationOptions>,
849 ) -> Option<HashMap<String, Value>> {
850 self.get_parameter_from_store(
851 user,
852 parameter_store_name,
853 parameter_name,
854 fallback,
855 options,
856 )
857 }
858
859 pub fn get_parameter_from_store<T: DeserializeOwned>(
860 &self,
861 user: &StatsigUser,
862 parameter_store_name: &str,
863 parameter_name: &str,
864 fallback: Option<T>,
865 options: Option<ParameterStoreEvaluationOptions>,
866 ) -> Option<T> {
867 let store = self
868 .get_parameter_store_with_options(parameter_store_name, options.unwrap_or_default());
869 match fallback {
870 Some(fallback) => Some(store.get(user, parameter_name, fallback)),
871 None => store.get_opt(user, parameter_name),
872 }
873 }
874
875 pub fn get_parameter_store(&self, parameter_store_name: &str) -> ParameterStore {
876 self.get_parameter_store_with_options(
877 parameter_store_name,
878 ParameterStoreEvaluationOptions::default(),
879 )
880 }
881
882 pub fn get_parameter_store_with_options(
883 &self,
884 parameter_store_name: &str,
885 options: ParameterStoreEvaluationOptions,
886 ) -> ParameterStore {
887 self.event_logger
888 .increment_non_exposure_checks(parameter_store_name);
889
890 let data = read_lock_or_else!(self.spec_store.data, {
891 log_error_to_statsig_and_console!(
892 self.ops_stats.clone(),
893 TAG,
894 StatsigErr::LockFailure(
895 "Failed to acquire read lock for spec store data".to_string()
896 )
897 );
898 return ParameterStore {
899 name: parameter_store_name.to_string(),
900 parameters: HashMap::new(),
901 details: EvaluationDetails::unrecognized_no_data(),
902 options,
903 _statsig_ref: self,
904 };
905 });
906
907 let stores = &data.values.param_stores;
908 let store = match stores {
909 Some(stores) => stores.get(parameter_store_name),
910 None => {
911 return ParameterStore {
912 name: parameter_store_name.to_string(),
913 parameters: HashMap::new(),
914 details: EvaluationDetails::unrecognized(&data),
915 options,
916 _statsig_ref: self,
917 };
918 }
919 };
920 match store {
921 Some(store) => ParameterStore {
922 name: parameter_store_name.to_string(),
923 parameters: store.parameters.clone(),
924 details: EvaluationDetails::recognized(&data, &EvaluatorResult::default()),
925 options,
926 _statsig_ref: self,
927 },
928 None => ParameterStore {
929 name: parameter_store_name.to_string(),
930 parameters: HashMap::new(),
931 details: EvaluationDetails::unrecognized(&data),
932 options,
933 _statsig_ref: self,
934 },
935 }
936 }
937}
938
939impl Statsig {
944 pub fn identify(&self, user: &StatsigUser) {
945 let user_internal = self.internalize_user(user);
946
947 self.event_logger.enqueue(EnqueuePassthroughOp {
948 event: StatsigEventInternal::new_custom_event(
949 user_internal.to_loggable(),
950 "statsig::identify".to_string(),
951 None,
952 None,
953 ),
954 });
955 }
956}
957
958impl Statsig {
963 pub fn get_cmab_ranked_groups(
964 &self,
965 user: &StatsigUser,
966 cmab_name: &str,
967 ) -> Vec<CMABRankedGroup> {
968 self.event_logger.increment_non_exposure_checks(cmab_name);
969
970 let data = read_lock_or_else!(self.spec_store.data, {
971 log_error_to_statsig_and_console!(
972 self.ops_stats.clone(),
973 TAG,
974 StatsigErr::LockFailure(
975 "Failed to acquire read lock for spec store data".to_string()
976 )
977 );
978 return vec![];
979 });
980 let user_internal = self.internalize_user(user);
981 get_cmab_ranked_list(
982 &mut EvaluatorContext::new(
983 &user_internal,
984 &data,
985 &self.hashing,
986 data.values.app_id.as_ref(),
987 self.override_adapter.as_ref(),
988 ),
989 cmab_name,
990 )
991 }
992
993 pub fn log_cmab_exposure_for_group(
994 &self,
995 user: &StatsigUser,
996 cmab_name: &str,
997 group_id: String,
998 ) {
999 let user_internal = self.internalize_user(user);
1000
1001 let mut experiment = self.get_experiment_impl(&user_internal, cmab_name);
1002 experiment.rule_id = group_id;
1003
1004 self.event_logger.enqueue(EnqueueExperimentExpoOp {
1005 user: &user_internal,
1006 experiment: &experiment,
1007 trigger: ExposureTrigger::Manual,
1008 });
1009 }
1010}
1011
1012impl Statsig {
1017 pub fn shared() -> Arc<Statsig> {
1018 let lock = match SHARED_INSTANCE.lock() {
1019 Ok(lock) => lock,
1020 Err(e) => {
1021 log_e!(TAG, "Statsig::shared() mutex error: {}", e);
1022 return Arc::new(Statsig::new(ERROR_SDK_KEY, None));
1023 }
1024 };
1025
1026 match lock.as_ref() {
1027 Some(statsig) => statsig.clone(),
1028 None => {
1029 log_e!(
1030 TAG,
1031 "Statsig::shared() called, but no instance has been set with Statsig::new_shared(...)"
1032 );
1033 Arc::new(Statsig::new(ERROR_SDK_KEY, None))
1034 }
1035 }
1036 }
1037
1038 pub fn new_shared(
1039 sdk_key: &str,
1040 options: Option<Arc<StatsigOptions>>,
1041 ) -> Result<Arc<Statsig>, StatsigErr> {
1042 match SHARED_INSTANCE.lock() {
1043 Ok(mut lock) => {
1044 if lock.is_some() {
1045 let message = "Statsig shared instance already exists. Call Statsig::remove_shared() before creating a new instance.";
1046 log_e!(TAG, "{}", message);
1047 return Err(StatsigErr::SharedInstanceFailure(message.to_string()));
1048 }
1049
1050 let statsig = Arc::new(Statsig::new(sdk_key, options));
1051 *lock = Some(statsig.clone());
1052 Ok(statsig)
1053 }
1054 Err(e) => {
1055 let message = format!("Statsig::new_shared() mutex error: {}", e);
1056 log_e!(TAG, "{}", message);
1057 Err(StatsigErr::SharedInstanceFailure(message))
1058 }
1059 }
1060 }
1061
1062 pub fn remove_shared() {
1063 match SHARED_INSTANCE.lock() {
1064 Ok(mut lock) => {
1065 *lock = None;
1066 }
1067 Err(e) => {
1068 log_e!(TAG, "Statsig::remove_shared() mutex error: {}", e);
1069 }
1070 }
1071 }
1072
1073 pub fn has_shared_instance() -> bool {
1074 match SHARED_INSTANCE.lock() {
1075 Ok(lock) => lock.is_some(),
1076 Err(_) => false,
1077 }
1078 }
1079}
1080
1081impl Statsig {
1086 pub fn check_gate(&self, user: &StatsigUser, gate_name: &str) -> bool {
1087 self.check_gate_with_options(user, gate_name, FeatureGateEvaluationOptions::default())
1088 }
1089
1090 pub fn check_gate_with_options(
1091 &self,
1092 user: &StatsigUser,
1093 gate_name: &str,
1094 options: FeatureGateEvaluationOptions,
1095 ) -> bool {
1096 let user_internal = self.internalize_user(user);
1097 let disable_exposure_logging = options.disable_exposure_logging;
1098 let (details, evaluation) = self.get_gate_evaluation(&user_internal, gate_name);
1099 let value = evaluation.as_ref().map(|e| e.value).unwrap_or_default();
1100
1101 if disable_exposure_logging {
1102 log_d!(TAG, "Exposure logging is disabled for gate {}", gate_name);
1103 self.event_logger.increment_non_exposure_checks(gate_name);
1104 } else {
1105 self.event_logger.enqueue(EnqueueGateExpoOp {
1106 user: &user_internal,
1107 queried_gate_name: gate_name,
1108 evaluation: evaluation.map(Cow::Owned),
1109 details: details.clone(),
1110 trigger: ExposureTrigger::Auto,
1111 });
1112 }
1113
1114 value
1115 }
1116
1117 pub fn get_feature_gate(&self, user: &StatsigUser, gate_name: &str) -> FeatureGate {
1118 self.get_feature_gate_with_options(user, gate_name, FeatureGateEvaluationOptions::default())
1119 }
1120
1121 pub fn get_feature_gate_with_options(
1122 &self,
1123 user: &StatsigUser,
1124 gate_name: &str,
1125 options: FeatureGateEvaluationOptions,
1126 ) -> FeatureGate {
1127 let user_internal = self.internalize_user(user);
1128 let disable_exposure_logging = options.disable_exposure_logging;
1129 let (details, evaluation) = self.get_gate_evaluation(&user_internal, gate_name);
1130
1131 if disable_exposure_logging {
1132 log_d!(TAG, "Exposure logging is disabled for gate {}", gate_name);
1133 self.event_logger.increment_non_exposure_checks(gate_name);
1134 } else {
1135 self.event_logger.enqueue(EnqueueGateExpoOp {
1136 user: &user_internal,
1137 queried_gate_name: gate_name,
1138 evaluation: evaluation.as_ref().map(Cow::Borrowed),
1139 details: details.clone(),
1140 trigger: ExposureTrigger::Auto,
1141 });
1142 }
1143
1144 make_feature_gate(gate_name, evaluation, details)
1145 }
1146
1147 pub fn manually_log_gate_exposure(&self, user: &StatsigUser, gate_name: &str) {
1148 let user_internal = self.internalize_user(user);
1149 let (details, evaluation) = self.get_gate_evaluation(&user_internal, gate_name);
1150 self.event_logger.enqueue(EnqueueGateExpoOp {
1151 user: &user_internal,
1152 queried_gate_name: gate_name,
1153 evaluation: evaluation.map(Cow::Owned),
1154 details: details.clone(),
1155 trigger: ExposureTrigger::Manual,
1156 });
1157 }
1158
1159 pub fn get_fields_needed_for_gate(&self, gate_name: &str) -> Vec<String> {
1160 let data = read_lock_or_else!(self.spec_store.data, {
1161 log_error_to_statsig_and_console!(
1162 self.ops_stats.clone(),
1163 TAG,
1164 StatsigErr::LockFailure(
1165 "Failed to acquire read lock for spec store data".to_string()
1166 )
1167 );
1168 return vec![];
1169 });
1170
1171 let gate = data.values.feature_gates.get(gate_name);
1172 match gate {
1173 Some(gate) => match &gate.spec.fields_used {
1174 Some(fields) => fields.clone(),
1175 None => vec![],
1176 },
1177 None => vec![],
1178 }
1179 }
1180}
1181
1182impl Statsig {
1187 pub fn override_gate(&self, gate_name: &str, value: bool, id: Option<&str>) {
1188 if let Some(adapter) = &self.override_adapter {
1189 adapter.override_gate(gate_name, value, id);
1190 }
1191 }
1192
1193 pub fn override_dynamic_config(
1194 &self,
1195 config_name: &str,
1196 value: HashMap<String, serde_json::Value>,
1197 id: Option<&str>,
1198 ) {
1199 if let Some(adapter) = &self.override_adapter {
1200 adapter.override_dynamic_config(config_name, value, id);
1201 }
1202 }
1203
1204 pub fn override_layer(
1205 &self,
1206 layer_name: &str,
1207 value: HashMap<String, serde_json::Value>,
1208 id: Option<&str>,
1209 ) {
1210 if let Some(adapter) = &self.override_adapter {
1211 adapter.override_layer(layer_name, value, id);
1212 }
1213 }
1214
1215 pub fn override_experiment(
1216 &self,
1217 experiment_name: &str,
1218 value: HashMap<String, serde_json::Value>,
1219 id: Option<&str>,
1220 ) {
1221 if let Some(adapter) = &self.override_adapter {
1222 adapter.override_experiment(experiment_name, value, id);
1223 }
1224 }
1225
1226 pub fn override_experiment_by_group_name(
1227 &self,
1228 experiment_name: &str,
1229 group_name: &str,
1230 id: Option<&str>,
1231 ) {
1232 if let Some(adapter) = &self.override_adapter {
1233 adapter.override_experiment_by_group_name(experiment_name, group_name, id);
1234 }
1235 }
1236
1237 pub fn remove_gate_override(&self, gate_name: &str, id: Option<&str>) {
1238 if let Some(adapter) = &self.override_adapter {
1239 adapter.remove_gate_override(gate_name, id);
1240 }
1241 }
1242
1243 pub fn remove_dynamic_config_override(&self, config_name: &str, id: Option<&str>) {
1244 if let Some(adapter) = &self.override_adapter {
1245 adapter.remove_dynamic_config_override(config_name, id);
1246 }
1247 }
1248
1249 pub fn remove_experiment_override(&self, experiment_name: &str, id: Option<&str>) {
1250 if let Some(adapter) = &self.override_adapter {
1251 adapter.remove_experiment_override(experiment_name, id);
1252 }
1253 }
1254
1255 pub fn remove_layer_override(&self, layer_name: &str, id: Option<&str>) {
1256 if let Some(adapter) = &self.override_adapter {
1257 adapter.remove_layer_override(layer_name, id);
1258 }
1259 }
1260
1261 pub fn remove_all_overrides(&self) {
1262 if let Some(adapter) = &self.override_adapter {
1263 adapter.remove_all_overrides();
1264 }
1265 }
1266}
1267
1268impl Statsig {
1273 pub fn get_feature_gate_list(&self) -> Vec<String> {
1274 let data = read_lock_or_else!(self.spec_store.data, {
1275 log_error_to_statsig_and_console!(
1276 &self.ops_stats,
1277 TAG,
1278 StatsigErr::LockFailure(
1279 "Failed to acquire read lock for spec store data".to_string()
1280 )
1281 );
1282 return vec![];
1283 });
1284
1285 data.values.feature_gates.unperformant_keys()
1286 }
1287
1288 pub fn get_dynamic_config_list(&self) -> Vec<String> {
1289 let data = read_lock_or_else!(self.spec_store.data, {
1290 log_error_to_statsig_and_console!(
1291 &self.ops_stats,
1292 TAG,
1293 StatsigErr::LockFailure(
1294 "Failed to acquire read lock for spec store data".to_string()
1295 )
1296 );
1297 return vec![];
1298 });
1299
1300 data.values
1301 .dynamic_configs
1302 .unperformant_keys_entity_filter("dynamic_config")
1303 }
1304
1305 pub fn get_experiment_list(&self) -> Vec<String> {
1306 let data = read_lock_or_else!(self.spec_store.data, {
1307 log_error_to_statsig_and_console!(
1308 &self.ops_stats,
1309 TAG,
1310 StatsigErr::LockFailure(
1311 "Failed to acquire read lock for spec store data".to_string()
1312 )
1313 );
1314 return vec![];
1315 });
1316
1317 data.values
1318 .dynamic_configs
1319 .unperformant_keys_entity_filter("experiment")
1320 }
1321
1322 pub fn get_autotune_list(&self) -> Vec<String> {
1323 let data = read_lock_or_else!(self.spec_store.data, {
1324 log_error_to_statsig_and_console!(
1325 &self.ops_stats,
1326 TAG,
1327 StatsigErr::LockFailure(
1328 "Failed to acquire read lock for spec store data".to_string()
1329 )
1330 );
1331 return vec![];
1332 });
1333
1334 data.values
1335 .dynamic_configs
1336 .unperformant_keys_entity_filter("autotune")
1337 }
1338
1339 pub fn get_parameter_store_list(&self) -> Vec<String> {
1340 let data = read_lock_or_else!(self.spec_store.data, {
1341 log_error_to_statsig_and_console!(
1342 &self.ops_stats,
1343 TAG,
1344 StatsigErr::LockFailure(
1345 "Failed to acquire read lock for spec store data".to_string()
1346 )
1347 );
1348 return vec![];
1349 });
1350
1351 match &data.values.param_stores {
1352 Some(param_stores) => param_stores.keys().cloned().collect(),
1353 None => vec![],
1354 }
1355 }
1356
1357 pub fn get_layer_list(&self) -> Vec<String> {
1358 let data = read_lock_or_else!(self.spec_store.data, {
1359 log_error_to_statsig_and_console!(
1360 &self.ops_stats,
1361 TAG,
1362 StatsigErr::LockFailure(
1363 "Failed to acquire read lock for spec store data".to_string()
1364 )
1365 );
1366 return vec![];
1367 });
1368
1369 data.values.layer_configs.unperformant_keys()
1370 }
1371}
1372
1373impl Statsig {
1378 pub fn get_dynamic_config(
1379 &self,
1380 user: &StatsigUser,
1381 dynamic_config_name: &str,
1382 ) -> DynamicConfig {
1383 self.get_dynamic_config_with_options(
1384 user,
1385 dynamic_config_name,
1386 DynamicConfigEvaluationOptions::default(),
1387 )
1388 }
1389
1390 pub fn get_dynamic_config_with_options(
1391 &self,
1392 user: &StatsigUser,
1393 dynamic_config_name: &str,
1394 options: DynamicConfigEvaluationOptions,
1395 ) -> DynamicConfig {
1396 let user_internal = self.internalize_user(user);
1397 let disable_exposure_logging = options.disable_exposure_logging;
1398 let dynamic_config = self.get_dynamic_config_impl(&user_internal, dynamic_config_name);
1399
1400 if disable_exposure_logging {
1401 log_d!(
1402 TAG,
1403 "Exposure logging is disabled for Dynamic Config {}",
1404 dynamic_config_name
1405 );
1406 self.event_logger
1407 .increment_non_exposure_checks(dynamic_config_name);
1408 } else {
1409 self.event_logger.enqueue(EnqueueConfigExpoOp {
1410 user: &user_internal,
1411 config: &dynamic_config,
1412 trigger: ExposureTrigger::Auto,
1413 });
1414 }
1415
1416 dynamic_config
1417 }
1418
1419 pub fn manually_log_dynamic_config_exposure(
1420 &self,
1421 user: &StatsigUser,
1422 dynamic_config_name: &str,
1423 ) {
1424 let user_internal = self.internalize_user(user);
1425 let dynamic_config = self.get_dynamic_config_impl(&user_internal, dynamic_config_name);
1426 self.event_logger.enqueue(EnqueueConfigExpoOp {
1427 user: &user_internal,
1428 config: &dynamic_config,
1429 trigger: ExposureTrigger::Manual,
1430 });
1431 }
1432
1433 pub fn get_fields_needed_for_dynamic_config(&self, config_name: &str) -> Vec<String> {
1434 let data = read_lock_or_else!(self.spec_store.data, {
1435 log_error_to_statsig_and_console!(
1436 self.ops_stats.clone(),
1437 TAG,
1438 StatsigErr::LockFailure(
1439 "Failed to acquire read lock for spec store data".to_string()
1440 )
1441 );
1442 return vec![];
1443 });
1444
1445 let config = data.values.dynamic_configs.get(config_name);
1446 match config {
1447 Some(config) => match &config.spec.fields_used {
1448 Some(fields) => fields.clone(),
1449 None => vec![],
1450 },
1451 None => vec![],
1452 }
1453 }
1454}
1455
1456impl Statsig {
1461 pub fn get_experiment(&self, user: &StatsigUser, experiment_name: &str) -> Experiment {
1462 self.get_experiment_with_options(
1463 user,
1464 experiment_name,
1465 ExperimentEvaluationOptions::default(),
1466 )
1467 }
1468
1469 pub fn get_experiment_with_options(
1470 &self,
1471 user: &StatsigUser,
1472 experiment_name: &str,
1473 options: ExperimentEvaluationOptions,
1474 ) -> Experiment {
1475 let user_internal = self.internalize_user(user);
1476 let disable_exposure_logging = options.disable_exposure_logging;
1477 let mut experiment = self.get_experiment_impl(&user_internal, experiment_name);
1478 if let Some(persisted_experiment) = self.persistent_values_manager.as_ref().and_then(|m| {
1479 m.try_apply_sticky_value_to_experiment(&user_internal, &options, &experiment)
1480 }) {
1481 experiment = persisted_experiment
1482 }
1483
1484 if disable_exposure_logging {
1485 log_d!(
1486 TAG,
1487 "Exposure logging is disabled for experiment {}",
1488 experiment_name
1489 );
1490 self.event_logger
1491 .increment_non_exposure_checks(experiment_name);
1492 } else {
1493 self.event_logger.enqueue(EnqueueExperimentExpoOp {
1494 user: &user_internal,
1495 experiment: &experiment,
1496 trigger: ExposureTrigger::Auto,
1497 });
1498 }
1499
1500 experiment
1501 }
1502
1503 pub fn manually_log_experiment_exposure(&self, user: &StatsigUser, experiment_name: &str) {
1504 let user_internal = self.internalize_user(user);
1505 let experiment = self.get_experiment_impl(&user_internal, experiment_name);
1506 self.event_logger.enqueue(EnqueueExperimentExpoOp {
1507 user: &user_internal,
1508 experiment: &experiment,
1509 trigger: ExposureTrigger::Manual,
1510 });
1511 }
1512
1513 pub fn get_fields_needed_for_experiment(&self, experiment_name: &str) -> Vec<String> {
1514 let data = read_lock_or_else!(self.spec_store.data, {
1515 log_error_to_statsig_and_console!(
1516 self.ops_stats.clone(),
1517 TAG,
1518 StatsigErr::LockFailure(
1519 "Failed to acquire read lock for spec store data".to_string()
1520 )
1521 );
1522 return vec![];
1523 });
1524
1525 let config = data.values.dynamic_configs.get(experiment_name);
1526 match config {
1527 Some(config) => match &config.spec.fields_used {
1528 Some(fields) => fields.clone(),
1529 None => vec![],
1530 },
1531 None => vec![],
1532 }
1533 }
1534}
1535
1536impl Statsig {
1541 pub fn get_layer(&self, user: &StatsigUser, layer_name: &str) -> Layer {
1542 self.get_layer_with_options(user, layer_name, LayerEvaluationOptions::default())
1543 }
1544
1545 pub fn get_layer_with_options(
1546 &self,
1547 user: &StatsigUser,
1548 layer_name: &str,
1549 options: LayerEvaluationOptions,
1550 ) -> Layer {
1551 let user_internal = self.internalize_user(user);
1552 self.get_layer_impl(user_internal, layer_name, options)
1553 }
1554
1555 pub fn manually_log_layer_parameter_exposure(
1556 &self,
1557 user: &StatsigUser,
1558 layer_name: &str,
1559 parameter_name: String,
1560 ) {
1561 let user_internal = self.internalize_user(user);
1562 let layer =
1563 self.get_layer_impl(user_internal, layer_name, LayerEvaluationOptions::default());
1564
1565 self.event_logger
1566 .enqueue(EnqueueLayerParamExpoOp::LayerOwned(
1567 Box::new(layer),
1568 parameter_name,
1569 ExposureTrigger::Manual,
1570 ));
1571 }
1572
1573 pub fn get_fields_needed_for_layer(&self, layer_name: &str) -> Vec<String> {
1574 let data = read_lock_or_else!(self.spec_store.data, {
1575 log_error_to_statsig_and_console!(
1576 self.ops_stats.clone(),
1577 TAG,
1578 StatsigErr::LockFailure(
1579 "Failed to acquire read lock for spec store data".to_string()
1580 )
1581 );
1582 return vec![];
1583 });
1584
1585 let layer = data.values.layer_configs.get(layer_name);
1586 match layer {
1587 Some(layer) => match &layer.spec.fields_used {
1588 Some(fields) => fields.clone(),
1589 None => vec![],
1590 },
1591 None => vec![],
1592 }
1593 }
1594}
1595
1596impl Statsig {
1601 pub(crate) fn get_from_statsig_env(&self, key: &str) -> Option<DynamicValue> {
1602 if let Some(env) = &self.statsig_environment {
1603 return env.get(key).cloned();
1604 }
1605
1606 if let Ok(fallback_env) = self.fallback_environment.lock() {
1607 if let Some(env) = &*fallback_env {
1608 return env.get(key).cloned();
1609 }
1610 }
1611
1612 None
1613 }
1614
1615 pub(crate) fn get_value_from_global_custom_fields(&self, key: &str) -> Option<&DynamicValue> {
1616 if let Some(env) = &self.options.global_custom_fields {
1617 return env.get(key);
1618 }
1619
1620 None
1621 }
1622
1623 pub(crate) fn use_global_custom_fields<T>(
1624 &self,
1625 f: impl FnOnce(Option<&HashMap<String, DynamicValue>>) -> T,
1626 ) -> T {
1627 f(self.options.global_custom_fields.as_ref())
1628 }
1629
1630 pub(crate) fn use_statsig_env<T>(
1631 &self,
1632 f: impl FnOnce(Option<&HashMap<String, DynamicValue>>) -> T,
1633 ) -> T {
1634 if let Some(env) = &self.statsig_environment {
1635 return f(Some(env));
1636 }
1637
1638 if let Ok(fallback_env) = self.fallback_environment.lock() {
1639 if let Some(env) = &*fallback_env {
1640 return f(Some(env));
1641 }
1642 }
1643
1644 f(None)
1645 }
1646}
1647
1648impl Statsig {
1653 fn evaluate_spec<T>(
1654 &self,
1655 user_internal: &StatsigUserInternal,
1656 spec_name: &str,
1657 make_empty_result: impl FnOnce(EvaluationDetails) -> T,
1658 make_result: impl FnOnce(EvaluatorResult, EvaluationDetails) -> T,
1659 spec_type: &SpecType,
1660 ) -> T {
1661 let data = read_lock_or_else!(self.spec_store.data, {
1662 log_error_to_statsig_and_console!(
1663 &self.ops_stats,
1664 TAG,
1665 StatsigErr::LockFailure(
1666 "Failed to acquire read lock for spec store data".to_string()
1667 )
1668 );
1669 return make_empty_result(EvaluationDetails::unrecognized_no_data());
1670 });
1671 let app_id = data.values.app_id.as_ref();
1672 let mut context = EvaluatorContext::new(
1673 user_internal,
1674 &data,
1675 &self.hashing,
1676 app_id,
1677 self.override_adapter.as_ref(),
1678 );
1679
1680 match Evaluator::evaluate_with_details(&mut context, spec_name, spec_type) {
1681 Ok(eval_details) => make_result(context.result, eval_details),
1682 Err(e) => {
1683 log_error_to_statsig_and_console!(
1684 &self.ops_stats,
1685 TAG,
1686 StatsigErr::EvaluationError(e.to_string())
1687 );
1688 make_empty_result(EvaluationDetails::error(&e.to_string()))
1689 }
1690 }
1691 }
1692
1693 fn get_gate_evaluation(
1694 &self,
1695 user_internal: &StatsigUserInternal,
1696 gate_name: &str,
1697 ) -> (EvaluationDetails, Option<GateEvaluation>) {
1698 self.evaluate_spec(
1699 user_internal,
1700 gate_name,
1701 |eval_details| (eval_details, None),
1702 |mut result, eval_details| {
1703 let evaluation = result_to_gate_eval(gate_name, &mut result);
1704 (eval_details, Some(evaluation))
1705 },
1706 &SpecType::Gate,
1707 )
1708 }
1709
1710 fn get_dynamic_config_impl(
1711 &self,
1712 user_internal: &StatsigUserInternal,
1713 config_name: &str,
1714 ) -> DynamicConfig {
1715 self.evaluate_spec(
1716 user_internal,
1717 config_name,
1718 |eval_details| make_dynamic_config(config_name, None, eval_details),
1719 |mut result, eval_details| {
1720 let evaluation = result_to_dynamic_config_eval(config_name, &mut result);
1721 make_dynamic_config(config_name, Some(evaluation), eval_details)
1722 },
1723 &SpecType::DynamicConfig,
1724 )
1725 }
1726
1727 fn get_experiment_impl(
1728 &self,
1729 user_internal: &StatsigUserInternal,
1730 experiment_name: &str,
1731 ) -> Experiment {
1732 self.evaluate_spec(
1733 user_internal,
1734 experiment_name,
1735 |eval_details| make_experiment(experiment_name, None, eval_details),
1736 |mut result, eval_details| {
1737 let evaluation = result_to_experiment_eval(experiment_name, None, &mut result);
1738 make_experiment(experiment_name, Some(evaluation), eval_details)
1739 },
1740 &SpecType::Experiment,
1741 )
1742 }
1743
1744 fn get_layer_impl(
1745 &self,
1746 user_internal: StatsigUserInternal,
1747 layer_name: &str,
1748 evaluation_options: LayerEvaluationOptions,
1749 ) -> Layer {
1750 let disable_exposure_logging = evaluation_options.disable_exposure_logging;
1751
1752 if disable_exposure_logging {
1753 self.event_logger.increment_non_exposure_checks(layer_name);
1754 }
1755
1756 let mut layer = self.evaluate_spec(
1757 &user_internal,
1758 layer_name,
1759 |eval_details| {
1760 make_layer(
1761 user_internal.to_loggable(),
1762 layer_name,
1763 None,
1764 eval_details,
1765 None,
1766 disable_exposure_logging,
1767 )
1768 },
1769 |mut result, eval_details| {
1770 let evaluation = result_to_layer_eval(layer_name, &mut result);
1771 let event_logger_ptr = Arc::downgrade(&self.event_logger);
1772
1773 make_layer(
1774 user_internal.to_loggable(),
1775 layer_name,
1776 Some(evaluation),
1777 eval_details,
1778 Some(event_logger_ptr),
1779 disable_exposure_logging,
1780 )
1781 },
1782 &SpecType::Layer,
1783 );
1784 if let Some(persisted_layer) = self.persistent_values_manager.as_ref().and_then(|p| {
1785 let event_logger_ptr = Arc::downgrade(&self.event_logger);
1786 p.try_apply_sticky_value_to_layer(
1787 &user_internal,
1788 &evaluation_options,
1789 &layer,
1790 Some(event_logger_ptr),
1791 disable_exposure_logging,
1792 )
1793 }) {
1794 layer = persisted_layer
1795 }
1796 layer
1797 }
1798
1799 fn internalize_user<'s, 'u>(&'s self, user: &'u StatsigUser) -> StatsigUserInternal<'s, 'u> {
1800 StatsigUserInternal::new(user, Some(self))
1801 }
1802
1803 fn set_default_environment_from_server(&self) {
1804 let data = read_lock_or_else!(self.spec_store.data, {
1805 return;
1806 });
1807
1808 if let Some(default_env) = data.values.default_environment.as_ref() {
1809 let env_map = HashMap::from([("tier".to_string(), dyn_value!(default_env.as_str()))]);
1810
1811 if let Ok(mut fallback_env) = self.fallback_environment.lock() {
1812 *fallback_env = Some(env_map);
1813 }
1814 }
1815 }
1816
1817 fn log_init_finish(
1818 &self,
1819 success: bool,
1820 error_message: &Option<String>,
1821 duration: &f64,
1822 specs_info: &SpecsInfo,
1823 ) {
1824 let is_store_populated = specs_info.source != SpecsSource::NoValues;
1825 let source_str = specs_info.source.to_string();
1826
1827 let event = ObservabilityEvent::new_event(
1828 MetricType::Dist,
1829 "initialization".to_string(),
1830 *duration,
1831 Some(HashMap::from([
1832 ("success".to_owned(), success.to_string()),
1833 ("source".to_owned(), source_str.clone()),
1834 ("store_populated".to_owned(), is_store_populated.to_string()),
1835 (
1836 "spec_source_api".to_owned(),
1837 specs_info.source_api.clone().unwrap_or_default(),
1838 ),
1839 ])),
1840 );
1841
1842 self.ops_stats.log(event);
1843 self.ops_stats.add_marker(
1844 {
1845 let marker = Marker::new(KeyType::Overall, ActionType::End, None)
1846 .with_is_success(success)
1847 .with_config_spec_ready(specs_info.source != SpecsSource::NoValues)
1848 .with_source(source_str);
1849
1850 if let Some(msg) = &error_message {
1851 marker.with_message(msg.to_string())
1852 } else {
1853 marker
1854 }
1855 },
1856 Some(ContextType::Initialize),
1857 );
1858 self.ops_stats
1859 .enqueue_diagnostics_event(None, Some(ContextType::Initialize));
1860 }
1861}
1862
1863fn initialize_event_logging_adapter(
1864 sdk_key: &str,
1865 options: &StatsigOptions,
1866) -> Arc<dyn EventLoggingAdapter> {
1867 options
1868 .event_logging_adapter
1869 .clone()
1870 .unwrap_or_else(|| Arc::new(StatsigHttpEventLoggingAdapter::new(sdk_key, Some(options))))
1871}
1872
1873fn initialize_specs_adapter(
1874 sdk_key: &str,
1875 options: &StatsigOptions,
1876 hashing: &HashUtil,
1877) -> Arc<dyn SpecsAdapter> {
1878 if let Some(adapter) = options.specs_adapter.clone() {
1879 log_d!(TAG, "Using provided SpecsAdapter: {}", sdk_key);
1880 return adapter;
1881 }
1882
1883 if let Some(adapter_config) = options.spec_adapters_config.clone() {
1884 return Arc::new(StatsigCustomizedSpecsAdapter::new_from_config(
1885 sdk_key,
1886 adapter_config,
1887 options,
1888 hashing,
1889 ));
1890 }
1891
1892 if let Some(data_adapter) = options.data_store.clone() {
1893 return Arc::new(StatsigCustomizedSpecsAdapter::new_from_data_store(
1894 sdk_key,
1895 data_adapter,
1896 options,
1897 hashing,
1898 ));
1899 }
1900
1901 Arc::new(StatsigHttpSpecsAdapter::new(sdk_key, Some(options), None))
1902}
1903
1904fn initialize_id_lists_adapter(
1905 sdk_key: &str,
1906 options: &StatsigOptions,
1907) -> Option<Arc<dyn IdListsAdapter>> {
1908 if let Some(id_lists_adapter) = options.id_lists_adapter.clone() {
1909 return Some(id_lists_adapter);
1910 }
1911
1912 if options.enable_id_lists.unwrap_or(false) {
1913 return Some(Arc::new(StatsigHttpIdListsAdapter::new(sdk_key, options)));
1914 }
1915
1916 None
1917}
1918
1919fn setup_ops_stats(
1920 sdk_key: &str,
1921 statsig_runtime: Arc<StatsigRuntime>,
1922 error_observer: &Arc<dyn OpsStatsEventObserver>,
1923 diagnostics_observer: &Arc<dyn OpsStatsEventObserver>,
1924 external_observer: &Option<Weak<dyn ObservabilityClient>>,
1925) -> Arc<OpsStatsForInstance> {
1926 let ops_stat = OPS_STATS.get_for_instance(sdk_key);
1927 ops_stat.subscribe(statsig_runtime.clone(), Arc::downgrade(error_observer));
1928 ops_stat.subscribe(
1929 statsig_runtime.clone(),
1930 Arc::downgrade(diagnostics_observer),
1931 );
1932
1933 if let Some(ob_client) = external_observer {
1934 if let Some(client) = ob_client.upgrade() {
1935 client.init();
1936 let as_observer = client.to_ops_stats_event_observer();
1937 ops_stat.subscribe(statsig_runtime, Arc::downgrade(&as_observer));
1938 }
1939 }
1940
1941 ops_stat
1942}