monocle-apptrace 0.3.1b1__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 (46) 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 +8 -5
  6. monocle_apptrace/exporters/okahu/okahu_exporter.py +5 -2
  7. monocle_apptrace/instrumentation/common/__init__.py +1 -1
  8. monocle_apptrace/instrumentation/common/constants.py +12 -5
  9. monocle_apptrace/instrumentation/common/instrumentor.py +44 -22
  10. monocle_apptrace/instrumentation/common/span_handler.py +102 -50
  11. monocle_apptrace/instrumentation/common/tracing.md +68 -0
  12. monocle_apptrace/instrumentation/common/utils.py +114 -63
  13. monocle_apptrace/instrumentation/common/wrapper.py +202 -47
  14. monocle_apptrace/instrumentation/common/wrapper_method.py +15 -7
  15. monocle_apptrace/instrumentation/metamodel/aiohttp/__init__.py +0 -0
  16. monocle_apptrace/instrumentation/metamodel/aiohttp/_helper.py +66 -0
  17. monocle_apptrace/instrumentation/metamodel/aiohttp/entities/http.py +51 -0
  18. monocle_apptrace/instrumentation/metamodel/aiohttp/methods.py +13 -0
  19. monocle_apptrace/instrumentation/metamodel/anthropic/methods.py +4 -2
  20. monocle_apptrace/instrumentation/metamodel/flask/_helper.py +50 -3
  21. monocle_apptrace/instrumentation/metamodel/flask/entities/http.py +48 -0
  22. monocle_apptrace/instrumentation/metamodel/flask/methods.py +10 -1
  23. monocle_apptrace/instrumentation/metamodel/haystack/_helper.py +17 -4
  24. monocle_apptrace/instrumentation/metamodel/haystack/entities/inference.py +5 -2
  25. monocle_apptrace/instrumentation/metamodel/haystack/methods.py +8 -4
  26. monocle_apptrace/instrumentation/metamodel/langchain/_helper.py +12 -4
  27. monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +1 -1
  28. monocle_apptrace/instrumentation/metamodel/langchain/methods.py +6 -14
  29. monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +13 -9
  30. monocle_apptrace/instrumentation/metamodel/llamaindex/entities/inference.py +1 -1
  31. monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +16 -15
  32. monocle_apptrace/instrumentation/metamodel/openai/_helper.py +26 -5
  33. monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +184 -26
  34. monocle_apptrace/instrumentation/metamodel/openai/methods.py +6 -8
  35. monocle_apptrace/instrumentation/metamodel/requests/_helper.py +31 -0
  36. monocle_apptrace/instrumentation/metamodel/requests/entities/http.py +51 -0
  37. monocle_apptrace/instrumentation/metamodel/requests/methods.py +2 -1
  38. monocle_apptrace/instrumentation/metamodel/teamsai/_helper.py +55 -5
  39. monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/actionplanner_output_processor.py +13 -33
  40. monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/teamsai_output_processor.py +24 -20
  41. monocle_apptrace/instrumentation/metamodel/teamsai/methods.py +54 -8
  42. {monocle_apptrace-0.3.1b1.dist-info → monocle_apptrace-0.4.0.dist-info}/METADATA +22 -18
  43. {monocle_apptrace-0.3.1b1.dist-info → monocle_apptrace-0.4.0.dist-info}/RECORD +46 -39
  44. {monocle_apptrace-0.3.1b1.dist-info → monocle_apptrace-0.4.0.dist-info}/WHEEL +0 -0
  45. {monocle_apptrace-0.3.1b1.dist-info → monocle_apptrace-0.4.0.dist-info}/licenses/LICENSE +0 -0
  46. {monocle_apptrace-0.3.1b1.dist-info → monocle_apptrace-0.4.0.dist-info}/licenses/NOTICE +0 -0
@@ -6,76 +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
- from monocle_apptrace.instrumentation.common.constants import WORKFLOW_TYPE_KEY
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
21
26
 
22
- if hasattr(instance, "name") and instance.name:
23
- name = f"{to_wrap.get('span_name')}.{instance.name.lower()}"
24
- elif to_wrap.get("span_name"):
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}")
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):
52
+ if to_wrap.get("span_name"):
25
53
  name = to_wrap.get("span_name")
26
54
  else:
27
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
28
93
 
94
+ def monocle_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, source_path, args, kwargs):
29
95
  return_value = None
30
96
  token = None
31
97
  try:
32
- handler.pre_tracing(to_wrap, wrapped, instance, args, kwargs)
33
- skip_scan:bool = to_wrap.get('skip_span') or handler.skip_span(to_wrap, wrapped, instance, args, kwargs)
34
- if not to_wrap.get('skip_span'):
35
- token = SpanHandler.attach_workflow_type(to_wrap=to_wrap)
36
- if skip_scan:
37
- if async_task:
38
- return_value = async_wrapper(wrapped, None, None, None, *args, **kwargs)
39
- else:
40
- return_value = wrapped(*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}")
102
+ if to_wrap.get('skip_span', False) or handler.skip_span(to_wrap, wrapped, instance, args, kwargs):
103
+ return_value = wrapped(*args, **kwargs)
41
104
  else:
42
- return_value = span_processor(name, async_task, tracer, handler, to_wrap, wrapped, instance, args, kwargs)
105
+ add_workflow_span = get_value(ADD_NEW_WORKFLOW) == True
106
+ token = attach(set_value(ADD_NEW_WORKFLOW, False))
107
+ try:
108
+ return_value, span_status = monocle_wrapper_span_processor(tracer, handler, to_wrap, wrapped, instance, source_path, add_workflow_span, args, kwargs)
109
+ finally:
110
+ detach(token)
43
111
  return return_value
44
112
  finally:
45
- handler.detach_workflow_type(token)
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, to_wrap, wrapped, instance, args, kwargs):
49
- # 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)
50
121
  return_value = None
51
- with tracer.start_as_current_span(name) as span:
52
- # Since Spanhandler can be overridden, ensure we set default monocle attributes.
53
- SpanHandler.set_default_monocle_attributes(span)
54
- if SpanHandler.is_root_span(span):
55
- SpanHandler.set_workflow_properties(span, to_wrap)
56
- if handler.is_non_workflow_root_span(span, to_wrap):
57
- # This is a direct API call of a non-framework type, call the span_processor recursively for the actual span
58
- return_value = span_processor(name, async_task, tracer, handler, to_wrap, wrapped, instance, args, kwargs)
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
+
128
+ if SpanHandler.is_root_span(span) or add_workflow_span:
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
- handler.pre_task_processing(to_wrap, wrapped, instance, args, kwargs, span)
61
- if async_task:
62
- return_value = async_wrapper(wrapped, None, None, None, *args, **kwargs)
63
- else:
64
- return_value = wrapped(*args, **kwargs)
65
- handler.hydrate_span(to_wrap, wrapped, instance, args, kwargs, return_value, span)
66
- handler.post_task_processing(to_wrap, wrapped, instance, args, kwargs, return_value, span)
67
- 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}")
68
177
 
69
178
  @with_tracer_wrapper
70
- def task_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
71
- 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)
72
181
 
73
182
  @with_tracer_wrapper
74
- async def atask_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
75
- 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)
76
185
 
77
186
  @with_tracer_wrapper
78
- 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):
79
188
  scope_name = to_wrap.get('scope_name', None)
80
189
  if scope_name:
81
190
  token = set_scope(scope_name)
@@ -85,8 +194,54 @@ def scope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instan
85
194
  return return_value
86
195
 
87
196
  @with_tracer_wrapper
88
- 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):
89
198
  scope_name = to_wrap.get('scope_name', None)
90
199
  scope_value = to_wrap.get('scope_value', None)
91
- return_value = async_wrapper(wrapped, scope_name, scope_value, None, *args, **kwargs)
92
- 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,14 +10,16 @@ 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
- from monocle_apptrace.instrumentation.metamodel.flask._helper import FlaskSpanHandler
16
+ from monocle_apptrace.instrumentation.metamodel.flask._helper import FlaskSpanHandler, FlaskResponseSpanHandler
16
17
  from monocle_apptrace.instrumentation.metamodel.requests.methods import (REQUESTS_METHODS, )
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,19 +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(),
78
+ "flask_response_handler": FlaskResponseSpanHandler(),
72
79
  "request_handler": RequestSpanHandler(),
73
- "non_framework_handler": NonFrameworkSpanHandler()
80
+ "non_framework_handler": NonFrameworkSpanHandler(),
81
+ "openai_handler": OpenAISpanHandler(),
74
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
+ }
@@ -0,0 +1,13 @@
1
+ from monocle_apptrace.instrumentation.common.wrapper import atask_wrapper
2
+ from monocle_apptrace.instrumentation.metamodel.aiohttp.entities.http import AIO_HTTP_PROCESSOR
3
+
4
+ AIOHTTP_METHODS = [
5
+ {
6
+ "package": "aiohttp.web_app",
7
+ "object": "Application",
8
+ "method": "_handle",
9
+ "wrapper_method": atask_wrapper,
10
+ "span_handler": "aiohttp_handler",
11
+ "output_processor": AIO_HTTP_PROCESSOR
12
+ }
13
+ ]
@@ -5,17 +5,19 @@ from monocle_apptrace.instrumentation.metamodel.anthropic.entities.inference imp
5
5
 
6
6
  ANTHROPIC_METHODS = [
7
7
  {
8
- "package": "anthropic.resources.messages.messages",
8
+ "package": "anthropic.resources",
9
9
  "object": "Messages",
10
10
  "method": "create",
11
11
  "wrapper_method": task_wrapper,
12
+ "span_handler": "non_framework_handler",
12
13
  "output_processor": INFERENCE
13
14
  },
14
15
  {
15
- "package": "anthropic.resources.messages.messages",
16
+ "package": "anthropic.resources",
16
17
  "object": "AsyncMessages",
17
18
  "method": "create",
18
19
  "wrapper_method": atask_wrapper,
20
+ "span_handler": "non_framework_handler",
19
21
  "output_processor": INFERENCE
20
22
  },
21
23
 
@@ -1,11 +1,46 @@
1
+ import logging
1
2
  from threading import local
2
3
  from monocle_apptrace.instrumentation.common.utils import extract_http_headers, clear_http_scopes
3
- from opentelemetry.propagate import extract
4
- from opentelemetry.context import Context, attach, detach
5
4
  from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
5
+ from monocle_apptrace.instrumentation.common.constants import HTTP_SUCCESS_CODES
6
+ from monocle_apptrace.instrumentation.common.utils import MonocleSpanException
7
+ from urllib.parse import unquote
8
+ from opentelemetry.context import get_current
9
+ from opentelemetry.trace import Span, get_current_span
10
+ from opentelemetry.trace.propagation import _SPAN_KEY
11
+
12
+ logger = logging.getLogger(__name__)
13
+ MAX_DATA_LENGTH = 1000
6
14
  token_data = local()
7
15
  token_data.current_token = None
8
16
 
17
+ def get_route(args) -> str:
18
+ return args[0]['PATH_INFO'] if 'PATH_INFO' in args[0] else ""
19
+
20
+ def get_method(args) -> str:
21
+ return args[0]['REQUEST_METHOD'] if 'REQUEST_METHOD' in args[0] else ""
22
+
23
+ def get_params(args) -> dict:
24
+ params = args[0]['QUERY_STRING'] if 'QUERY_STRING' in args[0] else ""
25
+ return unquote(params)
26
+
27
+ def get_body(args) -> dict:
28
+ return ""
29
+
30
+ def extract_response(instance) -> str:
31
+ if hasattr(instance, 'data') and hasattr(instance, 'content_length'):
32
+ response = instance.data[0:max(instance.content_length, MAX_DATA_LENGTH)]
33
+ else:
34
+ response = ""
35
+ return response
36
+
37
+ def extract_status(instance) -> str:
38
+ status = f"{instance.status_code}" if hasattr(instance, 'status_code') else ""
39
+ if status not in HTTP_SUCCESS_CODES:
40
+ error_message = extract_response(instance)
41
+ raise MonocleSpanException(f"error: {status} - {error_message}")
42
+ return status
43
+
9
44
  def flask_pre_tracing(args):
10
45
  headers = dict()
11
46
  for key, value in args[0].items():
@@ -26,4 +61,16 @@ class FlaskSpanHandler(SpanHandler):
26
61
 
27
62
  def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value):
28
63
  flask_post_tracing()
29
- return super().post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
64
+ return super().post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
65
+
66
+ class FlaskResponseSpanHandler(SpanHandler):
67
+ def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value):
68
+ try:
69
+ _parent_span_context = get_current()
70
+ if _parent_span_context is not None:
71
+ parent_span: Span = _parent_span_context.get(_SPAN_KEY, None)
72
+ if parent_span is not None:
73
+ self.hydrate_events(to_wrap, wrapped, instance, args, kwargs, return_value, parent_span=parent_span)
74
+ except Exception as e:
75
+ logger.info(f"Failed to propogate flask response: {e}")
76
+ super().post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
@@ -0,0 +1,48 @@
1
+ from monocle_apptrace.instrumentation.metamodel.flask import _helper
2
+ FLASK_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
+ ]
18
+ }
19
+
20
+ FLASK_RESPONSE_PROCESSOR = {
21
+ "events": [
22
+ {
23
+ "name": "data.input",
24
+ "attributes": [
25
+ {
26
+ "_comment": "route params",
27
+ "attribute": "params",
28
+ "accessor": lambda arguments: _helper.get_params(arguments['args'])
29
+ }
30
+ ]
31
+ },
32
+ {
33
+ "name": "data.output",
34
+ "attributes": [
35
+ {
36
+ "_comment": "status from HTTP response",
37
+ "attribute": "status",
38
+ "accessor": lambda arguments: _helper.extract_status(arguments['instance'])
39
+ },
40
+ {
41
+ "_comment": "this is result from LLM",
42
+ "attribute": "response",
43
+ "accessor": lambda arguments: _helper.extract_response(arguments['instance'])
44
+ }
45
+ ]
46
+ }
47
+ ]
48
+ }
@@ -1,13 +1,22 @@
1
1
  from monocle_apptrace.instrumentation.common.wrapper import task_wrapper
2
+ from monocle_apptrace.instrumentation.metamodel.flask.entities.http import FLASK_HTTP_PROCESSOR, FLASK_RESPONSE_PROCESSOR
2
3
 
3
4
  FLASK_METHODS = [
4
5
  {
5
6
  "package": "flask.app",
6
7
  "object": "Flask",
7
8
  "method": "wsgi_app",
8
- "span_name": "Flask.wsgi_app",
9
9
  "wrapper_method": task_wrapper,
10
10
  "span_handler": "flask_handler",
11
+ "output_processor": FLASK_HTTP_PROCESSOR,
12
+ },
13
+ {
14
+ "package": "werkzeug.wrappers.response",
15
+ "object": "Response",
16
+ "method": "__call__",
17
+ "wrapper_method": task_wrapper,
18
+ "span_handler": "flask_response_handler",
19
+ "output_processor": FLASK_RESPONSE_PROCESSOR,
11
20
  "skip_span": True
12
21
  }
13
22
  ]