11from api_to_dataframe .models .retainer import retry_strategies , Strategies
22from api_to_dataframe .models .get_data import GetData
3- from api_to_dataframe .utils .logger import logger
4- from otel_wrapper import OpenObservability
3+ from api_to_dataframe .utils .logger import logger , telemetry
4+ import time
55
66
77class ClientBuilder :
@@ -35,16 +35,40 @@ def __init__( # pylint: disable=too-many-positional-arguments,too-many-argument
3535 if headers is None :
3636 headers = {}
3737 if endpoint == "" :
38- logger .error ("endpoint cannot be an empty string" )
38+ error_msg = "endpoint cannot be an empty string"
39+ logger .error (error_msg )
40+ telemetry .logs ().new_log (
41+ msg = error_msg ,
42+ tags = {"component" : "ClientBuilder" , "method" : "__init__" },
43+ level = 40 # ERROR level
44+ )
3945 raise ValueError
4046 if not isinstance (retries , int ) or retries < 0 :
41- logger .error ("retries must be a non-negative integer" )
47+ error_msg = "retries must be a non-negative integer"
48+ logger .error (error_msg )
49+ telemetry .logs ().new_log (
50+ msg = error_msg ,
51+ tags = {"component" : "ClientBuilder" , "method" : "__init__" },
52+ level = 40 # ERROR level
53+ )
4254 raise ValueError
4355 if not isinstance (initial_delay , int ) or initial_delay < 0 :
44- logger .error ("initial_delay must be a non-negative integer" )
56+ error_msg = "initial_delay must be a non-negative integer"
57+ logger .error (error_msg )
58+ telemetry .logs ().new_log (
59+ msg = error_msg ,
60+ tags = {"component" : "ClientBuilder" , "method" : "__init__" },
61+ level = 40 # ERROR level
62+ )
4563 raise ValueError
4664 if not isinstance (connection_timeout , int ) or connection_timeout < 0 :
47- logger .error ("connection_timeout must be a non-negative integer" )
65+ error_msg = "connection_timeout must be a non-negative integer"
66+ logger .error (error_msg )
67+ telemetry .logs ().new_log (
68+ msg = error_msg ,
69+ tags = {"component" : "ClientBuilder" , "method" : "__init__" },
70+ level = 40 # ERROR level
71+ )
4872 raise ValueError
4973
5074 self .endpoint = endpoint
@@ -53,9 +77,28 @@ def __init__( # pylint: disable=too-many-positional-arguments,too-many-argument
5377 self .headers = headers
5478 self .retries = retries
5579 self .delay = initial_delay
56- self ._o11y_wrapper = OpenObservability (application_name = "api-to-dataframe" ).get_wrapper ()
57- self ._traces = self ._o11y_wrapper .traces ()
58- self ._tracer = self ._traces .get_tracer ()
80+
81+ # Record client initialization metric
82+ telemetry .metrics ().metric_increment (
83+ name = "client.initialization" ,
84+ tags = {
85+ "endpoint" : endpoint ,
86+ "retry_strategy" : retry_strategy .name ,
87+ "connection_timeout" : str (connection_timeout )
88+ }
89+ )
90+
91+ # Log initialization
92+ telemetry .logs ().new_log (
93+ msg = f"ClientBuilder initialized with endpoint { endpoint } " ,
94+ tags = {
95+ "endpoint" : endpoint ,
96+ "retry_strategy" : retry_strategy .name ,
97+ "connection_timeout" : str (connection_timeout ),
98+ "component" : "ClientBuilder"
99+ },
100+ level = 20 # INFO level
101+ )
59102
60103 @retry_strategies
61104 def get_api_data (self ):
@@ -69,16 +112,62 @@ def get_api_data(self):
69112 Returns:
70113 dict: The JSON response from the API as a dictionary.
71114 """
72-
73- with self ._tracer .start_as_current_span ("get_last_quote" ) as span :
115+ # Use the telemetry spans with context manager
116+ with telemetry .traces ().span_in_context ("get_api_data" ) as (span , _ ):
117+ # Add span attributes
74118 span .set_attribute ("endpoint" , self .endpoint )
75-
119+ span .set_attribute ("retry_strategy" , self .retry_strategy .name )
120+ span .set_attribute ("connection_timeout" , self .connection_timeout )
121+
122+ # Log the API request
123+ telemetry .logs ().new_log (
124+ msg = f"Making API request to { self .endpoint } " ,
125+ tags = {
126+ "endpoint" : self .endpoint ,
127+ "component" : "ClientBuilder" ,
128+ "method" : "get_api_data"
129+ },
130+ level = 20 # INFO level
131+ )
132+
133+ # Record the start time for response time measurement
134+ start_time = time .time ()
135+
136+ # Make the API request
76137 response = GetData .get_response (
77138 endpoint = self .endpoint ,
78139 headers = self .headers ,
79140 connection_timeout = self .connection_timeout ,
80141 )
81-
142+
143+ # Calculate response time
144+ response_time = time .time () - start_time
145+
146+ # Record response time as histogram
147+ telemetry .metrics ().record_histogram (
148+ name = "api.response_time" ,
149+ tags = {"endpoint" : self .endpoint },
150+ value = response_time
151+ )
152+
153+ # Record successful request metric
154+ telemetry .metrics ().metric_increment (
155+ name = "api.request.success" ,
156+ tags = {"endpoint" : self .endpoint }
157+ )
158+
159+ # Log success
160+ telemetry .logs ().new_log (
161+ msg = f"API request to { self .endpoint } successful" ,
162+ tags = {
163+ "endpoint" : self .endpoint ,
164+ "response_status" : response .status_code ,
165+ "response_time" : response_time ,
166+ "component" : "ClientBuilder" ,
167+ "method" : "get_api_data"
168+ },
169+ level = 20 # INFO level
170+ )
82171
83172 return response .json ()
84173
@@ -97,7 +186,66 @@ def api_to_dataframe(response: dict):
97186 Returns:
98187 DataFrame: A pandas DataFrame containing the data from the API response.
99188 """
100-
101- df = GetData .to_dataframe (response )
102-
103- return df
189+ # Use telemetry for this operation
190+ with telemetry .traces ().span_in_context ("api_to_dataframe" ) as (span , _ ):
191+ response_size = len (response ) if isinstance (response , list ) else 1
192+ span .set_attribute ("response_size" , response_size )
193+
194+ # Log conversion start
195+ telemetry .logs ().new_log (
196+ msg = "Converting API response to DataFrame" ,
197+ tags = {
198+ "response_size" : response_size ,
199+ "response_type" : type (response ).__name__ ,
200+ "component" : "ClientBuilder" ,
201+ "method" : "api_to_dataframe"
202+ },
203+ level = 20 # INFO level
204+ )
205+
206+ try :
207+ # Convert to dataframe
208+ df = GetData .to_dataframe (response )
209+
210+ # Record metrics
211+ telemetry .metrics ().metric_increment (
212+ name = "dataframe.conversion.success" ,
213+ tags = {"size" : len (df )}
214+ )
215+
216+ # Log success
217+ telemetry .logs ().new_log (
218+ msg = "Successfully converted API response to DataFrame" ,
219+ tags = {
220+ "dataframe_rows" : len (df ),
221+ "dataframe_columns" : len (df .columns ),
222+ "component" : "ClientBuilder" ,
223+ "method" : "api_to_dataframe"
224+ },
225+ level = 20 # INFO level
226+ )
227+
228+ return df
229+
230+ except Exception as e :
231+ # Record failure metric
232+ telemetry .metrics ().metric_increment (
233+ name = "dataframe.conversion.failure" ,
234+ tags = {"error_type" : type (e ).__name__ }
235+ )
236+
237+ # Log error
238+ error_msg = f"Failed to convert API response to DataFrame: { str (e )} "
239+ telemetry .logs ().new_log (
240+ msg = error_msg ,
241+ tags = {
242+ "error" : str (e ),
243+ "error_type" : type (e ).__name__ ,
244+ "component" : "ClientBuilder" ,
245+ "method" : "api_to_dataframe"
246+ },
247+ level = 40 # ERROR level
248+ )
249+
250+ # Re-raise the exception
251+ raise
0 commit comments