aiqa-client 0.1.1__py3-none-any.whl → 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aiqa/__init__.py +1 -1
- aiqa/aiqa_exporter.py +148 -47
- aiqa/tracing.py +65 -71
- {aiqa_client-0.1.1.dist-info → aiqa_client-0.1.3.dist-info}/METADATA +2 -1
- aiqa_client-0.1.3.dist-info/RECORD +9 -0
- aiqa_client-0.1.1.dist-info/RECORD +0 -9
- {aiqa_client-0.1.1.dist-info → aiqa_client-0.1.3.dist-info}/WHEEL +0 -0
- {aiqa_client-0.1.1.dist-info → aiqa_client-0.1.3.dist-info}/licenses/LICENSE +0 -0
- {aiqa_client-0.1.1.dist-info → aiqa_client-0.1.3.dist-info}/top_level.txt +0 -0
aiqa/__init__.py
CHANGED
aiqa/aiqa_exporter.py
CHANGED
|
@@ -149,18 +149,65 @@ class AIQASpanExporter(SpanExporter):
|
|
|
149
149
|
nanos = int(nanoseconds % 1_000_000_000)
|
|
150
150
|
return (seconds, nanos)
|
|
151
151
|
|
|
152
|
+
def _build_request_headers(self) -> Dict[str, str]:
|
|
153
|
+
"""Build HTTP headers for span requests."""
|
|
154
|
+
headers = {"Content-Type": "application/json"}
|
|
155
|
+
if self.api_key:
|
|
156
|
+
headers["Authorization"] = f"ApiKey {self.api_key}"
|
|
157
|
+
return headers
|
|
158
|
+
|
|
159
|
+
def _get_span_url(self) -> str:
|
|
160
|
+
"""Get the URL for sending spans."""
|
|
161
|
+
if not self.server_url:
|
|
162
|
+
raise ValueError("AIQA_SERVER_URL is not set. Cannot send spans to server.")
|
|
163
|
+
return f"{self.server_url}/span"
|
|
164
|
+
|
|
165
|
+
def _is_interpreter_shutdown_error(self, error: Exception) -> bool:
|
|
166
|
+
"""Check if error is due to interpreter shutdown."""
|
|
167
|
+
error_str = str(error)
|
|
168
|
+
return "cannot schedule new futures after" in error_str or "interpreter shutdown" in error_str
|
|
169
|
+
|
|
170
|
+
def _extract_spans_from_buffer(self) -> List[Dict[str, Any]]:
|
|
171
|
+
"""Extract spans from buffer (thread-safe). Returns copy of buffer."""
|
|
172
|
+
with self.buffer_lock:
|
|
173
|
+
return self.buffer[:]
|
|
174
|
+
|
|
175
|
+
def _extract_and_remove_spans_from_buffer(self) -> List[Dict[str, Any]]:
|
|
176
|
+
"""
|
|
177
|
+
Atomically extract and remove all spans from buffer (thread-safe).
|
|
178
|
+
Returns the extracted spans. This prevents race conditions where spans
|
|
179
|
+
are added between extraction and clearing.
|
|
180
|
+
"""
|
|
181
|
+
with self.buffer_lock:
|
|
182
|
+
spans = self.buffer[:]
|
|
183
|
+
self.buffer.clear()
|
|
184
|
+
return spans
|
|
185
|
+
|
|
186
|
+
def _prepend_spans_to_buffer(self, spans: List[Dict[str, Any]]) -> None:
|
|
187
|
+
"""
|
|
188
|
+
Prepend spans back to buffer (thread-safe). Used to restore spans
|
|
189
|
+
if sending fails.
|
|
190
|
+
"""
|
|
191
|
+
with self.buffer_lock:
|
|
192
|
+
self.buffer[:0] = spans
|
|
193
|
+
|
|
194
|
+
def _clear_buffer(self) -> None:
|
|
195
|
+
"""Clear the buffer (thread-safe)."""
|
|
196
|
+
with self.buffer_lock:
|
|
197
|
+
self.buffer.clear()
|
|
198
|
+
|
|
152
199
|
async def flush(self) -> None:
|
|
153
200
|
"""
|
|
154
201
|
Flush buffered spans to the server. Thread-safe: ensures only one flush operation runs at a time.
|
|
202
|
+
Atomically extracts spans to prevent race conditions with concurrent export() calls.
|
|
155
203
|
"""
|
|
156
204
|
logger.debug("flush() called - attempting to acquire flush lock")
|
|
157
205
|
with self.flush_lock:
|
|
158
206
|
logger.debug("flush() acquired flush lock")
|
|
159
|
-
#
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
logger.debug(f"flush() extracted {len(spans_to_flush)} span(s) from buffer")
|
|
207
|
+
# Atomically extract and remove spans to prevent race conditions
|
|
208
|
+
# where export() adds spans between extraction and clearing
|
|
209
|
+
spans_to_flush = self._extract_and_remove_spans_from_buffer()
|
|
210
|
+
logger.debug(f"flush() extracted {len(spans_to_flush)} span(s) from buffer")
|
|
164
211
|
|
|
165
212
|
if not spans_to_flush:
|
|
166
213
|
logger.debug("flush() completed: no spans to flush")
|
|
@@ -171,14 +218,33 @@ class AIQASpanExporter(SpanExporter):
|
|
|
171
218
|
logger.warning(
|
|
172
219
|
f"Skipping flush: AIQA_SERVER_URL is not set. {len(spans_to_flush)} span(s) will not be sent."
|
|
173
220
|
)
|
|
221
|
+
# Spans already removed from buffer, nothing to clear
|
|
174
222
|
return
|
|
175
223
|
|
|
176
224
|
logger.info(f"flush() sending {len(spans_to_flush)} span(s) to server")
|
|
177
225
|
try:
|
|
178
226
|
await self._send_spans(spans_to_flush)
|
|
179
227
|
logger.info(f"flush() successfully sent {len(spans_to_flush)} span(s) to server")
|
|
228
|
+
# Spans already removed from buffer during extraction
|
|
229
|
+
except RuntimeError as error:
|
|
230
|
+
if self._is_interpreter_shutdown_error(error):
|
|
231
|
+
if self.shutdown_requested:
|
|
232
|
+
logger.debug(f"flush() skipped due to interpreter shutdown: {error}")
|
|
233
|
+
# Put spans back for retry with sync send during shutdown
|
|
234
|
+
self._prepend_spans_to_buffer(spans_to_flush)
|
|
235
|
+
else:
|
|
236
|
+
logger.warning(f"flush() interrupted by interpreter shutdown: {error}")
|
|
237
|
+
# Put spans back for retry
|
|
238
|
+
self._prepend_spans_to_buffer(spans_to_flush)
|
|
239
|
+
raise
|
|
240
|
+
logger.error(f"Error flushing spans to server: {error}", exc_info=True)
|
|
241
|
+
# Put spans back for retry
|
|
242
|
+
self._prepend_spans_to_buffer(spans_to_flush)
|
|
243
|
+
raise
|
|
180
244
|
except Exception as error:
|
|
181
245
|
logger.error(f"Error flushing spans to server: {error}", exc_info=True)
|
|
246
|
+
# Put spans back for retry
|
|
247
|
+
self._prepend_spans_to_buffer(spans_to_flush)
|
|
182
248
|
if self.shutdown_requested:
|
|
183
249
|
raise
|
|
184
250
|
|
|
@@ -211,17 +277,17 @@ class AIQASpanExporter(SpanExporter):
|
|
|
211
277
|
|
|
212
278
|
logger.info(f"Auto-flush worker thread stopping (shutdown requested). Completed {cycle_count} cycles.")
|
|
213
279
|
|
|
214
|
-
#
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
logger.error(f"Error in final flush: {e}", exc_info=True)
|
|
222
|
-
finally:
|
|
280
|
+
# Don't do final flush here - shutdown() will handle it with synchronous send
|
|
281
|
+
# This avoids event loop shutdown issues
|
|
282
|
+
logger.debug("Auto-flush thread skipping final flush (will be handled by shutdown() with sync send)")
|
|
283
|
+
|
|
284
|
+
# Close the event loop
|
|
285
|
+
try:
|
|
286
|
+
if not loop.is_closed():
|
|
223
287
|
loop.close()
|
|
224
|
-
|
|
288
|
+
logger.debug("Auto-flush worker thread event loop closed")
|
|
289
|
+
except Exception:
|
|
290
|
+
pass # Ignore errors during cleanup
|
|
225
291
|
|
|
226
292
|
flush_thread = threading.Thread(target=flush_worker, daemon=True, name="AIQA-AutoFlush")
|
|
227
293
|
flush_thread.start()
|
|
@@ -229,20 +295,13 @@ class AIQASpanExporter(SpanExporter):
|
|
|
229
295
|
logger.info(f"Auto-flush thread started: {flush_thread.name} (daemon={flush_thread.daemon})")
|
|
230
296
|
|
|
231
297
|
async def _send_spans(self, spans: List[Dict[str, Any]]) -> None:
|
|
232
|
-
"""Send spans to the server API."""
|
|
233
|
-
if not self.server_url:
|
|
234
|
-
raise ValueError("AIQA_SERVER_URL is not set. Cannot send spans to server.")
|
|
235
|
-
|
|
298
|
+
"""Send spans to the server API (async)."""
|
|
236
299
|
import aiohttp
|
|
237
300
|
|
|
238
|
-
url =
|
|
301
|
+
url = self._get_span_url()
|
|
302
|
+
headers = self._build_request_headers()
|
|
239
303
|
logger.debug(f"_send_spans() sending {len(spans)} spans to {url}")
|
|
240
|
-
|
|
241
|
-
headers = {
|
|
242
|
-
"Content-Type": "application/json",
|
|
243
|
-
}
|
|
244
304
|
if self.api_key:
|
|
245
|
-
headers["Authorization"] = f"ApiKey {self.api_key[:10]}..." # Log partial key for security
|
|
246
305
|
logger.debug("_send_spans() using API key authentication")
|
|
247
306
|
else:
|
|
248
307
|
logger.debug("_send_spans() no API key provided")
|
|
@@ -250,11 +309,7 @@ class AIQASpanExporter(SpanExporter):
|
|
|
250
309
|
try:
|
|
251
310
|
async with aiohttp.ClientSession() as session:
|
|
252
311
|
logger.debug(f"_send_spans() POST request starting to {url}")
|
|
253
|
-
async with session.post(
|
|
254
|
-
url,
|
|
255
|
-
json=spans,
|
|
256
|
-
headers=headers,
|
|
257
|
-
) as response:
|
|
312
|
+
async with session.post(url, json=spans, headers=headers) as response:
|
|
258
313
|
logger.debug(f"_send_spans() received response: status={response.status}")
|
|
259
314
|
if not response.ok:
|
|
260
315
|
error_text = await response.text()
|
|
@@ -266,10 +321,48 @@ class AIQASpanExporter(SpanExporter):
|
|
|
266
321
|
f"Failed to send spans: {response.status} {response.reason} - {error_text}"
|
|
267
322
|
)
|
|
268
323
|
logger.debug(f"_send_spans() successfully sent {len(spans)} spans")
|
|
324
|
+
except RuntimeError as e:
|
|
325
|
+
if self._is_interpreter_shutdown_error(e):
|
|
326
|
+
if self.shutdown_requested:
|
|
327
|
+
logger.debug(f"_send_spans() skipped due to interpreter shutdown: {e}")
|
|
328
|
+
else:
|
|
329
|
+
logger.warning(f"_send_spans() interrupted by interpreter shutdown: {e}")
|
|
330
|
+
raise
|
|
331
|
+
logger.error(f"_send_spans() RuntimeError: {type(e).__name__}: {e}", exc_info=True)
|
|
332
|
+
raise
|
|
269
333
|
except Exception as e:
|
|
270
334
|
logger.error(f"_send_spans() exception: {type(e).__name__}: {e}", exc_info=True)
|
|
271
335
|
raise
|
|
272
336
|
|
|
337
|
+
def _send_spans_sync(self, spans: List[Dict[str, Any]]) -> None:
|
|
338
|
+
"""Send spans to the server API (synchronous, for shutdown scenarios)."""
|
|
339
|
+
import requests
|
|
340
|
+
|
|
341
|
+
url = self._get_span_url()
|
|
342
|
+
headers = self._build_request_headers()
|
|
343
|
+
logger.debug(f"_send_spans_sync() sending {len(spans)} spans to {url}")
|
|
344
|
+
if self.api_key:
|
|
345
|
+
logger.debug("_send_spans_sync() using API key authentication")
|
|
346
|
+
else:
|
|
347
|
+
logger.debug("_send_spans_sync() no API key provided")
|
|
348
|
+
|
|
349
|
+
try:
|
|
350
|
+
response = requests.post(url, json=spans, headers=headers, timeout=10.0)
|
|
351
|
+
logger.debug(f"_send_spans_sync() received response: status={response.status_code}")
|
|
352
|
+
if not response.ok:
|
|
353
|
+
error_text = response.text[:200] if response.text else ""
|
|
354
|
+
logger.error(
|
|
355
|
+
f"_send_spans_sync() failed: status={response.status_code}, "
|
|
356
|
+
f"reason={response.reason}, error={error_text}"
|
|
357
|
+
)
|
|
358
|
+
raise Exception(
|
|
359
|
+
f"Failed to send spans: {response.status_code} {response.reason} - {error_text}"
|
|
360
|
+
)
|
|
361
|
+
logger.debug(f"_send_spans_sync() successfully sent {len(spans)} spans")
|
|
362
|
+
except Exception as e:
|
|
363
|
+
logger.error(f"_send_spans_sync() exception: {type(e).__name__}: {e}", exc_info=True)
|
|
364
|
+
raise
|
|
365
|
+
|
|
273
366
|
def shutdown(self) -> None:
|
|
274
367
|
"""Shutdown the exporter, flushing any remaining spans. Call before process exit."""
|
|
275
368
|
logger.info("shutdown() called - initiating exporter shutdown")
|
|
@@ -291,24 +384,32 @@ class AIQASpanExporter(SpanExporter):
|
|
|
291
384
|
else:
|
|
292
385
|
logger.debug("shutdown() no active auto-flush thread to wait for")
|
|
293
386
|
|
|
294
|
-
# Final flush attempt (synchronous)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
387
|
+
# Final flush attempt (use synchronous send to avoid event loop issues)
|
|
388
|
+
with self.flush_lock:
|
|
389
|
+
logger.debug("shutdown() performing final flush with synchronous send")
|
|
390
|
+
# Atomically extract and remove spans to prevent race conditions
|
|
391
|
+
spans_to_flush = self._extract_and_remove_spans_from_buffer()
|
|
392
|
+
logger.debug(f"shutdown() extracted {len(spans_to_flush)} span(s) from buffer for final flush")
|
|
393
|
+
|
|
394
|
+
if spans_to_flush:
|
|
395
|
+
if not self.server_url:
|
|
396
|
+
logger.warning(
|
|
397
|
+
f"shutdown() skipping final flush: AIQA_SERVER_URL is not set. "
|
|
398
|
+
f"{len(spans_to_flush)} span(s) will not be sent."
|
|
399
|
+
)
|
|
400
|
+
# Spans already removed from buffer
|
|
401
|
+
else:
|
|
402
|
+
logger.info(f"shutdown() sending {len(spans_to_flush)} span(s) to server (synchronous)")
|
|
403
|
+
try:
|
|
404
|
+
self._send_spans_sync(spans_to_flush)
|
|
405
|
+
logger.info(f"shutdown() successfully sent {len(spans_to_flush)} span(s) to server")
|
|
406
|
+
# Spans already removed from buffer during extraction
|
|
407
|
+
except Exception as e:
|
|
408
|
+
logger.error(f"shutdown() failed to send spans: {e}", exc_info=True)
|
|
409
|
+
# Spans already removed, but process is exiting anyway
|
|
410
|
+
logger.warning(f"shutdown() {len(spans_to_flush)} span(s) were not sent due to error")
|
|
305
411
|
else:
|
|
306
|
-
logger.debug("shutdown()
|
|
307
|
-
loop.run_until_complete(self.flush())
|
|
308
|
-
except RuntimeError:
|
|
309
|
-
# No event loop, create one
|
|
310
|
-
logger.debug("shutdown() no event loop found, creating new one for final flush")
|
|
311
|
-
asyncio.run(self.flush())
|
|
412
|
+
logger.debug("shutdown() no spans to flush")
|
|
312
413
|
|
|
313
414
|
# Check buffer state after shutdown
|
|
314
415
|
with self.buffer_lock:
|
aiqa/tracing.py
CHANGED
|
@@ -175,8 +175,6 @@ def WithTracing(
|
|
|
175
175
|
if is_async:
|
|
176
176
|
@wraps(fn)
|
|
177
177
|
async def async_traced_fn(*args, **kwargs):
|
|
178
|
-
span = tracer.start_span(fn_name)
|
|
179
|
-
|
|
180
178
|
# Prepare input
|
|
181
179
|
input_data = _prepare_input(args, kwargs)
|
|
182
180
|
if filter_input:
|
|
@@ -186,41 +184,40 @@ def WithTracing(
|
|
|
186
184
|
if key in input_data:
|
|
187
185
|
del input_data[key]
|
|
188
186
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
# Call the function within the span context
|
|
196
|
-
trace_id = format(span.get_span_context().trace_id, "032x")
|
|
197
|
-
logger.debug(f"do traceable stuff {fn_name} {trace_id}")
|
|
187
|
+
# Use start_as_current_span to ensure span is recorded by BatchSpanProcessor
|
|
188
|
+
with tracer.start_as_current_span(fn_name) as span:
|
|
189
|
+
if input_data is not None:
|
|
190
|
+
# Serialize for span attributes (OpenTelemetry only accepts primitives or JSON strings)
|
|
191
|
+
serialized_input = _serialize_for_span(input_data)
|
|
192
|
+
span.set_attribute("input", serialized_input)
|
|
198
193
|
|
|
199
|
-
|
|
194
|
+
try:
|
|
195
|
+
# Call the function within the span context
|
|
196
|
+
trace_id = format(span.get_span_context().trace_id, "032x")
|
|
197
|
+
logger.debug(f"do traceable stuff {fn_name} {trace_id}")
|
|
198
|
+
|
|
200
199
|
result = await fn(*args, **kwargs)
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
finally:
|
|
223
|
-
span.end()
|
|
200
|
+
|
|
201
|
+
# Prepare output
|
|
202
|
+
output_data = result
|
|
203
|
+
if filter_output:
|
|
204
|
+
output_data = filter_output(output_data)
|
|
205
|
+
if ignore_output and isinstance(output_data, dict):
|
|
206
|
+
# Make a copy of output_data to avoid modifying the original
|
|
207
|
+
output_data = output_data.copy()
|
|
208
|
+
for key in ignore_output:
|
|
209
|
+
if key in output_data:
|
|
210
|
+
del output_data[key]
|
|
211
|
+
|
|
212
|
+
span.set_attribute("output", _serialize_for_span(output_data))
|
|
213
|
+
span.set_status(Status(StatusCode.OK))
|
|
214
|
+
|
|
215
|
+
return result
|
|
216
|
+
except Exception as exception:
|
|
217
|
+
error = exception if isinstance(exception, Exception) else Exception(str(exception))
|
|
218
|
+
span.record_exception(error)
|
|
219
|
+
span.set_status(Status(StatusCode.ERROR, str(error)))
|
|
220
|
+
raise
|
|
224
221
|
|
|
225
222
|
async_traced_fn._is_traced = True
|
|
226
223
|
logger.debug(f"Function {fn_name} is now traced (async)")
|
|
@@ -228,8 +225,6 @@ def WithTracing(
|
|
|
228
225
|
else:
|
|
229
226
|
@wraps(fn)
|
|
230
227
|
def sync_traced_fn(*args, **kwargs):
|
|
231
|
-
span = tracer.start_span(fn_name)
|
|
232
|
-
|
|
233
228
|
# Prepare input
|
|
234
229
|
input_data = _prepare_input(args, kwargs)
|
|
235
230
|
if filter_input:
|
|
@@ -239,41 +234,40 @@ def WithTracing(
|
|
|
239
234
|
if key in input_data:
|
|
240
235
|
del input_data[key]
|
|
241
236
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
# Call the function within the span context
|
|
249
|
-
trace_id = format(span.get_span_context().trace_id, "032x")
|
|
250
|
-
logger.debug(f"do traceable stuff {fn_name} {trace_id}")
|
|
237
|
+
# Use start_as_current_span to ensure span is recorded by BatchSpanProcessor
|
|
238
|
+
with tracer.start_as_current_span(fn_name) as span:
|
|
239
|
+
if input_data is not None:
|
|
240
|
+
# Serialize for span attributes (OpenTelemetry only accepts primitives or JSON strings)
|
|
241
|
+
serialized_input = _serialize_for_span(input_data)
|
|
242
|
+
span.set_attribute("input", serialized_input)
|
|
251
243
|
|
|
252
|
-
|
|
244
|
+
try:
|
|
245
|
+
# Call the function within the span context
|
|
246
|
+
trace_id = format(span.get_span_context().trace_id, "032x")
|
|
247
|
+
logger.debug(f"do traceable stuff {fn_name} {trace_id}")
|
|
248
|
+
|
|
253
249
|
result = fn(*args, **kwargs)
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
finally:
|
|
276
|
-
span.end()
|
|
250
|
+
|
|
251
|
+
# Prepare output
|
|
252
|
+
output_data = result
|
|
253
|
+
if filter_output:
|
|
254
|
+
output_data = filter_output(output_data)
|
|
255
|
+
if ignore_output and isinstance(output_data, dict):
|
|
256
|
+
# Make a copy of output_data to avoid modifying the original
|
|
257
|
+
output_data = output_data.copy()
|
|
258
|
+
for key in ignore_output:
|
|
259
|
+
if key in output_data:
|
|
260
|
+
del output_data[key]
|
|
261
|
+
|
|
262
|
+
span.set_attribute("output", _serialize_for_span(output_data))
|
|
263
|
+
span.set_status(Status(StatusCode.OK))
|
|
264
|
+
|
|
265
|
+
return result
|
|
266
|
+
except Exception as exception:
|
|
267
|
+
error = exception if isinstance(exception, Exception) else Exception(str(exception))
|
|
268
|
+
span.record_exception(error)
|
|
269
|
+
span.set_status(Status(StatusCode.ERROR, str(error)))
|
|
270
|
+
raise
|
|
277
271
|
|
|
278
272
|
sync_traced_fn._is_traced = True
|
|
279
273
|
logger.debug(f"Function {fn_name} is now traced (sync)")
|
|
@@ -305,7 +299,7 @@ def set_span_name(span_name: str) -> bool:
|
|
|
305
299
|
"""
|
|
306
300
|
span = trace.get_current_span()
|
|
307
301
|
if span and span.is_recording():
|
|
308
|
-
span.
|
|
302
|
+
span.update_name(span_name)
|
|
309
303
|
return True
|
|
310
304
|
return False
|
|
311
305
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aiqa-client
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.3
|
|
4
4
|
Summary: OpenTelemetry-based Python client for tracing functions and sending traces to the AIQA server
|
|
5
5
|
Author-email: AIQA <info@aiqa.dev>
|
|
6
6
|
License: MIT
|
|
@@ -27,6 +27,7 @@ Requires-Dist: opentelemetry-api>=1.24.0
|
|
|
27
27
|
Requires-Dist: opentelemetry-sdk>=1.24.0
|
|
28
28
|
Requires-Dist: opentelemetry-semantic-conventions>=0.40b0
|
|
29
29
|
Requires-Dist: aiohttp>=3.9.0
|
|
30
|
+
Requires-Dist: requests>=2.31.0
|
|
30
31
|
Provides-Extra: dev
|
|
31
32
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
32
33
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
aiqa/__init__.py,sha256=mQWkldjxytAru66UBU5aRiENpR9hPsqxY5FBSqLwS60,470
|
|
2
|
+
aiqa/aiqa_exporter.py,sha256=vXyX6Q_iOjrDz3tCPOMXuBTQg7ocACdOOqzpkUqhy9g,19131
|
|
3
|
+
aiqa/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
aiqa/tracing.py,sha256=TnKkasn9ESofNMzztTMAOhs_OfD6WZj1KS1U2Ikbypk,12084
|
|
5
|
+
aiqa_client-0.1.3.dist-info/licenses/LICENSE,sha256=kIzkzLuzG0HHaWYm4F4W5FeJ1Yxut3Ec6bhLWyw798A,1062
|
|
6
|
+
aiqa_client-0.1.3.dist-info/METADATA,sha256=lqRVBC0F7vbpeC0akkvRcn_c84vkwTLuwCzX6FTzpDs,3772
|
|
7
|
+
aiqa_client-0.1.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
+
aiqa_client-0.1.3.dist-info/top_level.txt,sha256=nwcsuVVSuWu27iLxZd4n1evVzv1W6FVTrSnCXCc-NQs,5
|
|
9
|
+
aiqa_client-0.1.3.dist-info/RECORD,,
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
aiqa/__init__.py,sha256=LEONMsfGaQXePRZN9XxdULWsufbfJnTJ1t1-LU9c-9o,470
|
|
2
|
-
aiqa/aiqa_exporter.py,sha256=Y0VrZqnb3LG4WSb9XQYct9ABwunG04T9aM8I6EH0qQQ,13781
|
|
3
|
-
aiqa/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
aiqa/tracing.py,sha256=RwcHk9P_dKoQUv545VVHqnJ4x8jaZkqA4YhdVUXGywc,11889
|
|
5
|
-
aiqa_client-0.1.1.dist-info/licenses/LICENSE,sha256=kIzkzLuzG0HHaWYm4F4W5FeJ1Yxut3Ec6bhLWyw798A,1062
|
|
6
|
-
aiqa_client-0.1.1.dist-info/METADATA,sha256=NcmuXu5XyQsq2X-MRn_qaq4pCgnwH-1khBuZruYAwKQ,3740
|
|
7
|
-
aiqa_client-0.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
8
|
-
aiqa_client-0.1.1.dist-info/top_level.txt,sha256=nwcsuVVSuWu27iLxZd4n1evVzv1W6FVTrSnCXCc-NQs,5
|
|
9
|
-
aiqa_client-0.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|