monocle-apptrace 0.3.1__py3-none-any.whl → 0.4.0__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.

Potentially problematic release.


This version of monocle-apptrace might be problematic. Click here for more details.

Files changed (38) hide show
  1. monocle_apptrace/exporters/aws/s3_exporter.py +3 -1
  2. monocle_apptrace/exporters/azure/blob_exporter.py +2 -2
  3. monocle_apptrace/exporters/base_exporter.py +10 -4
  4. monocle_apptrace/exporters/file_exporter.py +19 -4
  5. monocle_apptrace/exporters/monocle_exporters.py +3 -3
  6. monocle_apptrace/exporters/okahu/okahu_exporter.py +5 -2
  7. monocle_apptrace/instrumentation/common/constants.py +9 -5
  8. monocle_apptrace/instrumentation/common/instrumentor.py +24 -13
  9. monocle_apptrace/instrumentation/common/span_handler.py +79 -38
  10. monocle_apptrace/instrumentation/common/utils.py +90 -54
  11. monocle_apptrace/instrumentation/common/wrapper.py +193 -40
  12. monocle_apptrace/instrumentation/common/wrapper_method.py +13 -6
  13. monocle_apptrace/instrumentation/metamodel/aiohttp/__init__.py +0 -0
  14. monocle_apptrace/instrumentation/metamodel/aiohttp/_helper.py +66 -0
  15. monocle_apptrace/instrumentation/metamodel/aiohttp/entities/http.py +51 -0
  16. monocle_apptrace/instrumentation/metamodel/aiohttp/methods.py +13 -0
  17. monocle_apptrace/instrumentation/metamodel/flask/_helper.py +8 -3
  18. monocle_apptrace/instrumentation/metamodel/flask/entities/http.py +0 -1
  19. monocle_apptrace/instrumentation/metamodel/haystack/_helper.py +17 -4
  20. monocle_apptrace/instrumentation/metamodel/haystack/entities/inference.py +1 -1
  21. monocle_apptrace/instrumentation/metamodel/haystack/methods.py +8 -1
  22. monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +1 -1
  23. monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +13 -9
  24. monocle_apptrace/instrumentation/metamodel/llamaindex/entities/inference.py +1 -1
  25. monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +14 -0
  26. monocle_apptrace/instrumentation/metamodel/openai/_helper.py +26 -5
  27. monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +184 -26
  28. monocle_apptrace/instrumentation/metamodel/openai/methods.py +6 -6
  29. monocle_apptrace/instrumentation/metamodel/requests/_helper.py +1 -1
  30. monocle_apptrace/instrumentation/metamodel/teamsai/_helper.py +55 -5
  31. monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/actionplanner_output_processor.py +13 -33
  32. monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/teamsai_output_processor.py +24 -20
  33. monocle_apptrace/instrumentation/metamodel/teamsai/methods.py +54 -8
  34. {monocle_apptrace-0.3.1.dist-info → monocle_apptrace-0.4.0.dist-info}/METADATA +22 -18
  35. {monocle_apptrace-0.3.1.dist-info → monocle_apptrace-0.4.0.dist-info}/RECORD +38 -34
  36. {monocle_apptrace-0.3.1.dist-info → monocle_apptrace-0.4.0.dist-info}/WHEEL +0 -0
  37. {monocle_apptrace-0.3.1.dist-info → monocle_apptrace-0.4.0.dist-info}/licenses/LICENSE +0 -0
  38. {monocle_apptrace-0.3.1.dist-info → monocle_apptrace-0.4.0.dist-info}/licenses/NOTICE +0 -0
@@ -1,13 +1,13 @@
1
1
  import logging, json
2
2
  import os
3
+ import traceback
3
4
  from typing import Callable, Generic, Optional, TypeVar, Mapping
4
- import threading, asyncio
5
5
 
6
6
  from opentelemetry.context import attach, detach, get_current, get_value, set_value, Context
7
- from opentelemetry.trace import NonRecordingSpan, Span, get_tracer
7
+ from opentelemetry.trace import NonRecordingSpan, Span
8
8
  from opentelemetry.trace.propagation import _SPAN_KEY
9
9
  from opentelemetry.sdk.trace import id_generator, TracerProvider
10
- from opentelemetry.propagate import inject, extract
10
+ from opentelemetry.propagate import extract
11
11
  from opentelemetry import baggage
12
12
  from monocle_apptrace.instrumentation.common.constants import MONOCLE_SCOPE_NAME_PREFIX, SCOPE_METHOD_FILE, SCOPE_CONFIG_PATH, llm_type_map, MONOCLE_SDK_VERSION, ADD_NEW_WORKFLOW
13
13
  from importlib.metadata import version
@@ -17,7 +17,6 @@ U = TypeVar('U')
17
17
 
18
18
  logger = logging.getLogger(__name__)
19
19
 
20
- monocle_tracer_provider: TracerProvider = None
21
20
  embedding_model_context = {}
22
21
  scope_id_generator = id_generator.RandomIdGenerator()
23
22
  http_scopes:dict[str:str] = {}
@@ -43,14 +42,6 @@ class MonocleSpanException(Exception):
43
42
  """String representation of the exception."""
44
43
  return f"[Monocle Span Error: {self.message} {self.status}"
45
44
 
46
- def set_tracer_provider(tracer_provider: TracerProvider):
47
- global monocle_tracer_provider
48
- monocle_tracer_provider = tracer_provider
49
-
50
- def get_tracer_provider() -> TracerProvider:
51
- global monocle_tracer_provider
52
- return monocle_tracer_provider
53
-
54
45
  def set_span_attribute(span, name, value):
55
46
  if value is not None:
56
47
  if value != "":
@@ -93,7 +84,12 @@ def with_tracer_wrapper(func):
93
84
  except Exception as e:
94
85
  logger.error("Exception in attaching parent context: %s", e)
95
86
 
96
- val = func(tracer, handler, to_wrap, wrapped, instance, args, kwargs)
87
+ if traceback.extract_stack().__len__() > 2:
88
+ filename, line_number, _, _ = traceback.extract_stack()[-2]
89
+ source_path = f"{filename}:{line_number}"
90
+ else:
91
+ source_path = ""
92
+ val = func(tracer, handler, to_wrap, wrapped, instance, source_path, args, kwargs)
97
93
  return val
98
94
 
99
95
  return wrapper
@@ -240,6 +236,13 @@ def set_scopes_from_baggage(baggage_context:Context):
240
236
  scope_name = scope_key[len(MONOCLE_SCOPE_NAME_PREFIX):]
241
237
  set_scope(scope_name, scope_value)
242
238
 
239
+ def get_parent_span() -> Span:
240
+ parent_span: Span = None
241
+ _parent_span_context = get_current()
242
+ if _parent_span_context is not None and _parent_span_context.get(_SPAN_KEY, None):
243
+ parent_span = _parent_span_context.get(_SPAN_KEY, None)
244
+ return parent_span
245
+
243
246
  def extract_http_headers(headers) -> object:
244
247
  global http_scopes
245
248
  trace_context:Context = extract(headers, context=get_current())
@@ -275,49 +278,44 @@ async def http_async_route_handler(func, *args, **kwargs):
275
278
  headers = kwargs['req'].headers
276
279
  else:
277
280
  headers = None
278
- return async_wrapper(func, None, None, headers, *args, **kwargs)
279
-
280
- def run_async_with_scope(method, current_context, exceptions, *args, **kwargs):
281
- token = None
282
281
  try:
283
- if current_context:
284
- token = attach(current_context)
285
- return asyncio.run(method(*args, **kwargs))
286
- except Exception as e:
287
- exceptions['exception'] = e
288
- raise e
282
+ if headers is not None:
283
+ token = extract_http_headers(headers)
284
+ return await func(*args, **kwargs)
289
285
  finally:
290
- if token:
291
- detach(token)
286
+ if token is not None:
287
+ clear_http_scopes(token)
292
288
 
293
- def async_wrapper(method, scope_name=None, scope_value=None, headers=None, *args, **kwargs):
294
- try:
295
- run_loop = asyncio.get_running_loop()
296
- except RuntimeError:
297
- run_loop = None
298
-
299
- token = None
300
- exceptions = {}
301
- if scope_name:
302
- token = set_scope(scope_name, scope_value)
303
- elif headers:
304
- token = extract_http_headers(headers)
305
- current_context = get_current()
306
- try:
307
- if run_loop and run_loop.is_running():
308
- results = []
309
- thread = threading.Thread(target=lambda: results.append(run_async_with_scope(method, current_context, exceptions, *args, **kwargs)))
310
- thread.start()
311
- thread.join()
312
- if 'exception' in exceptions:
313
- raise exceptions['exception']
314
- return_value = results[0] if len(results) > 0 else None
315
- return return_value
316
- else:
317
- return run_async_with_scope(method, None, exceptions, *args, **kwargs)
318
- finally:
319
- if token:
320
- remove_scope(token)
289
+ # def run_async_with_scope(method, current_context, exceptions, *args, **kwargs):
290
+ # token = None
291
+ # try:
292
+ # if current_context:
293
+ # token = attach(current_context)
294
+ # return asyncio.run(method(*args, **kwargs))
295
+ # except Exception as e:
296
+ # exceptions['exception'] = e
297
+ # raise e
298
+ # finally:
299
+ # if token:
300
+ # detach(token)
301
+
302
+ # async def async_wrapper(method, headers=None, *args, **kwargs):
303
+ # current_context = get_current()
304
+ # try:
305
+ # if run_loop and run_loop.is_running():
306
+ # results = []
307
+ # thread = threading.Thread(target=lambda: results.append(run_async_with_scope(method, current_context, exceptions, *args, **kwargs)))
308
+ # thread.start()
309
+ # thread.join()
310
+ # if 'exception' in exceptions:
311
+ # raise exceptions['exception']
312
+ # return_value = results[0] if len(results) > 0 else None
313
+ # return return_value
314
+ # else:
315
+ # return run_async_with_scope(method, None, exceptions, *args, **kwargs)
316
+ # finally:
317
+ # if token:
318
+ # remove_scope(token)
321
319
 
322
320
  def get_monocle_version() -> str:
323
321
  global monocle_sdk_version
@@ -365,7 +363,45 @@ def try_option(func: Callable[..., T], *args, **kwargs) -> Option[T]:
365
363
  def get_llm_type(instance):
366
364
  try:
367
365
  t_name = type(instance).__name__.lower()
368
- llm_type = llm_type_map.get(type(instance).__name__.lower())
366
+ t_name = t_name.replace("async", "") if "async" in t_name else t_name
367
+ llm_type = llm_type_map.get(t_name)
369
368
  return llm_type
370
369
  except:
371
370
  pass
371
+
372
+ def get_status(arguments):
373
+ if arguments['exception'] is not None:
374
+ return 'error'
375
+ else:
376
+ return 'success'
377
+
378
+ def get_exception_status_code(arguments):
379
+ if arguments['exception'] is not None and hasattr(arguments['exception'], 'code'):
380
+ return arguments['exception'].code
381
+ else:
382
+ return 'error'
383
+
384
+ def get_exception_message(arguments):
385
+ if arguments['exception'] is not None:
386
+ if hasattr(arguments['exception'], 'message'):
387
+ return arguments['exception'].message
388
+ else:
389
+ return arguments['exception'].__str__()
390
+ else:
391
+ return ''
392
+
393
+ def patch_instance_method(obj, method_name, func):
394
+ """
395
+ Patch a special method (like __iter__) for a single instance.
396
+
397
+ Args:
398
+ obj: the instance to patch
399
+ method_name: the name of the method (e.g., '__iter__')
400
+ func: the new function, expecting (self, ...)
401
+ """
402
+ cls = obj.__class__
403
+ # Dynamically create a new class that inherits from obj's class
404
+ new_cls = type(f"Patched{cls.__name__}", (cls,), {
405
+ method_name: func
406
+ })
407
+ obj.__class__ = new_cls
@@ -6,78 +6,185 @@ from opentelemetry.context import set_value, attach, detach, get_value
6
6
  from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
7
7
  from monocle_apptrace.instrumentation.common.utils import (
8
8
  get_fully_qualified_class_name,
9
+ set_scopes,
9
10
  with_tracer_wrapper,
10
11
  set_scope,
11
12
  remove_scope,
12
- async_wrapper
13
+ get_parent_span
13
14
  )
14
15
  from monocle_apptrace.instrumentation.common.constants import WORKFLOW_TYPE_KEY, ADD_NEW_WORKFLOW
15
16
  logger = logging.getLogger(__name__)
16
17
 
17
- def wrapper_processor(async_task: bool, tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
18
- # Some Langchain objects are wrapped elsewhere, so we ignore them here
19
- if instance.__class__.__name__ in ("AgentExecutor"):
20
- return wrapped(*args, **kwargs)
18
+ def get_auto_close_span(to_wrap, kwargs):
19
+ try:
20
+ if to_wrap.get("output_processor") and to_wrap.get("output_processor").get("is_auto_close"):
21
+ return to_wrap.get("output_processor").get("is_auto_close")(kwargs)
22
+ return True
23
+ except Exception as e:
24
+ logger.warning("Warning: Error occurred in get_auto_close_span: %s", str(e))
25
+ return True
26
+
27
+ def pre_process_span(name, tracer, handler, add_workflow_span, to_wrap, wrapped, instance, args, kwargs, span, source_path):
28
+ SpanHandler.set_default_monocle_attributes(span, source_path)
29
+ if SpanHandler.is_root_span(span) or add_workflow_span:
30
+ # This is a direct API call of a non-framework type
31
+ SpanHandler.set_workflow_properties(span, to_wrap)
32
+ else:
33
+ SpanHandler.set_non_workflow_properties(span)
34
+ try:
35
+ handler.pre_task_processing(to_wrap, wrapped, instance, args, kwargs, span)
36
+ except Exception as e:
37
+ logger.info(f"Warning: Error occurred in pre_task_processing: {e}")
21
38
 
39
+ def post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, return_value, span, parent_span=None, ex = None):
40
+ if not (SpanHandler.is_root_span(span) or get_value(ADD_NEW_WORKFLOW) == True):
41
+ try:
42
+ handler.hydrate_span(to_wrap, wrapped, instance, args, kwargs, return_value, span, parent_span, ex)
43
+ except Exception as e:
44
+ logger.info(f"Warning: Error occurred in hydrate_span: {e}")
45
+
46
+ try:
47
+ handler.post_task_processing(to_wrap, wrapped, instance, args, kwargs, return_value, span)
48
+ except Exception as e:
49
+ logger.info(f"Warning: Error occurred in post_task_processing: {e}")
50
+
51
+ def get_span_name(to_wrap, instance):
22
52
  if to_wrap.get("span_name"):
23
53
  name = to_wrap.get("span_name")
24
54
  else:
25
55
  name = get_fully_qualified_class_name(instance)
56
+ return name
57
+
58
+ def monocle_wrapper_span_processor(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, add_workflow_span, args, kwargs):
59
+ # Main span processing logic
60
+ name = get_span_name(to_wrap, instance)
61
+ return_value = None
62
+ span_status = None
63
+ auto_close_span = get_auto_close_span(to_wrap, kwargs)
64
+ parent_span = get_parent_span()
65
+ with tracer.start_as_current_span(name, end_on_exit=auto_close_span) as span:
66
+ pre_process_span(name, tracer, handler, add_workflow_span, to_wrap, wrapped, instance, args, kwargs, span, source_path)
67
+
68
+ if SpanHandler.is_root_span(span) or add_workflow_span:
69
+ # Recursive call for the actual span
70
+ return_value, span_status = monocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, False, args, kwargs)
71
+ span.set_status(span_status)
72
+ if not auto_close_span:
73
+ span.end()
74
+ else:
75
+ ex:Exception = None
76
+ try:
77
+ with SpanHandler.workflow_type(to_wrap, span):
78
+ return_value = wrapped(*args, **kwargs)
79
+ except Exception as e:
80
+ ex = e
81
+ raise
82
+ finally:
83
+ def post_process_span_internal(ret_val):
84
+ post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, ret_val, span, parent_span ,ex)
85
+ if not auto_close_span:
86
+ span.end()
87
+ if ex is None and not auto_close_span and to_wrap.get("output_processor") and to_wrap.get("output_processor").get("response_processor"):
88
+ to_wrap.get("output_processor").get("response_processor")(to_wrap, return_value, post_process_span_internal)
89
+ else:
90
+ post_process_span_internal(return_value)
91
+ span_status = span.status
92
+ return return_value, span_status
26
93
 
94
+ def monocle_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
27
95
  return_value = None
28
96
  token = None
29
97
  try:
30
- handler.pre_tracing(to_wrap, wrapped, instance, args, kwargs)
98
+ try:
99
+ handler.pre_tracing(to_wrap, wrapped, instance, args, kwargs)
100
+ except Exception as e:
101
+ logger.info(f"Warning: Error occurred in pre_tracing: {e}")
31
102
  if to_wrap.get('skip_span', False) or handler.skip_span(to_wrap, wrapped, instance, args, kwargs):
32
- if async_task:
33
- return_value = async_wrapper(wrapped, None, None, None, *args, **kwargs)
34
- else:
35
- return_value = wrapped(*args, **kwargs)
103
+ return_value = wrapped(*args, **kwargs)
36
104
  else:
37
105
  add_workflow_span = get_value(ADD_NEW_WORKFLOW) == True
38
106
  token = attach(set_value(ADD_NEW_WORKFLOW, False))
39
107
  try:
40
- return_value = span_processor(name, async_task, tracer, handler, add_workflow_span,
41
- to_wrap, wrapped, instance, args, kwargs)
108
+ return_value, span_status = monocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, add_workflow_span, args, kwargs)
42
109
  finally:
43
110
  detach(token)
44
111
  return return_value
45
112
  finally:
46
- handler.post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
113
+ try:
114
+ handler.post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
115
+ except Exception as e:
116
+ logger.info(f"Warning: Error occurred in post_tracing: {e}")
47
117
 
48
- def span_processor(name: str, async_task: bool, tracer: Tracer, handler: SpanHandler, add_workflow_span:bool,
49
- to_wrap, wrapped, instance, args, kwargs):
50
- # For singleton spans, eg OpenAI inference generate a workflow span to format the workflow specific attributes
118
+ async def amonocle_wrapper_span_processor(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, add_workflow_span, args, kwargs):
119
+ # Main span processing logic
120
+ name = get_span_name(to_wrap, instance)
51
121
  return_value = None
52
- with tracer.start_as_current_span(name) as span:
53
- # Since Spanhandler can be overridden, ensure we set default monocle attributes.
54
- SpanHandler.set_default_monocle_attributes(span)
122
+ span_status = None
123
+ auto_close_span = get_auto_close_span(to_wrap, kwargs)
124
+ parent_span = get_parent_span()
125
+ with tracer.start_as_current_span(name, end_on_exit=auto_close_span) as span:
126
+ pre_process_span(name, tracer, handler, add_workflow_span, to_wrap, wrapped, instance, args, kwargs, span, source_path)
127
+
55
128
  if SpanHandler.is_root_span(span) or add_workflow_span:
56
- # This is a direct API call of a non-framework type, call the span_processor recursively for the actual span
57
- SpanHandler.set_workflow_properties(span, to_wrap)
58
- return_value = span_processor(name, async_task, tracer, handler, False, to_wrap, wrapped, instance, args, kwargs)
129
+ # Recursive call for the actual span
130
+ return_value, span_status = await amonocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, False, args, kwargs)
131
+ span.set_status(span_status)
132
+ if not auto_close_span:
133
+ span.end()
59
134
  else:
60
- with SpanHandler.workflow_type(to_wrap):
61
- SpanHandler.set_non_workflow_properties(span)
62
- handler.pre_task_processing(to_wrap, wrapped, instance, args, kwargs, span)
63
- if async_task:
64
- return_value = async_wrapper(wrapped, None, None, None, *args, **kwargs)
65
- else:
66
- return_value = wrapped(*args, **kwargs)
67
- handler.hydrate_span(to_wrap, wrapped, instance, args, kwargs, return_value, span)
68
- handler.post_task_processing(to_wrap, wrapped, instance, args, kwargs, return_value, span)
69
- return return_value
135
+ ex:Exception = None
136
+ try:
137
+ with SpanHandler.workflow_type(to_wrap, span):
138
+ return_value = await wrapped(*args, **kwargs)
139
+ except Exception as e:
140
+ ex = e
141
+ raise
142
+ finally:
143
+ def post_process_span_internal(ret_val):
144
+ post_process_span(handler, to_wrap, wrapped, instance, args, kwargs, ret_val, span, parent_span,ex)
145
+ if not auto_close_span:
146
+ span.end()
147
+ if ex is None and not auto_close_span and to_wrap.get("output_processor") and to_wrap.get("output_processor").get("response_processor"):
148
+ to_wrap.get("output_processor").get("response_processor")(to_wrap, return_value, post_process_span_internal)
149
+ else:
150
+ post_process_span_internal(return_value)
151
+ span_status = span.status
152
+ return return_value, span.status
153
+
154
+ async def amonocle_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
155
+ return_value = None
156
+ token = None
157
+ try:
158
+ try:
159
+ handler.pre_tracing(to_wrap, wrapped, instance, args, kwargs)
160
+ except Exception as e:
161
+ logger.info(f"Warning: Error occurred in pre_tracing: {e}")
162
+ if to_wrap.get('skip_span', False) or handler.skip_span(to_wrap, wrapped, instance, args, kwargs):
163
+ return_value = await wrapped(*args, **kwargs)
164
+ else:
165
+ add_workflow_span = get_value(ADD_NEW_WORKFLOW) == True
166
+ token = attach(set_value(ADD_NEW_WORKFLOW, False))
167
+ try:
168
+ return_value, span_status = await amonocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, add_workflow_span, args, kwargs)
169
+ finally:
170
+ detach(token)
171
+ return return_value
172
+ finally:
173
+ try:
174
+ handler.post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
175
+ except Exception as e:
176
+ logger.info(f"Warning: Error occurred in post_tracing: {e}")
70
177
 
71
178
  @with_tracer_wrapper
72
- def task_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
73
- return wrapper_processor(False, tracer, handler, to_wrap, wrapped, instance, args, kwargs)
179
+ def task_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
180
+ return monocle_wrapper(tracer, handler, to_wrap, wrapped, instance, source_path, args, kwargs)
74
181
 
75
182
  @with_tracer_wrapper
76
- async def atask_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
77
- return wrapper_processor(True, tracer, handler, to_wrap, wrapped, instance, args, kwargs)
183
+ async def atask_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
184
+ return await amonocle_wrapper(tracer, handler, to_wrap, wrapped, instance, source_path, args, kwargs)
78
185
 
79
186
  @with_tracer_wrapper
80
- def scope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
187
+ def scope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
81
188
  scope_name = to_wrap.get('scope_name', None)
82
189
  if scope_name:
83
190
  token = set_scope(scope_name)
@@ -87,8 +194,54 @@ def scope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instan
87
194
  return return_value
88
195
 
89
196
  @with_tracer_wrapper
90
- async def ascope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
197
+ async def ascope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
91
198
  scope_name = to_wrap.get('scope_name', None)
92
199
  scope_value = to_wrap.get('scope_value', None)
93
- return_value = async_wrapper(wrapped, scope_name, scope_value, None, *args, **kwargs)
94
- return return_value
200
+ token = None
201
+ try:
202
+ if scope_name:
203
+ token = set_scope(scope_name, scope_value)
204
+ return_value = await wrapped(*args, **kwargs)
205
+ return return_value
206
+ finally:
207
+ if token:
208
+ remove_scope(token)
209
+
210
+ @with_tracer_wrapper
211
+ def scopes_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
212
+ scope_values = to_wrap.get('scope_values', None)
213
+ scope_values = evaluate_scope_values(args, kwargs, to_wrap, scope_values)
214
+ token = None
215
+ try:
216
+ if scope_values:
217
+ token = set_scopes(scope_values)
218
+ return_value = wrapped(*args, **kwargs)
219
+ return return_value
220
+ finally:
221
+ if token:
222
+ remove_scope(token)
223
+
224
+ @with_tracer_wrapper
225
+ async def ascopes_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
226
+ scope_values = to_wrap.get('scope_values', None)
227
+ scope_values = evaluate_scope_values(args, kwargs, to_wrap, scope_values)
228
+ token = None
229
+ try:
230
+ if scope_values:
231
+ token = set_scopes(scope_values)
232
+ return_value = await wrapped(*args, **kwargs)
233
+ return return_value
234
+ finally:
235
+ if token:
236
+ remove_scope(token)
237
+
238
+ def evaluate_scope_values(args, kwargs, to_wrap, scope_values):
239
+ if callable(scope_values):
240
+ try:
241
+ scope_values = scope_values(args, kwargs)
242
+ except Exception as e:
243
+ logger.warning("Warning: Error occurred in evaluate_scope_values: %s", str(e))
244
+ scope_values = None
245
+ if isinstance(scope_values, dict):
246
+ return scope_values
247
+ return None
@@ -10,6 +10,7 @@ from monocle_apptrace.instrumentation.metamodel.langchain.methods import (
10
10
  from monocle_apptrace.instrumentation.metamodel.llamaindex.methods import (LLAMAINDEX_METHODS, )
11
11
  from monocle_apptrace.instrumentation.metamodel.haystack.methods import (HAYSTACK_METHODS, )
12
12
  from monocle_apptrace.instrumentation.metamodel.openai.methods import (OPENAI_METHODS,)
13
+ from monocle_apptrace.instrumentation.metamodel.openai._helper import OpenAISpanHandler
13
14
  from monocle_apptrace.instrumentation.metamodel.langgraph.methods import LANGGRAPH_METHODS
14
15
  from monocle_apptrace.instrumentation.metamodel.flask.methods import (FLASK_METHODS, )
15
16
  from monocle_apptrace.instrumentation.metamodel.flask._helper import FlaskSpanHandler, FlaskResponseSpanHandler
@@ -17,7 +18,8 @@ from monocle_apptrace.instrumentation.metamodel.requests.methods import (REQUEST
17
18
  from monocle_apptrace.instrumentation.metamodel.requests._helper import RequestSpanHandler
18
19
  from monocle_apptrace.instrumentation.metamodel.teamsai.methods import (TEAMAI_METHODS, )
19
20
  from monocle_apptrace.instrumentation.metamodel.anthropic.methods import (ANTHROPIC_METHODS, )
20
-
21
+ from monocle_apptrace.instrumentation.metamodel.aiohttp.methods import (AIOHTTP_METHODS, )
22
+ from monocle_apptrace.instrumentation.metamodel.aiohttp._helper import aiohttpSpanHandler
21
23
  class WrapperMethod:
22
24
  def __init__(
23
25
  self,
@@ -29,7 +31,8 @@ class WrapperMethod:
29
31
  wrapper_method = task_wrapper,
30
32
  span_handler = 'default',
31
33
  scope_name: str = None,
32
- span_type: str = None
34
+ span_type: str = None,
35
+ scope_values = None,
33
36
  ):
34
37
  self.package = package
35
38
  self.object = object_name
@@ -37,10 +40,11 @@ class WrapperMethod:
37
40
  self.span_name = span_name
38
41
  self.output_processor=output_processor
39
42
  self.span_type = span_type
43
+ self.scope_values = scope_values
40
44
 
41
45
  self.span_handler:SpanHandler.__class__ = span_handler
42
46
  self.scope_name = scope_name
43
- if scope_name:
47
+ if scope_name and not scope_values:
44
48
  self.wrapper_method = scope_wrapper
45
49
  else:
46
50
  self.wrapper_method = wrapper_method
@@ -56,20 +60,23 @@ class WrapperMethod:
56
60
  'wrapper_method': self.wrapper_method,
57
61
  'span_handler': self.span_handler,
58
62
  'scope_name': self.scope_name,
59
- 'span_type': self.span_type
63
+ 'span_type': self.span_type,
64
+ 'scope_values': self.scope_values,
60
65
  }
61
66
  return instance_dict
62
67
 
63
68
  def get_span_handler(self) -> SpanHandler:
64
69
  return self.span_handler()
65
70
 
66
- DEFAULT_METHODS_LIST = LANGCHAIN_METHODS + LLAMAINDEX_METHODS + HAYSTACK_METHODS + BOTOCORE_METHODS + FLASK_METHODS + REQUESTS_METHODS + LANGGRAPH_METHODS + OPENAI_METHODS + TEAMAI_METHODS + ANTHROPIC_METHODS
71
+ DEFAULT_METHODS_LIST = LANGCHAIN_METHODS + LLAMAINDEX_METHODS + HAYSTACK_METHODS + BOTOCORE_METHODS + FLASK_METHODS + REQUESTS_METHODS + LANGGRAPH_METHODS + OPENAI_METHODS + TEAMAI_METHODS + ANTHROPIC_METHODS + AIOHTTP_METHODS
67
72
 
68
73
  MONOCLE_SPAN_HANDLERS: Dict[str, SpanHandler] = {
69
74
  "default": SpanHandler(),
75
+ "aiohttp_handler": aiohttpSpanHandler(),
70
76
  "botocore_handler": BotoCoreSpanHandler(),
71
77
  "flask_handler": FlaskSpanHandler(),
72
78
  "flask_response_handler": FlaskResponseSpanHandler(),
73
79
  "request_handler": RequestSpanHandler(),
74
- "non_framework_handler": NonFrameworkSpanHandler()
80
+ "non_framework_handler": NonFrameworkSpanHandler(),
81
+ "openai_handler": OpenAISpanHandler(),
75
82
  }
@@ -0,0 +1,66 @@
1
+ import logging
2
+ from threading import local
3
+ from monocle_apptrace.instrumentation.common.utils import extract_http_headers, clear_http_scopes, try_option, Option, MonocleSpanException
4
+ from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
5
+ from monocle_apptrace.instrumentation.common.constants import HTTP_SUCCESS_CODES
6
+ from urllib.parse import unquote
7
+
8
+ logger = logging.getLogger(__name__)
9
+ MAX_DATA_LENGTH = 1000
10
+ token_data = local()
11
+ token_data.current_token = None
12
+
13
+ def get_route(args) -> str:
14
+ route_path: Option[str] = try_option(getattr, args[0], 'path')
15
+ return route_path.unwrap_or("")
16
+
17
+ def get_method(args) -> str:
18
+ # return args[0]['method'] if 'method' in args[0] else ""
19
+ http_method: Option[str] = try_option(getattr, args[0], 'method')
20
+ return http_method.unwrap_or("")
21
+
22
+ def get_params(args) -> dict:
23
+ params: Option[str] = try_option(getattr, args[0], 'query_string')
24
+ return unquote(params.unwrap_or(""))
25
+
26
+ def get_body(args) -> dict:
27
+ return ""
28
+
29
+ def extract_response(result) -> str:
30
+ if hasattr(result, 'text'):
31
+ response = result.text[0:max(result.text.__len__(), MAX_DATA_LENGTH)]
32
+ else:
33
+ response = ""
34
+ return response
35
+
36
+ def extract_status(result) -> str:
37
+ status = f"{result.status}" if hasattr(result, 'status') else ""
38
+ if status not in HTTP_SUCCESS_CODES:
39
+ error_message = extract_response(result)
40
+ raise MonocleSpanException(f"error: {status} - {error_message}")
41
+ return status
42
+
43
+ def aiohttp_pre_tracing(args):
44
+ token_data.current_token = extract_http_headers(args[0].headers)
45
+
46
+ def aiohttp_post_tracing():
47
+ clear_http_scopes(token_data.current_token)
48
+ token_data.current_token = None
49
+
50
+ def aiohttp_skip_span(args) -> bool:
51
+ if get_method(args) == "HEAD":
52
+ return True
53
+ return False
54
+
55
+ class aiohttpSpanHandler(SpanHandler):
56
+
57
+ def pre_tracing(self, to_wrap, wrapped, instance, args, kwargs):
58
+ aiohttp_pre_tracing(args)
59
+ return super().pre_tracing(to_wrap, wrapped, instance, args, kwargs)
60
+
61
+ def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value):
62
+ aiohttp_post_tracing()
63
+ return super().post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
64
+
65
+ def skip_span(self, to_wrap, wrapped, instance, args, kwargs) -> bool:
66
+ return aiohttp_skip_span(args)
@@ -0,0 +1,51 @@
1
+ from monocle_apptrace.instrumentation.metamodel.aiohttp import _helper
2
+ AIO_HTTP_PROCESSOR = {
3
+ "type": "http.process",
4
+ "attributes": [
5
+ [
6
+ {
7
+ "_comment": "request method, request URI",
8
+ "attribute": "method",
9
+ "accessor": lambda arguments: _helper.get_method(arguments['args'])
10
+ },
11
+ {
12
+ "_comment": "request method, request URI",
13
+ "attribute": "route",
14
+ "accessor": lambda arguments: _helper.get_route(arguments['args'])
15
+ },
16
+ {
17
+ "_comment": "request method, request URI",
18
+ "attribute": "body",
19
+ "accessor": lambda arguments: _helper.get_body(arguments['args'])
20
+ },
21
+ ]
22
+ ],
23
+ "events": [
24
+ {
25
+ "name": "data.input",
26
+ "attributes": [
27
+ {
28
+ "_comment": "route params",
29
+ "attribute": "params",
30
+ "accessor": lambda arguments: _helper.get_params(arguments['args'])
31
+ }
32
+ ]
33
+ },
34
+ {
35
+ "name": "data.output",
36
+ "attributes": [
37
+ {
38
+ "_comment": "status from HTTP response",
39
+ "attribute": "status",
40
+ "accessor": lambda arguments: _helper.extract_status(arguments['result'])
41
+ },
42
+ {
43
+ "_comment": "this is result from LLM",
44
+ "attribute": "response",
45
+ "accessor": lambda arguments: _helper.extract_response(arguments['result'])
46
+ }
47
+ ]
48
+ }
49
+
50
+ ]
51
+ }