1- from track .utils import require , stringify
2- from track .request import post
3- from track .const import ApiPaths
41import logging
52import numbers
3+
4+ from track .const import ApiPaths
5+ from track .consumer import Consumer
6+ from track .request import post
7+ from track .utils import require , stringify
8+
9+ try :
10+ import queue
11+ except ImportError :
12+ import Queue as queue
13+
614ID_TYPES = (numbers .Number , str )
715
816
917class Client (object ):
1018 logger = logging .getLogger ('interakt' )
1119
12- def __init__ (self , write_key = None , host = None , debug = False , sync_mode = True , timeout = 10 ):
20+ def __init__ (self , write_key = None , host = None , debug = False ,
21+ sync_mode = True , timeout = 10 , max_queue_size = 10000 , on_error = None , max_retries = 3 ):
1322 """Create a new interakt client"""
1423 require ('write_key' , write_key , str )
24+
25+ self .queue = queue .Queue (maxsize = max_queue_size )
1526 self .write_key = write_key
1627 self .debug = debug
1728 self .host = host
1829 self .sync_mode = sync_mode
1930 self .timeout = timeout
31+ self .on_error = on_error
2032 if debug :
2133 self .logger .setLevel (logging .DEBUG )
2234
35+ if sync_mode :
36+ self .consumer = None
37+ else :
38+ self .consumer = Consumer (
39+ queue = self .queue , write_key = write_key ,
40+ host = host , on_error = on_error , retries = max_retries ,
41+ timeout = timeout
42+ )
43+
2344 def identify (self , user_id = None , country_code = '+91' , phone_number = None , traits = {}):
2445 """Tie a user to their actions and record traits about them."""
2546 if not user_id and not phone_number :
@@ -29,26 +50,64 @@ def identify(self, user_id=None, country_code='+91', phone_number=None, traits={
2950 if phone_number :
3051 require ('phone_number' , phone_number , str )
3152 require ('traits' , traits , dict )
32- msg = {
53+ body = {
3354 'userId' : stringify (val = user_id ),
3455 'countryCode' : country_code ,
3556 'phoneNumber' : phone_number ,
3657 'traits' : traits
3758 }
38- return self .__send_request (path = ApiPaths .Identify .value , msg = msg )
59+ return self .__queue_request (path = ApiPaths .Identify .value , body = body )
3960
4061 def event (self , user_id = None , event = None , traits = {}):
4162 """To record user events"""
4263 traits = traits or {}
4364 require ('user_id' , user_id , ID_TYPES )
4465 require ('traits' , traits , dict )
4566 require ('event' , event , str )
46- msg = {
67+ body = {
4768 'userId' : stringify (val = user_id ),
4869 'event' : event ,
4970 'traits' : traits
5071 }
51- return self .__send_request (path = ApiPaths .Event .value , msg = msg )
72+ return self .__queue_request (path = ApiPaths .Event .value , body = body )
73+
74+ def flush (self ):
75+ """Forces a flush from the internal queue to the server"""
76+ queue = self .queue
77+ size = queue .qsize ()
78+ queue .join ()
79+ # Note that this message may not be precise, because of threading.
80+ self .logger .debug ('successfully flushed about %s items.' , size )
5281
53- def __send_request (self , path , msg ):
54- return post (self .write_key , host = self .host , path = path , body = msg , timeout = self .timeout )
82+ def join (self ):
83+ """Ends the consumer thread once the queue is empty.
84+ Blocks execution until finished
85+ """
86+ self .consumer .pause ()
87+ try :
88+ self .consumer .join ()
89+ except RuntimeError :
90+ # consumer thread has not started
91+ pass
92+
93+ def shutdown (self ):
94+ """Flush all messages and cleanly shutdown the client"""
95+ self .flush ()
96+ self .join ()
97+
98+ def __queue_request (self , path , body ):
99+ # Directly call api in sync mode and return response
100+ if self .sync_mode :
101+ return post (self .write_key , host = self .host , path = path , body = body , timeout = self .timeout )
102+
103+ queue_msg = {
104+ 'path' : path ,
105+ 'body' : body
106+ }
107+ try :
108+ self .queue .put (queue_msg , block = False )
109+ self .logger .debug (f'Enqueued msg for { path } ' )
110+ return True , queue_msg
111+ except queue .Full :
112+ self .logger .warning ('track-python queue is full' )
113+ return False , queue_msg
0 commit comments