monocle-apptrace 0.4.2__py3-none-any.whl → 0.5.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.
- monocle_apptrace/__main__.py +1 -1
- monocle_apptrace/exporters/file_exporter.py +125 -37
- monocle_apptrace/instrumentation/common/__init__.py +16 -1
- monocle_apptrace/instrumentation/common/constants.py +14 -1
- monocle_apptrace/instrumentation/common/instrumentor.py +19 -152
- monocle_apptrace/instrumentation/common/method_wrappers.py +376 -0
- monocle_apptrace/instrumentation/common/span_handler.py +58 -32
- monocle_apptrace/instrumentation/common/utils.py +52 -15
- monocle_apptrace/instrumentation/common/wrapper.py +124 -18
- monocle_apptrace/instrumentation/common/wrapper_method.py +47 -1
- monocle_apptrace/instrumentation/metamodel/a2a/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/a2a/_helper.py +37 -0
- monocle_apptrace/instrumentation/metamodel/a2a/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/a2a/entities/inference.py +112 -0
- monocle_apptrace/instrumentation/metamodel/a2a/methods.py +22 -0
- monocle_apptrace/instrumentation/metamodel/adk/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/adk/_helper.py +182 -0
- monocle_apptrace/instrumentation/metamodel/adk/entities/agent.py +50 -0
- monocle_apptrace/instrumentation/metamodel/adk/entities/tool.py +57 -0
- monocle_apptrace/instrumentation/metamodel/adk/methods.py +24 -0
- monocle_apptrace/instrumentation/metamodel/agents/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/agents/_helper.py +220 -0
- monocle_apptrace/instrumentation/metamodel/agents/agents_processor.py +152 -0
- monocle_apptrace/instrumentation/metamodel/agents/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/agents/entities/inference.py +191 -0
- monocle_apptrace/instrumentation/metamodel/agents/methods.py +56 -0
- monocle_apptrace/instrumentation/metamodel/aiohttp/_helper.py +6 -11
- monocle_apptrace/instrumentation/metamodel/anthropic/_helper.py +112 -18
- monocle_apptrace/instrumentation/metamodel/anthropic/entities/inference.py +18 -10
- monocle_apptrace/instrumentation/metamodel/azfunc/_helper.py +13 -11
- monocle_apptrace/instrumentation/metamodel/azfunc/entities/http.py +5 -0
- monocle_apptrace/instrumentation/metamodel/azureaiinference/_helper.py +88 -8
- monocle_apptrace/instrumentation/metamodel/azureaiinference/entities/inference.py +22 -8
- monocle_apptrace/instrumentation/metamodel/botocore/_helper.py +92 -16
- monocle_apptrace/instrumentation/metamodel/botocore/entities/inference.py +13 -8
- monocle_apptrace/instrumentation/metamodel/botocore/handlers/botocore_span_handler.py +1 -1
- monocle_apptrace/instrumentation/metamodel/fastapi/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/fastapi/_helper.py +82 -0
- monocle_apptrace/instrumentation/metamodel/fastapi/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/fastapi/entities/http.py +44 -0
- monocle_apptrace/instrumentation/metamodel/fastapi/methods.py +23 -0
- monocle_apptrace/instrumentation/metamodel/finish_types.py +463 -0
- monocle_apptrace/instrumentation/metamodel/flask/_helper.py +6 -11
- monocle_apptrace/instrumentation/metamodel/gemini/_helper.py +51 -7
- monocle_apptrace/instrumentation/metamodel/gemini/entities/inference.py +22 -11
- monocle_apptrace/instrumentation/metamodel/gemini/entities/retrieval.py +43 -0
- monocle_apptrace/instrumentation/metamodel/gemini/methods.py +18 -1
- monocle_apptrace/instrumentation/metamodel/haystack/_helper.py +79 -8
- monocle_apptrace/instrumentation/metamodel/haystack/entities/inference.py +15 -10
- monocle_apptrace/instrumentation/metamodel/haystack/methods.py +7 -0
- monocle_apptrace/instrumentation/metamodel/lambdafunc/_helper.py +78 -0
- monocle_apptrace/instrumentation/metamodel/lambdafunc/entities/http.py +51 -0
- monocle_apptrace/instrumentation/metamodel/lambdafunc/methods.py +23 -0
- monocle_apptrace/instrumentation/metamodel/lambdafunc/wrapper.py +23 -0
- monocle_apptrace/instrumentation/metamodel/langchain/_helper.py +145 -19
- monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +19 -10
- monocle_apptrace/instrumentation/metamodel/langgraph/_helper.py +67 -10
- monocle_apptrace/instrumentation/metamodel/langgraph/entities/inference.py +127 -20
- monocle_apptrace/instrumentation/metamodel/langgraph/langgraph_processor.py +46 -0
- monocle_apptrace/instrumentation/metamodel/langgraph/methods.py +35 -9
- monocle_apptrace/instrumentation/metamodel/litellm/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/litellm/_helper.py +89 -0
- monocle_apptrace/instrumentation/metamodel/litellm/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/litellm/entities/inference.py +108 -0
- monocle_apptrace/instrumentation/metamodel/litellm/methods.py +19 -0
- monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +227 -16
- monocle_apptrace/instrumentation/metamodel/llamaindex/entities/agent.py +127 -10
- monocle_apptrace/instrumentation/metamodel/llamaindex/entities/inference.py +13 -8
- monocle_apptrace/instrumentation/metamodel/llamaindex/llamaindex_processor.py +62 -0
- monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +68 -1
- monocle_apptrace/instrumentation/metamodel/mcp/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/mcp/_helper.py +118 -0
- monocle_apptrace/instrumentation/metamodel/mcp/entities/__init__.py +0 -0
- monocle_apptrace/instrumentation/metamodel/mcp/entities/inference.py +48 -0
- monocle_apptrace/instrumentation/metamodel/mcp/mcp_processor.py +8 -0
- monocle_apptrace/instrumentation/metamodel/mcp/methods.py +21 -0
- monocle_apptrace/instrumentation/metamodel/openai/_helper.py +188 -16
- monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +148 -92
- monocle_apptrace/instrumentation/metamodel/openai/entities/retrieval.py +1 -1
- monocle_apptrace/instrumentation/metamodel/teamsai/_helper.py +53 -23
- monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/actionplanner_output_processor.py +1 -1
- monocle_apptrace/instrumentation/metamodel/teamsai/entities/inference/teamsai_output_processor.py +15 -9
- monocle_apptrace/instrumentation/metamodel/teamsai/sample.json +0 -4
- {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0.dist-info}/METADATA +27 -11
- {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0.dist-info}/RECORD +88 -47
- {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0.dist-info}/WHEEL +0 -0
- {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0.dist-info}/licenses/LICENSE +0 -0
- {monocle_apptrace-0.4.2.dist-info → monocle_apptrace-0.5.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import inspect
|
|
3
|
+
from typing import Dict, List, Optional, Any
|
|
4
|
+
from functools import wraps
|
|
5
|
+
import inspect
|
|
6
|
+
from opentelemetry.context import attach, get_current, detach
|
|
7
|
+
from opentelemetry.sdk.trace import Span
|
|
8
|
+
from opentelemetry.trace.span import INVALID_SPAN
|
|
9
|
+
from opentelemetry.trace import get_tracer
|
|
10
|
+
from contextlib import contextmanager, asynccontextmanager
|
|
11
|
+
from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
|
|
12
|
+
from monocle_apptrace.instrumentation.common.wrapper import atask_wrapper, get_current_monocle_span, set_monocle_span_in_context, task_wrapper
|
|
13
|
+
from monocle_apptrace.instrumentation.common.utils import (
|
|
14
|
+
set_scope, remove_scope, http_route_handler, http_async_route_handler
|
|
15
|
+
)
|
|
16
|
+
from monocle_apptrace.instrumentation.common.constants import MONOCLE_INSTRUMENTOR
|
|
17
|
+
from monocle_apptrace.instrumentation.common.instrumentor import get_tracer_provider
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def start_trace(
|
|
25
|
+
span_name: Optional[str] = None,
|
|
26
|
+
attributes: Optional[Dict[str, Any]] = None,
|
|
27
|
+
events: Optional[List[Dict[str, Any]]] = None
|
|
28
|
+
):
|
|
29
|
+
"""
|
|
30
|
+
Starts a new trace. All the spans created after this call will be part of the same trace.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
span_name: Optional custom span name. If None, uses the default span name.
|
|
34
|
+
attributes: Optional dictionary of custom attributes to set on the span.
|
|
35
|
+
events: Optional list of events to add to the span. Each event should be a dict with
|
|
36
|
+
'name' and optionally 'attributes' keys.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Token: A token representing the attached context for the span.
|
|
40
|
+
This token is to be used later to stop the current trace.
|
|
41
|
+
Returns None if tracing fails.
|
|
42
|
+
|
|
43
|
+
Raises:
|
|
44
|
+
Exception: The function catches all exceptions internally and logs a warning.
|
|
45
|
+
"""
|
|
46
|
+
try:
|
|
47
|
+
tracer = get_tracer(instrumenting_module_name= MONOCLE_INSTRUMENTOR, tracer_provider= get_tracer_provider())
|
|
48
|
+
span_name = span_name or "custom_span"
|
|
49
|
+
span = tracer.start_span(name=span_name)
|
|
50
|
+
updated_span_context = set_monocle_span_in_context(span=span)
|
|
51
|
+
|
|
52
|
+
# Set default monocle attributes
|
|
53
|
+
SpanHandler.set_default_monocle_attributes(span)
|
|
54
|
+
if SpanHandler.is_root_span(span):
|
|
55
|
+
SpanHandler.set_workflow_properties(span)
|
|
56
|
+
|
|
57
|
+
# Set custom attributes and events using common method
|
|
58
|
+
_setup_span_attributes_and_events(span, attributes, events)
|
|
59
|
+
|
|
60
|
+
token = attach(updated_span_context)
|
|
61
|
+
return token
|
|
62
|
+
except Exception as e:
|
|
63
|
+
logger.warning(f"Failed to start trace: {e}")
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
def stop_trace(
|
|
67
|
+
token,
|
|
68
|
+
final_attributes: Optional[Dict[str, Any]] = None,
|
|
69
|
+
final_events: Optional[List[Dict[str, Any]]] = None
|
|
70
|
+
) -> None:
|
|
71
|
+
"""
|
|
72
|
+
Stop the active trace. All the spans created after this will not be part of the trace.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
token: The token that was returned when the trace was started. Can be None in which case only the span is ended.
|
|
76
|
+
final_attributes: Optional dictionary of final attributes to set on the span before ending.
|
|
77
|
+
final_events: Optional list of final events to add to the span before ending.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
None
|
|
81
|
+
"""
|
|
82
|
+
try:
|
|
83
|
+
parent_span = get_current_monocle_span()
|
|
84
|
+
if parent_span is not None and parent_span != INVALID_SPAN:
|
|
85
|
+
# Set final attributes and events using common method
|
|
86
|
+
_setup_span_attributes_and_events(parent_span, final_attributes, final_events)
|
|
87
|
+
parent_span.end()
|
|
88
|
+
if token is not None:
|
|
89
|
+
detach(token)
|
|
90
|
+
except Exception as e:
|
|
91
|
+
logger.warning(f"Failed to stop trace: {e}")
|
|
92
|
+
|
|
93
|
+
def start_scope(
|
|
94
|
+
scope_name: str,
|
|
95
|
+
scope_value: Optional[str] = None
|
|
96
|
+
) -> object:
|
|
97
|
+
"""
|
|
98
|
+
Start a new scope with the given name and optional value. If no value is provided, a random UUID will be generated.
|
|
99
|
+
All the spans, across traces created after this call will have the scope attached until the scope is stopped.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
scope_name: The name of the scope.
|
|
103
|
+
scope_value: Optional value of the scope. If None, a random UUID will be generated.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Token: A token representing the attached context for the scope. This token is to be used later to stop the current scope.
|
|
107
|
+
"""
|
|
108
|
+
try:
|
|
109
|
+
# Set the scope using existing utility
|
|
110
|
+
token = set_scope(scope_name, scope_value)
|
|
111
|
+
return token
|
|
112
|
+
except Exception as e:
|
|
113
|
+
logger.warning(f"Failed to start scope: {e}")
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
def stop_scope(
|
|
117
|
+
token: object
|
|
118
|
+
) -> None:
|
|
119
|
+
"""
|
|
120
|
+
Stop the active scope. All the spans created after this will not have the scope attached.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
token: The token that was returned when the scope was started.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
None
|
|
127
|
+
"""
|
|
128
|
+
try:
|
|
129
|
+
# Remove the scope
|
|
130
|
+
remove_scope(token)
|
|
131
|
+
except Exception as e:
|
|
132
|
+
logger.warning(f"Failed to stop scope: {e}")
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@contextmanager
|
|
137
|
+
def monocle_trace(
|
|
138
|
+
span_name: Optional[str] = None,
|
|
139
|
+
attributes: Optional[Dict[str, Any]] = None,
|
|
140
|
+
events: Optional[List[Dict[str, Any]]] = None
|
|
141
|
+
):
|
|
142
|
+
"""
|
|
143
|
+
Context manager to start and stop a trace. All the spans, across traces created within the encapsulated code will have same trace ID
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
span_name: Optional custom span name.
|
|
147
|
+
attributes: Optional dictionary of custom attributes to set on the span.
|
|
148
|
+
events: Optional list of events to add to the span at start.
|
|
149
|
+
"""
|
|
150
|
+
try:
|
|
151
|
+
tracer = get_tracer(instrumenting_module_name=MONOCLE_INSTRUMENTOR, tracer_provider=get_tracer_provider())
|
|
152
|
+
span_name = span_name or "custom_span"
|
|
153
|
+
|
|
154
|
+
with tracer.start_as_current_span(span_name) as span:
|
|
155
|
+
# Set default monocle attributes
|
|
156
|
+
SpanHandler.set_default_monocle_attributes(span)
|
|
157
|
+
if SpanHandler.is_root_span(span):
|
|
158
|
+
SpanHandler.set_workflow_properties(span)
|
|
159
|
+
|
|
160
|
+
# Set custom attributes and events using common method
|
|
161
|
+
_setup_span_attributes_and_events(span, attributes, events)
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
yield
|
|
165
|
+
finally:
|
|
166
|
+
pass
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
except Exception as e:
|
|
170
|
+
logger.warning(f"Failed in monocle_trace: {e}")
|
|
171
|
+
yield # Still yield to not break the context manager
|
|
172
|
+
|
|
173
|
+
@asynccontextmanager
|
|
174
|
+
async def amonocle_trace(
|
|
175
|
+
span_name: Optional[str] = None,
|
|
176
|
+
attributes: Optional[Dict[str, Any]] = None,
|
|
177
|
+
events: Optional[List[Dict[str, Any]]] = None
|
|
178
|
+
):
|
|
179
|
+
"""
|
|
180
|
+
Async context manager to start and stop a trace. All the spans, across traces created within the encapsulated code will have same trace ID
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
span_name: Optional custom span name.
|
|
184
|
+
attributes: Optional dictionary of custom attributes to set on the span.
|
|
185
|
+
events: Optional list of events to add to the span at start.
|
|
186
|
+
"""
|
|
187
|
+
try:
|
|
188
|
+
tracer = get_tracer(instrumenting_module_name=MONOCLE_INSTRUMENTOR, tracer_provider=get_tracer_provider())
|
|
189
|
+
span_name = span_name or "custom_span"
|
|
190
|
+
|
|
191
|
+
with tracer.start_as_current_span(span_name) as span:
|
|
192
|
+
# Set default monocle attributes
|
|
193
|
+
SpanHandler.set_default_monocle_attributes(span)
|
|
194
|
+
if SpanHandler.is_root_span(span):
|
|
195
|
+
SpanHandler.set_workflow_properties(span)
|
|
196
|
+
|
|
197
|
+
# Set custom attributes and events using common method
|
|
198
|
+
_setup_span_attributes_and_events(span, attributes, events)
|
|
199
|
+
|
|
200
|
+
try:
|
|
201
|
+
yield
|
|
202
|
+
finally:
|
|
203
|
+
pass
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
except Exception as e:
|
|
207
|
+
logger.warning(f"Failed in amonocle_trace: {e}")
|
|
208
|
+
yield # Still yield to not break the context manager
|
|
209
|
+
|
|
210
|
+
@contextmanager
|
|
211
|
+
def monocle_trace_scope(
|
|
212
|
+
scope_name: str,
|
|
213
|
+
scope_value: Optional[str] = None
|
|
214
|
+
):
|
|
215
|
+
"""
|
|
216
|
+
Context manager to start and stop a scope. All the spans, across traces created within the encapsulated code will have the scope attached.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
scope_name: The name of the scope.
|
|
220
|
+
scope_value: Optional value of the scope. If None, a random UUID will be generated.
|
|
221
|
+
"""
|
|
222
|
+
token = start_scope(scope_name, scope_value)
|
|
223
|
+
try:
|
|
224
|
+
yield
|
|
225
|
+
finally:
|
|
226
|
+
stop_scope(token)
|
|
227
|
+
|
|
228
|
+
@asynccontextmanager
|
|
229
|
+
async def amonocle_trace_scope(
|
|
230
|
+
scope_name: str,
|
|
231
|
+
scope_value: Optional[str] = None
|
|
232
|
+
):
|
|
233
|
+
"""
|
|
234
|
+
Async context manager to start and stop a scope. All the spans, across traces created within the encapsulated code will have the scope attached.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
scope_name: The name of the scope.
|
|
238
|
+
scope_value: Optional value of the scope. If None, a random UUID will be generated.
|
|
239
|
+
"""
|
|
240
|
+
token = start_scope(scope_name, scope_value)
|
|
241
|
+
try:
|
|
242
|
+
yield
|
|
243
|
+
finally:
|
|
244
|
+
stop_scope(token)
|
|
245
|
+
|
|
246
|
+
def monocle_trace_scope_method(
|
|
247
|
+
scope_name: str,
|
|
248
|
+
scope_value: Optional[str] = None
|
|
249
|
+
):
|
|
250
|
+
"""
|
|
251
|
+
Decorator to start and stop a scope for a method. All the spans, across traces created in the method will have the scope attached.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
scope_name: The name of the scope.
|
|
255
|
+
scope_value: Optional value of the scope. If None, a random UUID will be generated.
|
|
256
|
+
"""
|
|
257
|
+
def decorator(func):
|
|
258
|
+
if inspect.iscoroutinefunction(func):
|
|
259
|
+
@wraps(func)
|
|
260
|
+
async def wrapper(*args, **kwargs):
|
|
261
|
+
async with amonocle_trace_scope(
|
|
262
|
+
scope_name, scope_value
|
|
263
|
+
):
|
|
264
|
+
result = await func(*args, **kwargs)
|
|
265
|
+
return result
|
|
266
|
+
return wrapper
|
|
267
|
+
else:
|
|
268
|
+
@wraps(func)
|
|
269
|
+
def wrapper(*args, **kwargs):
|
|
270
|
+
with monocle_trace_scope(
|
|
271
|
+
scope_name, scope_value
|
|
272
|
+
):
|
|
273
|
+
result = func(*args, **kwargs)
|
|
274
|
+
return result
|
|
275
|
+
return wrapper
|
|
276
|
+
return decorator
|
|
277
|
+
|
|
278
|
+
def monocle_trace_method(
|
|
279
|
+
span_name: Optional[str] = None
|
|
280
|
+
):
|
|
281
|
+
"""
|
|
282
|
+
Decorator to start and stop a trace for a method. All the spans created in the method will be part of the same trace.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
span_name: Optional custom span name. If None, uses the decorated function's name.
|
|
286
|
+
"""
|
|
287
|
+
|
|
288
|
+
def decorator(func):
|
|
289
|
+
tracer = get_tracer(instrumenting_module_name=MONOCLE_INSTRUMENTOR, tracer_provider=get_tracer_provider())
|
|
290
|
+
handler = SpanHandler()
|
|
291
|
+
source_path= func.__code__.co_filename + ":" + str(func.__code__.co_firstlineno)
|
|
292
|
+
# Use function name as span name if not provided
|
|
293
|
+
effective_span_name = span_name or func.__name__ or "custom_span"
|
|
294
|
+
|
|
295
|
+
if inspect.iscoroutinefunction(func):
|
|
296
|
+
@wraps(func)
|
|
297
|
+
async def wrapper(*args, **kwargs):
|
|
298
|
+
return await atask_wrapper(
|
|
299
|
+
tracer=tracer,
|
|
300
|
+
handler=handler,
|
|
301
|
+
to_wrap={
|
|
302
|
+
"span_name": effective_span_name,
|
|
303
|
+
"output_processor":{
|
|
304
|
+
"type": "custom",
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
)( wrapped=func,
|
|
308
|
+
instance=None,
|
|
309
|
+
source_path=source_path,
|
|
310
|
+
args=args,
|
|
311
|
+
kwargs=kwargs)
|
|
312
|
+
return wrapper
|
|
313
|
+
else:
|
|
314
|
+
@wraps(func)
|
|
315
|
+
def wrapper(*args, **kwargs):
|
|
316
|
+
return task_wrapper(
|
|
317
|
+
tracer=tracer,
|
|
318
|
+
handler=handler,
|
|
319
|
+
to_wrap={
|
|
320
|
+
"span_name": effective_span_name,
|
|
321
|
+
"output_processor":{
|
|
322
|
+
"type": "custom",
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
)( wrapped=func,
|
|
326
|
+
instance=None,
|
|
327
|
+
source_path=source_path,
|
|
328
|
+
args=args,
|
|
329
|
+
kwargs=kwargs)
|
|
330
|
+
return wrapper
|
|
331
|
+
return decorator
|
|
332
|
+
|
|
333
|
+
def monocle_trace_http_route(func):
|
|
334
|
+
"""
|
|
335
|
+
Decorator to start and stop a continue traces and scope for a http route. It will also initiate new scopes from the http headers if configured in ``monocle_scopes.json``
|
|
336
|
+
All the spans, across traces created in the route will have the scope attached.
|
|
337
|
+
"""
|
|
338
|
+
if inspect.iscoroutinefunction(func):
|
|
339
|
+
@wraps(func)
|
|
340
|
+
async def wrapper(*args, **kwargs):
|
|
341
|
+
return await http_async_route_handler(func, *args, **kwargs)
|
|
342
|
+
return wrapper
|
|
343
|
+
else:
|
|
344
|
+
@wraps(func)
|
|
345
|
+
def wrapper(*args, **kwargs):
|
|
346
|
+
return http_route_handler(func, *args, **kwargs)
|
|
347
|
+
return wrapper
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _setup_span_attributes_and_events(
|
|
351
|
+
span,
|
|
352
|
+
attributes: Optional[Dict[str, Any]] = None,
|
|
353
|
+
events: Optional[List[Dict[str, Any]]] = None
|
|
354
|
+
) -> None:
|
|
355
|
+
"""
|
|
356
|
+
Common method to set attributes and events on a span.
|
|
357
|
+
|
|
358
|
+
Args:
|
|
359
|
+
span: The span to configure
|
|
360
|
+
attributes: Optional dictionary of custom attributes to set on the span
|
|
361
|
+
events: Optional list of events to add to the span
|
|
362
|
+
"""
|
|
363
|
+
# Set custom attributes if provided
|
|
364
|
+
if attributes:
|
|
365
|
+
for key, value in attributes.items():
|
|
366
|
+
if value is not None:
|
|
367
|
+
span.set_attribute(key, value)
|
|
368
|
+
|
|
369
|
+
# Add custom events if provided
|
|
370
|
+
if events:
|
|
371
|
+
for event in events:
|
|
372
|
+
event_name = event.get('name')
|
|
373
|
+
event_attributes = event.get('attributes', {})
|
|
374
|
+
if event_name:
|
|
375
|
+
span.add_event(event_name, event_attributes)
|
|
376
|
+
|
|
@@ -11,17 +11,30 @@ from monocle_apptrace.instrumentation.common.constants import (
|
|
|
11
11
|
MONOCLE_SDK_VERSION, MONOCLE_SDK_LANGUAGE, MONOCLE_DETECTED_SPAN_ERROR
|
|
12
12
|
)
|
|
13
13
|
from monocle_apptrace.instrumentation.common.utils import set_attribute, get_scopes, MonocleSpanException, get_monocle_version
|
|
14
|
-
from monocle_apptrace.instrumentation.common.constants import WORKFLOW_TYPE_KEY, WORKFLOW_TYPE_GENERIC
|
|
14
|
+
from monocle_apptrace.instrumentation.common.constants import WORKFLOW_TYPE_KEY, WORKFLOW_TYPE_GENERIC, CHILD_ERROR_CODE
|
|
15
15
|
|
|
16
16
|
logger = logging.getLogger(__name__)
|
|
17
17
|
|
|
18
18
|
WORKFLOW_TYPE_MAP = {
|
|
19
|
+
"llama_index.core.agent.workflow": WORKFLOW_TYPE_GENERIC,
|
|
19
20
|
"llama_index": "workflow.llamaindex",
|
|
20
21
|
"langchain": "workflow.langchain",
|
|
21
22
|
"haystack": "workflow.haystack",
|
|
22
23
|
"teams.ai": "workflow.teams_ai",
|
|
24
|
+
"langgraph": "workflow.langgraph",
|
|
25
|
+
"openai": "workflow.openai",
|
|
26
|
+
"anthropic": "workflow.anthropic",
|
|
27
|
+
"gemini": "workflow.gemini",
|
|
28
|
+
"litellm": "workflow.litellm",
|
|
23
29
|
}
|
|
24
30
|
|
|
31
|
+
FRAMEWORK_WORKFLOW_LIST = [
|
|
32
|
+
"workflow.llamaindex",
|
|
33
|
+
"workflow.langchain",
|
|
34
|
+
"workflow.haystack",
|
|
35
|
+
"workflow.teams_ai",
|
|
36
|
+
"workflow.litellm",
|
|
37
|
+
]
|
|
25
38
|
class SpanHandler:
|
|
26
39
|
|
|
27
40
|
def __init__(self,instrumentor=None):
|
|
@@ -36,7 +49,7 @@ class SpanHandler:
|
|
|
36
49
|
def pre_tracing(self, to_wrap, wrapped, instance, args, kwargs):
|
|
37
50
|
pass
|
|
38
51
|
|
|
39
|
-
def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value):
|
|
52
|
+
def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value, token=None):
|
|
40
53
|
pass
|
|
41
54
|
|
|
42
55
|
def skip_span(self, to_wrap, wrapped, instance, args, kwargs) -> bool:
|
|
@@ -55,8 +68,11 @@ class SpanHandler:
|
|
|
55
68
|
return span_type
|
|
56
69
|
|
|
57
70
|
def pre_task_processing(self, to_wrap, wrapped, instance, args,kwargs, span):
|
|
58
|
-
|
|
59
|
-
|
|
71
|
+
try:
|
|
72
|
+
if "pipeline" in to_wrap['package']:
|
|
73
|
+
set_attribute(QUERY, args[0]['prompt_builder']['question'])
|
|
74
|
+
except Exception as e:
|
|
75
|
+
logger.warning("Warning: Error occurred in pre_task_processing: %s", str(e))
|
|
60
76
|
|
|
61
77
|
@staticmethod
|
|
62
78
|
def set_default_monocle_attributes(span: Span, source_path = "" ):
|
|
@@ -66,6 +82,9 @@ class SpanHandler:
|
|
|
66
82
|
span.set_attribute("span_source", source_path)
|
|
67
83
|
for scope_key, scope_value in get_scopes().items():
|
|
68
84
|
span.set_attribute(f"scope.{scope_key}", scope_value)
|
|
85
|
+
workflow_name = SpanHandler.get_workflow_name(span=span)
|
|
86
|
+
if workflow_name:
|
|
87
|
+
span.set_attribute("workflow.name", workflow_name)
|
|
69
88
|
|
|
70
89
|
@staticmethod
|
|
71
90
|
def set_workflow_properties(span: Span, to_wrap = None):
|
|
@@ -75,17 +94,14 @@ class SpanHandler:
|
|
|
75
94
|
|
|
76
95
|
@staticmethod
|
|
77
96
|
def set_non_workflow_properties(span: Span, to_wrap = None):
|
|
78
|
-
workflow_name = SpanHandler.get_workflow_name(span=span)
|
|
79
|
-
if workflow_name:
|
|
80
|
-
span.set_attribute("workflow.name", workflow_name)
|
|
81
97
|
span.set_attribute("span.type", "generic")
|
|
82
98
|
|
|
83
|
-
def post_task_processing(self, to_wrap, wrapped, instance, args, kwargs, result, span:Span):
|
|
99
|
+
def post_task_processing(self, to_wrap, wrapped, instance, args, kwargs, result, ex, span:Span, parent_span:Span):
|
|
84
100
|
pass
|
|
85
101
|
|
|
86
102
|
def hydrate_span(self, to_wrap, wrapped, instance, args, kwargs, result, span, parent_span = None, ex:Exception = None) -> bool:
|
|
87
103
|
try:
|
|
88
|
-
detected_error_in_attribute = self.hydrate_attributes(to_wrap, wrapped, instance, args, kwargs, result, span)
|
|
104
|
+
detected_error_in_attribute = self.hydrate_attributes(to_wrap, wrapped, instance, args, kwargs, result, span, parent_span)
|
|
89
105
|
detected_error_in_event = self.hydrate_events(to_wrap, wrapped, instance, args, kwargs, result, span, parent_span, ex)
|
|
90
106
|
if detected_error_in_attribute or detected_error_in_event:
|
|
91
107
|
span.set_attribute(MONOCLE_DETECTED_SPAN_ERROR, True)
|
|
@@ -93,7 +109,7 @@ class SpanHandler:
|
|
|
93
109
|
if span.status.status_code == StatusCode.UNSET and ex is None:
|
|
94
110
|
span.set_status(StatusCode.OK)
|
|
95
111
|
|
|
96
|
-
def hydrate_attributes(self, to_wrap, wrapped, instance, args, kwargs, result, span:Span) -> bool:
|
|
112
|
+
def hydrate_attributes(self, to_wrap, wrapped, instance, args, kwargs, result, span:Span, parent_span:Span) -> bool:
|
|
97
113
|
detected_error:bool = False
|
|
98
114
|
span_index = 0
|
|
99
115
|
if SpanHandler.is_root_span(span):
|
|
@@ -104,6 +120,7 @@ class SpanHandler:
|
|
|
104
120
|
skip_processors:list[str] = self.skip_processor(to_wrap, wrapped, instance, span, args, kwargs) or []
|
|
105
121
|
|
|
106
122
|
if 'attributes' in output_processor and 'attributes' not in skip_processors:
|
|
123
|
+
arguments = {"instance":instance, "args":args, "kwargs":kwargs, "result":result, "parent_span":parent_span, "span":span}
|
|
107
124
|
for processors in output_processor["attributes"]:
|
|
108
125
|
for processor in processors:
|
|
109
126
|
attribute = processor.get('attribute')
|
|
@@ -112,10 +129,9 @@ class SpanHandler:
|
|
|
112
129
|
if attribute and accessor:
|
|
113
130
|
attribute_name = f"entity.{span_index+1}.{attribute}"
|
|
114
131
|
try:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
span.set_attribute(attribute_name, result)
|
|
132
|
+
processor_result = accessor(arguments)
|
|
133
|
+
if processor_result and isinstance(processor_result, (str, list)):
|
|
134
|
+
span.set_attribute(attribute_name, processor_result)
|
|
119
135
|
except MonocleSpanException as e:
|
|
120
136
|
span.set_status(StatusCode.ERROR, e.message)
|
|
121
137
|
detected_error = True
|
|
@@ -134,13 +150,13 @@ class SpanHandler:
|
|
|
134
150
|
span.set_attribute("entity.count", span_index)
|
|
135
151
|
return detected_error
|
|
136
152
|
|
|
137
|
-
def hydrate_events(self, to_wrap, wrapped, instance, args, kwargs, ret_result, span, parent_span=None, ex:Exception=None) -> bool:
|
|
153
|
+
def hydrate_events(self, to_wrap, wrapped, instance, args, kwargs, ret_result, span: Span, parent_span=None, ex:Exception=None) -> bool:
|
|
138
154
|
detected_error:bool = False
|
|
139
155
|
if 'output_processor' in to_wrap and to_wrap["output_processor"] is not None:
|
|
140
156
|
output_processor=to_wrap['output_processor']
|
|
141
157
|
skip_processors:list[str] = self.skip_processor(to_wrap, wrapped, instance, span, args, kwargs) or []
|
|
142
158
|
|
|
143
|
-
arguments = {"instance": instance, "args": args, "kwargs": kwargs, "result": ret_result, "exception":ex}
|
|
159
|
+
arguments = {"instance": instance, "args": args, "kwargs": kwargs, "result": ret_result, "exception":ex, "parent_span":parent_span, "span": span}
|
|
144
160
|
# Process events if they are defined in the output_processor.
|
|
145
161
|
# In case of inference.modelapi skip the event processing unless the span has an exception
|
|
146
162
|
if 'events' in output_processor and ('events' not in skip_processors or ex is not None):
|
|
@@ -170,10 +186,16 @@ class SpanHandler:
|
|
|
170
186
|
except Exception as e:
|
|
171
187
|
logger.debug(f"Error evaluating accessor for attribute '{attribute_key}': {e}")
|
|
172
188
|
matching_timestamp = getattr(ret_result, "timestamps", {}).get(event_name, None)
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
189
|
+
alreadyExist = False
|
|
190
|
+
for existing_event in span.events:
|
|
191
|
+
if event_name == existing_event.name:
|
|
192
|
+
existing_event.attributes._dict.update(event_attributes)
|
|
193
|
+
alreadyExist = True
|
|
194
|
+
if not alreadyExist:
|
|
195
|
+
if isinstance(matching_timestamp, int):
|
|
196
|
+
span.add_event(name=event_name, attributes=event_attributes, timestamp=matching_timestamp)
|
|
197
|
+
else:
|
|
198
|
+
span.add_event(name=event_name, attributes=event_attributes)
|
|
177
199
|
return detected_error
|
|
178
200
|
|
|
179
201
|
@staticmethod
|
|
@@ -187,6 +209,16 @@ class SpanHandler:
|
|
|
187
209
|
workflow_type = SpanHandler.get_workflow_type(to_wrap)
|
|
188
210
|
span.set_attribute(f"entity.{span_index}.type", workflow_type)
|
|
189
211
|
|
|
212
|
+
def get_workflow_name_in_progress(self) -> str:
|
|
213
|
+
return get_value(WORKFLOW_TYPE_KEY)
|
|
214
|
+
|
|
215
|
+
@staticmethod
|
|
216
|
+
def is_framework_workflow(workflow_type) -> bool:
|
|
217
|
+
return workflow_type in FRAMEWORK_WORKFLOW_LIST
|
|
218
|
+
|
|
219
|
+
def is_framework_span_in_progress(self) -> bool:
|
|
220
|
+
return SpanHandler.is_framework_workflow(self.get_workflow_name_in_progress())
|
|
221
|
+
|
|
190
222
|
@staticmethod
|
|
191
223
|
def get_workflow_type(to_wrap):
|
|
192
224
|
# workflow type
|
|
@@ -194,7 +226,7 @@ class SpanHandler:
|
|
|
194
226
|
if to_wrap is not None:
|
|
195
227
|
package_name = to_wrap.get('package')
|
|
196
228
|
for (package, framework_workflow_type) in WORKFLOW_TYPE_MAP.items():
|
|
197
|
-
if (package_name is not None and package
|
|
229
|
+
if (package_name is not None and package_name.startswith(package)):
|
|
198
230
|
workflow_type = framework_workflow_type
|
|
199
231
|
break
|
|
200
232
|
return workflow_type
|
|
@@ -231,7 +263,7 @@ class SpanHandler:
|
|
|
231
263
|
token = None
|
|
232
264
|
if to_wrap:
|
|
233
265
|
workflow_type = SpanHandler.get_workflow_type(to_wrap)
|
|
234
|
-
if workflow_type
|
|
266
|
+
if SpanHandler.is_framework_workflow(workflow_type):
|
|
235
267
|
token = attach(set_value(WORKFLOW_TYPE_KEY,
|
|
236
268
|
SpanHandler.get_workflow_type(to_wrap), context))
|
|
237
269
|
return token
|
|
@@ -252,21 +284,15 @@ class SpanHandler:
|
|
|
252
284
|
|
|
253
285
|
|
|
254
286
|
class NonFrameworkSpanHandler(SpanHandler):
|
|
255
|
-
|
|
256
|
-
def get_workflow_name_in_progress(self) -> str:
|
|
257
|
-
return get_value(WORKFLOW_TYPE_KEY)
|
|
258
|
-
|
|
259
|
-
def is_framework_span_in_progess(self) -> bool:
|
|
260
|
-
return self.get_workflow_name_in_progress() in WORKFLOW_TYPE_MAP.values()
|
|
261
|
-
|
|
262
287
|
# If the language framework is being executed, then skip generating direct openAI attributes and events
|
|
263
288
|
def skip_processor(self, to_wrap, wrapped, instance, span, args, kwargs) -> list[str]:
|
|
264
|
-
if
|
|
289
|
+
if super().is_framework_span_in_progress():
|
|
265
290
|
return ["attributes", "events"]
|
|
266
291
|
|
|
267
292
|
def set_span_type(self, to_wrap, wrapped, instance, output_processor, span:Span, args, kwargs) -> str:
|
|
268
293
|
span_type = super().set_span_type(to_wrap, wrapped, instance, output_processor, span, args, kwargs)
|
|
269
|
-
if self.
|
|
294
|
+
if self.is_framework_span_in_progress() and span_type is not None:
|
|
270
295
|
span_type = span_type+".modelapi"
|
|
271
296
|
span.set_attribute("span.type", span_type)
|
|
272
|
-
return span_type
|
|
297
|
+
return span_type
|
|
298
|
+
|