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