1
1
import functools
2
2
import time
3
- from typing import Optional , Dict , Any
3
+ from typing import Optional , Dict , Any , Callable
4
4
5
5
from .initialize_details import InitializeDetails
6
6
from .interface_observability_client import ObservabilityClient
@@ -49,17 +49,22 @@ def __init_subclass__(cls, **kwargs):
49
49
50
50
51
51
class StatsigTelemetryLogger (AutoTryCatch ):
52
- def __init__ (self , logger = None , ob_client : Optional [ObservabilityClient ] = None ):
52
+ def __init__ (self , logger = None , ob_client : Optional [ObservabilityClient ] = None ,
53
+ sdk_error_callback : Optional [Callable [[str , Exception ], None ]] = None ):
53
54
self .high_cardinality_tags = {"lcut" , "prev_lcut" }
54
55
self .logger = logger or OutputLogger (TELEMETRY_PREFIX )
55
56
self .ob_client = ob_client or NoopObservabilityClient ()
57
+ self .sdk_error_callback = sdk_error_callback
56
58
57
59
def set_logger (self , output_logger ):
58
60
self .logger = output_logger
59
61
60
62
def set_ob_client (self , ob_client ):
61
63
self .ob_client = ob_client
62
64
65
+ def set_sdk_error_callback (self , sdk_error_callback ):
66
+ self .sdk_error_callback = sdk_error_callback
67
+
63
68
def init (self ):
64
69
self .ob_client .init ()
65
70
@@ -84,6 +89,17 @@ def exception(self, msg, *args, **kwargs):
84
89
def log_process (self , process , msg ):
85
90
self .logger .log_process (process , msg )
86
91
92
+ def increment (self , metric_name : str , value : int = 1 , tags : Optional [Dict [str , Any ]] = None ):
93
+ self .ob_client .increment (f'{ TELEMETRY_PREFIX } .{ metric_name } ' , value ,
94
+ self .filter_high_cardinality_tags (tags or {}))
95
+
96
+ def gauge (self , metric_name : str , value : float , tags : Optional [Dict [str , Any ]] = None ):
97
+ self .ob_client .gauge (f'{ TELEMETRY_PREFIX } .{ metric_name } ' , value , self .filter_high_cardinality_tags (tags or {}))
98
+
99
+ def distribution (self , metric_name : str , value : float , tags : Optional [Dict [str , Any ]] = None ):
100
+ self .ob_client .distribution (f'{ TELEMETRY_PREFIX } .{ metric_name } ' , value ,
101
+ self .filter_high_cardinality_tags (tags or {}))
102
+
87
103
def log_post_init (self , options : StatsigOptions , init_details : InitializeDetails ):
88
104
if options .local_mode :
89
105
if init_details .init_success :
@@ -93,11 +109,11 @@ def log_post_init(self, options: StatsigOptions, init_details: InitializeDetails
93
109
self .logger .error ("Statsig SDK instance failed to initialize in local mode." )
94
110
return
95
111
96
- self .ob_client . distribution (f" { TELEMETRY_PREFIX } . initialization" , init_details .duration ,
97
- self .filter_high_cardinality_tags ({"source" : init_details .source ,
98
- "store_populated" : init_details .store_populated ,
99
- "init_success" : init_details .init_success ,
100
- "init_source_api" : init_details .init_source_api }))
112
+ self .distribution (" initialization" , init_details .duration ,
113
+ self .filter_high_cardinality_tags ({"source" : init_details .source ,
114
+ "store_populated" : init_details .store_populated ,
115
+ "init_success" : init_details .init_success ,
116
+ "init_source_api" : init_details .init_source_api }))
101
117
102
118
if init_details .init_success :
103
119
if init_details .store_populated :
@@ -120,20 +136,26 @@ def log_config_sync_update(self, initialized: bool, has_update: bool, lcut: int,
120
136
return # do not log for initialize
121
137
if not has_update :
122
138
self .log_process ("Config Sync" , "No update" )
123
- self .ob_client . increment (f" { TELEMETRY_PREFIX } . config_no_update" , 1 , {"source" : source , "source_api" : api })
139
+ self .increment (" config_no_update" , 1 , {"source" : source , "source_api" : api })
124
140
return
125
141
126
142
lcut_diff = abs (lcut - int (time .time () * 1000 ))
127
143
if lcut_diff > 0 :
128
- self .ob_client . distribution (f" { TELEMETRY_PREFIX } . config_propagation_diff" , lcut_diff ,
129
- self .filter_high_cardinality_tags ({
130
- "source" : source ,
131
- "source_api" : api ,
132
- "lcut" : lcut ,
133
- "prev_lcut" : prev_lcut
134
- }))
144
+ self .distribution (" config_propagation_diff" , lcut_diff ,
145
+ self .filter_high_cardinality_tags ({
146
+ "source" : source ,
147
+ "source_api" : api ,
148
+ "lcut" : lcut ,
149
+ "prev_lcut" : prev_lcut
150
+ }))
135
151
self .log_process ("Config Sync" , f"Received updated configs from { lcut } " )
136
152
153
+ def log_sdk_exception (self , tag : str , exception : Exception ):
154
+ if self .sdk_error_callback is not None :
155
+ self .sdk_error_callback (tag , exception )
156
+
157
+ self .increment ("sdk_exceptions_count" )
158
+
137
159
def filter_high_cardinality_tags (self , tags : Dict [str , Any ]) -> Dict [str , Any ]:
138
160
return {tag : value for tag , value in tags .items ()
139
161
if tag not in self .high_cardinality_tags or self .ob_client .should_enable_high_cardinality_for_this_tag (
0 commit comments