Skip to content

Commit acbeb35

Browse files
committed
basic telemetry classes
1 parent deb2cf5 commit acbeb35

File tree

7 files changed

+684
-2
lines changed

7 files changed

+684
-2
lines changed

splitio/api/telemetry.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
from splitio.api.client import HttpClientException
77
from splitio.api.commons import headers_from_metadata
88

9+
_LOGGER = logging.getLogger(__name__)
10+
911
class TelemetryAPI(object): # pylint: disable=too-few-public-methods
1012
"""Class that uses an httpClient to communicate with the Telemetry API."""
1113

@@ -45,3 +47,51 @@ def record_unique_keys(self, uniques):
4547
)
4648
_LOGGER.debug('Error: ', exc_info=True)
4749
raise APIException('Unique keys not flushed properly.') from exc
50+
51+
def record_init(self, configs):
52+
"""
53+
Send init config data to the backend.
54+
55+
:param configs: configs
56+
:type json
57+
"""
58+
try:
59+
response = self._client.post(
60+
'metrics',
61+
'/config',
62+
self._apikey,
63+
body=configs,
64+
extra_headers=self._metadata
65+
)
66+
if not 200 <= response.status_code < 300:
67+
raise APIException(response.body, response.status_code)
68+
except HttpClientException as exc:
69+
_LOGGER.error(
70+
'Error posting init config because an exception was raised by the HTTPClient'
71+
)
72+
_LOGGER.debug('Error: ', exc_info=True)
73+
raise APIException('Init config data not flushed properly.') from exc
74+
75+
def record_stats(self, stats):
76+
"""
77+
Send runtime stats to the backend.
78+
79+
:param configs: configs
80+
:type json
81+
"""
82+
try:
83+
response = self._client.post(
84+
'metrics',
85+
'/usage',
86+
self._apikey,
87+
body=stats,
88+
extra_headers=self._metadata
89+
)
90+
if not 200 <= response.status_code < 300:
91+
raise APIException(response.body, response.status_code)
92+
except HttpClientException as exc:
93+
_LOGGER.error(
94+
'Error posting runtime stats because an exception was raised by the HTTPClient'
95+
)
96+
_LOGGER.debug('Error: ', exc_info=True)
97+
raise APIException('Runtime stats not flushed properly.') from exc

splitio/client/factory.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,12 @@ def _build_in_memory_factory(api_key, cfg, sdk_url=None, events_url=None, # pyl
297297
if not input_validator.validate_factory_instantiation(api_key):
298298
return None
299299

300+
cfg['sdk_url'] = sdk_url if sdk_url is not None else None
301+
cfg['events_url'] = events_url if events_url is not None else None
302+
cfg['auth_url'] = auth_api_base_url if auth_api_base_url is not None else None
303+
cfg['streaming_url'] = streaming_api_base_url if streaming_api_base_url is not None else None
304+
cfg['telemetry_api_url'] = telemetry_api_base_url if telemetry_api_base_url is not None else None
305+
300306
http_client = HttpClient(
301307
sdk_url=sdk_url,
302308
events_url=events_url,
@@ -541,9 +547,13 @@ def _build_localhost_factory(cfg):
541547
def get_factory(api_key, **kwargs):
542548
"""Build and return the appropriate factory."""
543549
try:
550+
active_factory_count = 0
551+
redundant_factory_count = 0
544552
_INSTANTIATED_FACTORIES_LOCK.acquire()
545553
if _INSTANTIATED_FACTORIES:
546554
if api_key in _INSTANTIATED_FACTORIES:
555+
redundant_factory_count = redundant_factory_count + 1
556+
active_factory_count = active_factory_count + 1
547557
_LOGGER.warning(
548558
"factory instantiation: You already have %d %s with this API Key. "
549559
"We recommend keeping only one instance of the factory at all times "
@@ -552,6 +562,7 @@ def get_factory(api_key, **kwargs):
552562
'factory' if _INSTANTIATED_FACTORIES[api_key] == 1 else 'factories'
553563
)
554564
else:
565+
active_factory_count = active_factory_count + 1
555566
_LOGGER.warning(
556567
"factory instantiation: You already have an instance of the Split factory. "
557568
"Make sure you definitely want this additional instance. "
@@ -560,6 +571,8 @@ def get_factory(api_key, **kwargs):
560571
)
561572

562573
config = sanitize_config(api_key, kwargs.get('config', {}))
574+
config['redundantFactoryCount'] = redundant_factory_count
575+
config['activeFactoryCount'] = active_factory_count + 1
563576

564577
if config['operationMode'] == 'localhost-standalone':
565578
return _build_localhost_factory(config)

splitio/engine/telemetry.py

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
"""Telemetry engine classes."""
2+
from splitio.storage.inmemmory import InMemoryTelemetryStorage
3+
4+
class TelemetryStorageProducer(object):
5+
"""Telemetry storage producer class."""
6+
7+
def __init__(self, telemetry_storage):
8+
"""Initialize all producer classes."""
9+
self._telemetry_init_producer = TelemetryInitProducer(telemetry_storage)
10+
self._telemetry_evaluation_producer = TelemetryEvaluationProducer(telemetry_storage)
11+
self._telemetry_runtime_producer = TelemetryRuntimeProducer(telemetry_storage)
12+
13+
def get_telemetry_init_producer(self):
14+
"""get init producer instance."""
15+
return self._telemetry_init_producer
16+
17+
def get_telemetry_evaluation_producer(self):
18+
"""get evaluation producer instance."""
19+
return self._telemetry_evaluation_producer
20+
21+
def get_telemetry_runtime_producer(self):
22+
"""get runtime producer instance."""
23+
return self._telemetry_runtime_producer
24+
25+
class TelemetryInitProducer(object):
26+
"""Telemetry init producer class."""
27+
28+
def __init__(self, telemetry_storage):
29+
"""Constructor."""
30+
self._telemetry_storage = telemetry_storage
31+
32+
def record_config(self, config):
33+
"""Record configurations."""
34+
self._telemetry_storage.record_config(config)
35+
36+
def record_ready_time(self, ready_time):
37+
"""Record ready time."""
38+
self._telemetry_storage.record_ready_time(ready_time)
39+
40+
def record_bur_timeout(self):
41+
"""Record block until ready timeout."""
42+
self._telemetry_storage.record_bur_timeout()
43+
44+
def record_non_ready_usage(self):
45+
"""record non-ready usage."""
46+
self._telemetry_storage.record_non_ready_usage()
47+
48+
class TelemetryEvaluationProducer(object):
49+
"""Telemetry evaluation producer class."""
50+
51+
def __init__(self, telemetry_storage):
52+
"""Constructor."""
53+
self._telemetry_storage = telemetry_storage
54+
55+
def record_latency(self, method, latency):
56+
"""Record method latency time."""
57+
self._telemetry_storage.record_latency(method, latency)
58+
59+
def record_exception(self, method):
60+
"""Record method exception time."""
61+
self._telemetry_storage.record_exception(method)
62+
63+
class TelemetryRuntimeProducer(object):
64+
"""Telemetry runtime producer class."""
65+
66+
def __init__(self, telemetry_storage):
67+
"""Constructor."""
68+
self._telemetry_storage = telemetry_storage
69+
70+
def add_tag(self, tag):
71+
"""Record tag string."""
72+
self._telemetry_storage.add_tag(tag)
73+
74+
def record_impression_stats(self, data_type, count):
75+
"""Record impressions stats."""
76+
self._telemetry_storage.record_impression_stats(data_type, count)
77+
78+
def record_event_stats(self, data_type, count):
79+
"""Record events stats."""
80+
self._telemetry_storage.record_event_stats(data_type, count)
81+
82+
def record_suceessful_sync(self, resource, time):
83+
"""Record successful sync."""
84+
self._telemetry_storage.record_suceessful_sync(resource, time)
85+
86+
def record_sync_error(self, resource, status):
87+
"""Record sync error."""
88+
self._telemetry_storage.record_sync_error(resource, status)
89+
90+
def record_sync_latency(self, resource, latency):
91+
"""Record latency time."""
92+
self._telemetry_storage.record_sync_latency(resource, latency)
93+
94+
def record_auth_rejections(self):
95+
"""Record auth rejection."""
96+
self._telemetry_storage.record_auth_rejections()
97+
98+
def record_token_refreshes(self):
99+
"""Record sse token refresh."""
100+
self._telemetry_storage.record_token_refreshes()
101+
102+
def record_streaming_event(self, streaming_event):
103+
"""Record incoming streaming event."""
104+
self._telemetry_storage.record_streaming_event(streaming_event)
105+
106+
def record_session_length(self, session):
107+
"""Record session length."""
108+
self._telemetry_storage.record_session_length(session)
109+
110+
class TelemetryStorageConsumer(object):
111+
"""Telemetry storage consumer class."""
112+
113+
def __init__(self, telemetry_storage):
114+
"""Initialize all consumer classes."""
115+
self._telemetry_init_consumer = TelemetryInitConsumer(telemetry_storage)
116+
self._telemetry_evaluation_consumer = TelemetryEvaluationConsumer(telemetry_storage)
117+
self._telemetry_runtime_consumer = TelemetryRuntimeConsumer(telemetry_storage)
118+
119+
def get_telemetry_init_consumer(self):
120+
"""Get telemetry init instance"""
121+
return self._telemetry_init_consumer
122+
123+
def get_telemetry_evaluation_consumer(self):
124+
"""Get telemetry evaluation instance"""
125+
return self._telemetry_evaluation_consumer
126+
127+
def get_telemetry_runtime_consumer(self):
128+
"""Get telemetry runtime instance"""
129+
return self._telemetry_runtime_consumer
130+
131+
class TelemetryInitConsumer(object):
132+
"""Telemetry init consumer class."""
133+
134+
def __init__(self, telemetry_storage):
135+
"""Constructor."""
136+
self._telemetry_storage = telemetry_storage
137+
138+
def get_bur_timeouts(self):
139+
"""Get block until ready timeout."""
140+
return self._telemetry_storage.get_bur_timeouts()
141+
142+
def get_non_ready_usage(self):
143+
"""Get none-ready usage."""
144+
return self._telemetry_storage.get_non_ready_usage()
145+
146+
def get_config_stats(self):
147+
"""Get none-ready usage."""
148+
return self._telemetry_storage.get_config_stats()
149+
150+
class TelemetryEvaluationConsumer(object):
151+
"""Telemetry evaluation consumer class."""
152+
153+
def __init__(self, telemetry_storage):
154+
"""Constructor."""
155+
self._telemetry_storage = telemetry_storage
156+
157+
def pop_exceptions(self):
158+
"""Get and reset method exceptions."""
159+
return self._telemetry_storage.pop_exceptions()
160+
161+
def pop_latencies(self):
162+
"""Get and reset eval latencies."""
163+
return self._telemetry_storage.pop_latencies()
164+
165+
class TelemetryRuntimeConsumer(object):
166+
"""Telemetry runtime consumer class."""
167+
168+
def __init__(self, telemetry_storage):
169+
"""Constructor."""
170+
self._telemetry_storage = telemetry_storage
171+
172+
def get_impressions_stats(self, type):
173+
"""Get impressions stats"""
174+
return self._telemetry_storage.get_impressions_stats(type)
175+
176+
def get_events_stats(self, type):
177+
"""Get events stats"""
178+
return self._telemetry_storage.get_events_stats(type)
179+
180+
def get_last_synchronization(self):
181+
"""Get last sync"""
182+
return self._telemetry_storage.get_last_synchronization()
183+
184+
def pop_tags(self):
185+
"""Get and reset http errors."""
186+
return self._telemetry_storage.pop_tags()
187+
188+
def pop_http_errors(self):
189+
"""Get and reset http errors."""
190+
return self._telemetry_storage.pop_http_errors()
191+
192+
def pop_http_latencies(self):
193+
"""Get and reset http latencies."""
194+
return self._telemetry_storage.pop_http_latencies()
195+
196+
def pop_auth_rejections(self):
197+
"""Get and reset auth rejections."""
198+
return self._telemetry_storage.pop_auth_rejections()
199+
200+
def pop_token_refreshes(self):
201+
"""Get and reset token refreshes."""
202+
return self._telemetry_storage.pop_token_refreshes()
203+
204+
def pop_streaming_events(self):
205+
"""Get and reset streaming events."""
206+
return self._telemetry_storage.pop_streaming_events()
207+
208+
def get_session_length(self):
209+
"""Get session length"""
210+
return self._telemetry_storage.get_session_length()

splitio/storage/__init__.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,54 @@ def clear(self):
283283
Clear data.
284284
"""
285285
pass
286+
287+
class TelemetryStorage(object, metaclass=abc.ABCMeta):
288+
"""Telemetry storage interface."""
289+
290+
@abc.abstractmethod
291+
def record_init(self, config):
292+
"""
293+
initilize telemetry objects
294+
295+
:param congif: factory configuration parameters
296+
:type config: splitio.client.config
297+
"""
298+
pass
299+
300+
@abc.abstractmethod
301+
def record_latency(self, method, latency):
302+
"""
303+
record latency data
304+
305+
:param method: method name
306+
:type method: string
307+
:param latency: latency
308+
:type latency: int64
309+
"""
310+
pass
311+
312+
@abc.abstractmethod
313+
def record_exception(self, method):
314+
"""
315+
record an exception
316+
317+
:param method: method name
318+
:type method: string
319+
"""
320+
pass
321+
322+
@abc.abstractmethod
323+
def record_not_ready_usage(self):
324+
"""
325+
record not ready time
326+
327+
"""
328+
pass
329+
330+
@abc.abstractmethod
331+
def record_bur_time_out(self):
332+
"""
333+
record BUR timeouts
334+
335+
"""
336+
pass

0 commit comments

Comments
 (0)