ioa-observe-sdk 1.0.16__py3-none-any.whl → 1.0.18__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.
- ioa_observe/sdk/instrumentations/slim.py +382 -128
- {ioa_observe_sdk-1.0.16.dist-info → ioa_observe_sdk-1.0.18.dist-info}/METADATA +1 -1
- {ioa_observe_sdk-1.0.16.dist-info → ioa_observe_sdk-1.0.18.dist-info}/RECORD +6 -6
- {ioa_observe_sdk-1.0.16.dist-info → ioa_observe_sdk-1.0.18.dist-info}/WHEEL +0 -0
- {ioa_observe_sdk-1.0.16.dist-info → ioa_observe_sdk-1.0.18.dist-info}/licenses/LICENSE.md +0 -0
- {ioa_observe_sdk-1.0.16.dist-info → ioa_observe_sdk-1.0.18.dist-info}/top_level.txt +0 -0
|
@@ -7,7 +7,7 @@ import json
|
|
|
7
7
|
import base64
|
|
8
8
|
import threading
|
|
9
9
|
|
|
10
|
-
from opentelemetry import baggage
|
|
10
|
+
from opentelemetry import baggage, context
|
|
11
11
|
from opentelemetry.baggage.propagation import W3CBaggagePropagator
|
|
12
12
|
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
|
13
13
|
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator
|
|
@@ -16,7 +16,7 @@ from ioa_observe.sdk import TracerWrapper
|
|
|
16
16
|
from ioa_observe.sdk.client import kv_store
|
|
17
17
|
from ioa_observe.sdk.tracing import set_session_id, get_current_traceparent
|
|
18
18
|
|
|
19
|
-
_instruments = ("slim-bindings >= 0.
|
|
19
|
+
_instruments = ("slim-bindings >= 0.4",)
|
|
20
20
|
_global_tracer = None
|
|
21
21
|
_kv_lock = threading.RLock() # Add thread-safety for kv_store operations
|
|
22
22
|
|
|
@@ -38,123 +38,281 @@ class SLIMInstrumentor(BaseInstrumentor):
|
|
|
38
38
|
"No module named 'slim_bindings'. Please install it first."
|
|
39
39
|
)
|
|
40
40
|
|
|
41
|
-
# Instrument `publish`
|
|
42
|
-
|
|
41
|
+
# Instrument `publish` method - handles multiple signatures
|
|
42
|
+
if hasattr(slim_bindings.Slim, "publish"):
|
|
43
|
+
original_publish = slim_bindings.Slim.publish
|
|
44
|
+
|
|
45
|
+
@functools.wraps(original_publish)
|
|
46
|
+
async def instrumented_publish(self, *args, **kwargs):
|
|
47
|
+
if _global_tracer:
|
|
48
|
+
with _global_tracer.start_as_current_span("slim.publish") as span:
|
|
49
|
+
traceparent = get_current_traceparent()
|
|
50
|
+
|
|
51
|
+
# Handle different publish signatures
|
|
52
|
+
# Definition 1: publish(session, message, topic_name) - v0.4.0+ group chat
|
|
53
|
+
# Definition 2: publish(session, message, organization, namespace, topic) - legacy
|
|
54
|
+
if len(args) >= 3:
|
|
55
|
+
session_arg = args[0] if args else None
|
|
56
|
+
if hasattr(session_arg, "id"):
|
|
57
|
+
span.set_attribute(
|
|
58
|
+
"slim.session.id", str(session_arg.id)
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Check if third argument is PyName (new API) or string (legacy API)
|
|
62
|
+
if len(args) >= 3 and hasattr(args[2], "organization"):
|
|
63
|
+
# New API: args[2] is PyName
|
|
64
|
+
topic_name = args[2]
|
|
65
|
+
span.set_attribute(
|
|
66
|
+
"slim.topic.organization", topic_name.organization
|
|
67
|
+
)
|
|
68
|
+
span.set_attribute(
|
|
69
|
+
"slim.topic.namespace", topic_name.namespace
|
|
70
|
+
)
|
|
71
|
+
span.set_attribute("slim.topic.app", topic_name.app)
|
|
72
|
+
else:
|
|
73
|
+
traceparent = get_current_traceparent()
|
|
43
74
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
try:
|
|
74
|
-
# If it's already a JSON structure, preserve it
|
|
75
|
-
original_message = json.loads(decoded_message)
|
|
76
|
-
if isinstance(original_message, dict):
|
|
77
|
-
# Preserve all original fields and merge/update headers
|
|
78
|
-
wrapped_message = original_message.copy()
|
|
79
|
-
existing_headers = wrapped_message.get("headers", {})
|
|
80
|
-
existing_headers.update(headers)
|
|
81
|
-
wrapped_message["headers"] = existing_headers
|
|
82
|
-
else:
|
|
83
|
-
# If it's not a dict, wrap it as payload
|
|
84
|
-
wrapped_message = {
|
|
85
|
-
"headers": headers,
|
|
86
|
-
"payload": original_message,
|
|
87
|
-
}
|
|
88
|
-
except json.JSONDecodeError:
|
|
89
|
-
# If it's not JSON, treat as raw payload
|
|
90
|
-
wrapped_message = {
|
|
91
|
-
"headers": headers,
|
|
92
|
-
"payload": decoded_message,
|
|
93
|
-
}
|
|
94
|
-
except UnicodeDecodeError:
|
|
95
|
-
# If it can't be decoded, base64 encode it
|
|
96
|
-
wrapped_message = {
|
|
97
|
-
"headers": headers,
|
|
98
|
-
"payload": base64.b64encode(message).decode("utf-8"),
|
|
99
|
-
}
|
|
100
|
-
elif isinstance(message, str):
|
|
101
|
-
try:
|
|
102
|
-
# Try to parse as JSON first
|
|
103
|
-
original_message = json.loads(message)
|
|
104
|
-
if isinstance(original_message, dict):
|
|
105
|
-
# Preserve all original fields and merge/update headers
|
|
106
|
-
wrapped_message = original_message.copy()
|
|
107
|
-
existing_headers = wrapped_message.get("headers", {})
|
|
108
|
-
existing_headers.update(headers)
|
|
109
|
-
wrapped_message["headers"] = existing_headers
|
|
75
|
+
# Thread-safe access to kv_store
|
|
76
|
+
session_id = None
|
|
77
|
+
if traceparent:
|
|
78
|
+
with _kv_lock:
|
|
79
|
+
session_id = kv_store.get(f"execution.{traceparent}")
|
|
80
|
+
if session_id:
|
|
81
|
+
kv_store.set(f"execution.{traceparent}", session_id)
|
|
82
|
+
|
|
83
|
+
headers = {
|
|
84
|
+
"session_id": session_id if session_id else None,
|
|
85
|
+
"traceparent": traceparent,
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# Set baggage context
|
|
89
|
+
if traceparent and session_id:
|
|
90
|
+
baggage.set_baggage(f"execution.{traceparent}", session_id)
|
|
91
|
+
|
|
92
|
+
# Wrap message with headers - handle different message positions
|
|
93
|
+
message_arg_index = 1 # message will typically be the second argument
|
|
94
|
+
if len(args) > message_arg_index:
|
|
95
|
+
original_args = list(args)
|
|
96
|
+
message = original_args[message_arg_index]
|
|
97
|
+
wrapped_message = SLIMInstrumentor._wrap_message_with_headers(
|
|
98
|
+
self, message, headers
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Convert wrapped message back to bytes if needed
|
|
102
|
+
if isinstance(wrapped_message, dict):
|
|
103
|
+
message_to_send = json.dumps(wrapped_message).encode("utf-8")
|
|
110
104
|
else:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
105
|
+
message_to_send = wrapped_message
|
|
106
|
+
|
|
107
|
+
original_args[message_arg_index] = message_to_send
|
|
108
|
+
args = tuple(original_args)
|
|
109
|
+
|
|
110
|
+
return await original_publish(self, *args, **kwargs)
|
|
111
|
+
|
|
112
|
+
slim_bindings.Slim.publish = instrumented_publish
|
|
113
|
+
|
|
114
|
+
# Instrument `publish_to` (new v0.4.0+ method)
|
|
115
|
+
if hasattr(slim_bindings.Slim, "publish_to"):
|
|
116
|
+
original_publish_to = slim_bindings.Slim.publish_to
|
|
117
|
+
|
|
118
|
+
@functools.wraps(original_publish_to)
|
|
119
|
+
async def instrumented_publish_to(
|
|
120
|
+
self, session_info, message, *args, **kwargs
|
|
121
|
+
):
|
|
122
|
+
if _global_tracer:
|
|
123
|
+
with _global_tracer.start_as_current_span(
|
|
124
|
+
"slim.publish_to"
|
|
125
|
+
) as span:
|
|
126
|
+
traceparent = get_current_traceparent()
|
|
127
|
+
|
|
128
|
+
# Add session context to span
|
|
129
|
+
if hasattr(session_info, "id"):
|
|
130
|
+
span.set_attribute("slim.session.id", str(session_info.id))
|
|
131
|
+
else:
|
|
132
|
+
traceparent = get_current_traceparent()
|
|
133
|
+
|
|
134
|
+
# Thread-safe access to kv_store
|
|
135
|
+
session_id = None
|
|
136
|
+
if traceparent:
|
|
137
|
+
with _kv_lock:
|
|
138
|
+
session_id = kv_store.get(f"execution.{traceparent}")
|
|
139
|
+
if session_id:
|
|
140
|
+
kv_store.set(f"execution.{traceparent}", session_id)
|
|
141
|
+
|
|
142
|
+
headers = {
|
|
143
|
+
"session_id": session_id if session_id else None,
|
|
144
|
+
"traceparent": traceparent,
|
|
145
|
+
"slim_session_id": str(session_info.id)
|
|
146
|
+
if hasattr(session_info, "id")
|
|
147
|
+
else None,
|
|
133
148
|
}
|
|
134
149
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
150
|
+
# Set baggage context
|
|
151
|
+
if traceparent and session_id:
|
|
152
|
+
baggage.set_baggage(f"execution.{traceparent}", session_id)
|
|
153
|
+
|
|
154
|
+
wrapped_message = SLIMInstrumentor._wrap_message_with_headers(
|
|
155
|
+
self, message, headers
|
|
156
|
+
)
|
|
157
|
+
message_to_send = (
|
|
158
|
+
json.dumps(wrapped_message).encode("utf-8")
|
|
159
|
+
if isinstance(wrapped_message, dict)
|
|
160
|
+
else wrapped_message
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
return await original_publish_to(
|
|
164
|
+
self, session_info, message_to_send, *args, **kwargs
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
slim_bindings.Slim.publish_to = instrumented_publish_to
|
|
168
|
+
|
|
169
|
+
# Instrument `request_reply` (new v0.4.0+ method)
|
|
170
|
+
if hasattr(slim_bindings.Slim, "request_reply"):
|
|
171
|
+
original_request_reply = slim_bindings.Slim.request_reply
|
|
172
|
+
|
|
173
|
+
@functools.wraps(original_request_reply)
|
|
174
|
+
async def instrumented_request_reply(
|
|
175
|
+
self, session_info, message, remote_name, timeout=None, *args, **kwargs
|
|
176
|
+
):
|
|
177
|
+
if _global_tracer:
|
|
178
|
+
with _global_tracer.start_as_current_span(
|
|
179
|
+
"slim.request_reply"
|
|
180
|
+
) as span:
|
|
181
|
+
traceparent = get_current_traceparent()
|
|
182
|
+
|
|
183
|
+
# Add context to span
|
|
184
|
+
if hasattr(session_info, "id"):
|
|
185
|
+
span.set_attribute("slim.session.id", str(session_info.id))
|
|
186
|
+
if hasattr(remote_name, "organization"):
|
|
187
|
+
span.set_attribute(
|
|
188
|
+
"slim.remote.organization", remote_name.organization
|
|
189
|
+
)
|
|
190
|
+
span.set_attribute(
|
|
191
|
+
"slim.remote.namespace", remote_name.namespace
|
|
192
|
+
)
|
|
193
|
+
span.set_attribute("slim.remote.app", remote_name.app)
|
|
194
|
+
else:
|
|
195
|
+
traceparent = get_current_traceparent()
|
|
147
196
|
|
|
148
|
-
|
|
197
|
+
# Thread-safe access to kv_store
|
|
198
|
+
session_id = None
|
|
199
|
+
if traceparent:
|
|
200
|
+
with _kv_lock:
|
|
201
|
+
session_id = kv_store.get(f"execution.{traceparent}")
|
|
202
|
+
if session_id:
|
|
203
|
+
kv_store.set(f"execution.{traceparent}", session_id)
|
|
204
|
+
|
|
205
|
+
headers = {
|
|
206
|
+
"session_id": session_id if session_id else None,
|
|
207
|
+
"traceparent": traceparent,
|
|
208
|
+
"slim_session_id": str(session_info.id)
|
|
209
|
+
if hasattr(session_info, "id")
|
|
210
|
+
else None,
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
# Set baggage context
|
|
214
|
+
if traceparent and session_id:
|
|
215
|
+
baggage.set_baggage(f"execution.{traceparent}", session_id)
|
|
216
|
+
|
|
217
|
+
wrapped_message = SLIMInstrumentor._wrap_message_with_headers(
|
|
218
|
+
self, message, headers
|
|
219
|
+
)
|
|
220
|
+
message_to_send = (
|
|
221
|
+
json.dumps(wrapped_message).encode("utf-8")
|
|
222
|
+
if isinstance(wrapped_message, dict)
|
|
223
|
+
else wrapped_message
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
kwargs_with_timeout = kwargs.copy()
|
|
227
|
+
if timeout is not None:
|
|
228
|
+
kwargs_with_timeout["timeout"] = timeout
|
|
229
|
+
|
|
230
|
+
return await original_request_reply(
|
|
231
|
+
self,
|
|
232
|
+
session_info,
|
|
233
|
+
message_to_send,
|
|
234
|
+
remote_name,
|
|
235
|
+
**kwargs_with_timeout,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
slim_bindings.Slim.request_reply = instrumented_request_reply
|
|
239
|
+
|
|
240
|
+
# Instrument `invite` (new v0.4.0+ method for group chat)
|
|
241
|
+
if hasattr(slim_bindings.Slim, "invite"):
|
|
242
|
+
original_invite = slim_bindings.Slim.invite
|
|
243
|
+
|
|
244
|
+
@functools.wraps(original_invite)
|
|
245
|
+
async def instrumented_invite(
|
|
246
|
+
self, session_info, participant_name, *args, **kwargs
|
|
247
|
+
):
|
|
248
|
+
if _global_tracer:
|
|
249
|
+
with _global_tracer.start_as_current_span("slim.invite") as span:
|
|
250
|
+
# Add context to span
|
|
251
|
+
if hasattr(session_info, "id"):
|
|
252
|
+
span.set_attribute("slim.session.id", str(session_info.id))
|
|
253
|
+
if hasattr(participant_name, "organization"):
|
|
254
|
+
span.set_attribute(
|
|
255
|
+
"slim.participant.organization",
|
|
256
|
+
participant_name.organization,
|
|
257
|
+
)
|
|
258
|
+
span.set_attribute(
|
|
259
|
+
"slim.participant.namespace", participant_name.namespace
|
|
260
|
+
)
|
|
261
|
+
span.set_attribute(
|
|
262
|
+
"slim.participant.app", participant_name.app
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
return await original_invite(
|
|
266
|
+
self, session_info, participant_name, *args, **kwargs
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
slim_bindings.Slim.invite = instrumented_invite
|
|
270
|
+
|
|
271
|
+
# Instrument `set_route` (new v0.4.0+ method)
|
|
272
|
+
if hasattr(slim_bindings.Slim, "set_route"):
|
|
273
|
+
original_set_route = slim_bindings.Slim.set_route
|
|
274
|
+
|
|
275
|
+
@functools.wraps(original_set_route)
|
|
276
|
+
async def instrumented_set_route(self, remote_name, *args, **kwargs):
|
|
277
|
+
if _global_tracer:
|
|
278
|
+
with _global_tracer.start_as_current_span("slim.set_route") as span:
|
|
279
|
+
# Add context to span
|
|
280
|
+
if hasattr(remote_name, "organization"):
|
|
281
|
+
span.set_attribute(
|
|
282
|
+
"slim.route.organization", remote_name.organization
|
|
283
|
+
)
|
|
284
|
+
span.set_attribute(
|
|
285
|
+
"slim.route.namespace", remote_name.namespace
|
|
286
|
+
)
|
|
287
|
+
span.set_attribute("slim.route.app", remote_name.app)
|
|
288
|
+
|
|
289
|
+
return await original_set_route(self, remote_name, *args, **kwargs)
|
|
290
|
+
|
|
291
|
+
slim_bindings.Slim.set_route = instrumented_set_route
|
|
149
292
|
|
|
150
293
|
# Instrument `receive`
|
|
151
294
|
original_receive = slim_bindings.Slim.receive
|
|
152
295
|
|
|
153
296
|
@functools.wraps(original_receive)
|
|
154
|
-
async def instrumented_receive(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
297
|
+
async def instrumented_receive(
|
|
298
|
+
self, session=None, timeout=None, *args, **kwargs
|
|
299
|
+
):
|
|
300
|
+
# Handle both old and new API patterns
|
|
301
|
+
if session is not None or timeout is not None:
|
|
302
|
+
# New API pattern with session parameter
|
|
303
|
+
kwargs_with_params = kwargs.copy()
|
|
304
|
+
if session is not None:
|
|
305
|
+
kwargs_with_params["session"] = session
|
|
306
|
+
if timeout is not None:
|
|
307
|
+
kwargs_with_params["timeout"] = timeout
|
|
308
|
+
recv_session, raw_message = await original_receive(
|
|
309
|
+
self, **kwargs_with_params
|
|
310
|
+
)
|
|
311
|
+
else:
|
|
312
|
+
# Legacy API pattern
|
|
313
|
+
recv_session, raw_message = await original_receive(
|
|
314
|
+
self, *args, **kwargs
|
|
315
|
+
)
|
|
158
316
|
|
|
159
317
|
if raw_message is None:
|
|
160
318
|
return recv_session, raw_message
|
|
@@ -163,11 +321,11 @@ class SLIMInstrumentor(BaseInstrumentor):
|
|
|
163
321
|
message_dict = json.loads(raw_message.decode())
|
|
164
322
|
headers = message_dict.get("headers", {})
|
|
165
323
|
|
|
166
|
-
# Extract traceparent from headers
|
|
324
|
+
# Extract traceparent and session info from headers
|
|
167
325
|
traceparent = headers.get("traceparent")
|
|
168
326
|
session_id = headers.get("session_id")
|
|
169
327
|
|
|
170
|
-
#
|
|
328
|
+
# Create carrier for context propagation
|
|
171
329
|
carrier = {}
|
|
172
330
|
for key in ["traceparent", "Traceparent", "baggage", "Baggage"]:
|
|
173
331
|
if key.lower() in [k.lower() for k in headers.keys()]:
|
|
@@ -175,19 +333,24 @@ class SLIMInstrumentor(BaseInstrumentor):
|
|
|
175
333
|
if k.lower() == key.lower():
|
|
176
334
|
carrier[key.lower()] = headers[k]
|
|
177
335
|
|
|
178
|
-
# Restore
|
|
336
|
+
# Restore trace context
|
|
179
337
|
if carrier and traceparent:
|
|
180
338
|
ctx = TraceContextTextMapPropagator().extract(carrier=carrier)
|
|
181
339
|
ctx = W3CBaggagePropagator().extract(carrier=carrier, context=ctx)
|
|
182
340
|
|
|
183
|
-
#
|
|
184
|
-
|
|
185
|
-
# Pass the traceparent explicitly to prevent new context creation
|
|
186
|
-
set_session_id(session_id, traceparent=traceparent)
|
|
341
|
+
# Activate the restored context
|
|
342
|
+
token = context.attach(ctx)
|
|
187
343
|
|
|
188
|
-
|
|
189
|
-
with
|
|
190
|
-
|
|
344
|
+
try:
|
|
345
|
+
# Set execution ID with the restored context
|
|
346
|
+
if session_id and session_id != "None":
|
|
347
|
+
set_session_id(session_id, traceparent=traceparent)
|
|
348
|
+
|
|
349
|
+
# Store in kv_store with thread safety
|
|
350
|
+
with _kv_lock:
|
|
351
|
+
kv_store.set(f"execution.{traceparent}", session_id)
|
|
352
|
+
finally:
|
|
353
|
+
context.detach(token)
|
|
191
354
|
|
|
192
355
|
# Fallback: check stored execution ID if not found in headers
|
|
193
356
|
if traceparent and (not session_id or session_id == "None"):
|
|
@@ -197,21 +360,20 @@ class SLIMInstrumentor(BaseInstrumentor):
|
|
|
197
360
|
session_id = stored_session_id
|
|
198
361
|
set_session_id(session_id, traceparent=traceparent)
|
|
199
362
|
|
|
200
|
-
# Process the
|
|
201
|
-
# Remove tracing headers before returning the message
|
|
363
|
+
# Process and clean the message
|
|
202
364
|
message_to_return = message_dict.copy()
|
|
203
365
|
if "headers" in message_to_return:
|
|
204
366
|
headers_copy = message_to_return["headers"].copy()
|
|
205
367
|
# Remove tracing-specific headers but keep other headers
|
|
206
368
|
headers_copy.pop("traceparent", None)
|
|
207
369
|
headers_copy.pop("session_id", None)
|
|
370
|
+
headers_copy.pop("slim_session_id", None)
|
|
208
371
|
if headers_copy:
|
|
209
372
|
message_to_return["headers"] = headers_copy
|
|
210
373
|
else:
|
|
211
374
|
message_to_return.pop("headers", None)
|
|
212
375
|
|
|
213
|
-
#
|
|
214
|
-
# return just the payload for backward compatibility
|
|
376
|
+
# Return processed message
|
|
215
377
|
if len(message_to_return) == 1 and "payload" in message_to_return:
|
|
216
378
|
payload = message_to_return["payload"]
|
|
217
379
|
if isinstance(payload, str):
|
|
@@ -228,7 +390,6 @@ class SLIMInstrumentor(BaseInstrumentor):
|
|
|
228
390
|
"utf-8"
|
|
229
391
|
) if isinstance(payload, (dict, list)) else payload
|
|
230
392
|
else:
|
|
231
|
-
# Return the complete message structure with all original fields
|
|
232
393
|
return recv_session, json.dumps(message_to_return).encode("utf-8")
|
|
233
394
|
|
|
234
395
|
except Exception as e:
|
|
@@ -242,10 +403,88 @@ class SLIMInstrumentor(BaseInstrumentor):
|
|
|
242
403
|
|
|
243
404
|
@functools.wraps(original_connect)
|
|
244
405
|
async def instrumented_connect(self, *args, **kwargs):
|
|
245
|
-
|
|
406
|
+
if _global_tracer:
|
|
407
|
+
with _global_tracer.start_as_current_span("slim.connect"):
|
|
408
|
+
return await original_connect(self, *args, **kwargs)
|
|
409
|
+
else:
|
|
410
|
+
return await original_connect(self, *args, **kwargs)
|
|
246
411
|
|
|
247
412
|
slim_bindings.Slim.connect = instrumented_connect
|
|
248
413
|
|
|
414
|
+
# Instrument `create_session` (new v0.4.0+ method)
|
|
415
|
+
if hasattr(slim_bindings.Slim, "create_session"):
|
|
416
|
+
original_create_session = slim_bindings.Slim.create_session
|
|
417
|
+
|
|
418
|
+
@functools.wraps(original_create_session)
|
|
419
|
+
async def instrumented_create_session(self, config, *args, **kwargs):
|
|
420
|
+
if _global_tracer:
|
|
421
|
+
with _global_tracer.start_as_current_span(
|
|
422
|
+
"slim.create_session"
|
|
423
|
+
) as span:
|
|
424
|
+
session_info = await original_create_session(
|
|
425
|
+
self, config, *args, **kwargs
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
# Add session attributes to span
|
|
429
|
+
if hasattr(session_info, "id"):
|
|
430
|
+
span.set_attribute("slim.session.id", str(session_info.id))
|
|
431
|
+
|
|
432
|
+
return session_info
|
|
433
|
+
else:
|
|
434
|
+
return await original_create_session(self, config, *args, **kwargs)
|
|
435
|
+
|
|
436
|
+
slim_bindings.Slim.create_session = instrumented_create_session
|
|
437
|
+
|
|
438
|
+
def _wrap_message_with_headers(self, message, headers):
|
|
439
|
+
"""Helper method to wrap messages with headers consistently"""
|
|
440
|
+
if isinstance(message, bytes):
|
|
441
|
+
try:
|
|
442
|
+
decoded_message = message.decode("utf-8")
|
|
443
|
+
try:
|
|
444
|
+
original_message = json.loads(decoded_message)
|
|
445
|
+
if isinstance(original_message, dict):
|
|
446
|
+
wrapped_message = original_message.copy()
|
|
447
|
+
existing_headers = wrapped_message.get("headers", {})
|
|
448
|
+
existing_headers.update(headers)
|
|
449
|
+
wrapped_message["headers"] = existing_headers
|
|
450
|
+
else:
|
|
451
|
+
wrapped_message = {
|
|
452
|
+
"headers": headers,
|
|
453
|
+
"payload": original_message,
|
|
454
|
+
}
|
|
455
|
+
except json.JSONDecodeError:
|
|
456
|
+
wrapped_message = {"headers": headers, "payload": decoded_message}
|
|
457
|
+
except UnicodeDecodeError:
|
|
458
|
+
# Fix type annotation issue by ensuring message is bytes
|
|
459
|
+
encoded_message = (
|
|
460
|
+
message if isinstance(message, bytes) else message.encode("utf-8")
|
|
461
|
+
)
|
|
462
|
+
wrapped_message = {
|
|
463
|
+
"headers": headers,
|
|
464
|
+
"payload": base64.b64encode(encoded_message).decode("utf-8"),
|
|
465
|
+
}
|
|
466
|
+
elif isinstance(message, str):
|
|
467
|
+
try:
|
|
468
|
+
original_message = json.loads(message)
|
|
469
|
+
if isinstance(original_message, dict):
|
|
470
|
+
wrapped_message = original_message.copy()
|
|
471
|
+
existing_headers = wrapped_message.get("headers", {})
|
|
472
|
+
existing_headers.update(headers)
|
|
473
|
+
wrapped_message["headers"] = existing_headers
|
|
474
|
+
else:
|
|
475
|
+
wrapped_message = {"headers": headers, "payload": original_message}
|
|
476
|
+
except json.JSONDecodeError:
|
|
477
|
+
wrapped_message = {"headers": headers, "payload": message}
|
|
478
|
+
elif isinstance(message, dict):
|
|
479
|
+
wrapped_message = message.copy()
|
|
480
|
+
existing_headers = wrapped_message.get("headers", {})
|
|
481
|
+
existing_headers.update(headers)
|
|
482
|
+
wrapped_message["headers"] = existing_headers
|
|
483
|
+
else:
|
|
484
|
+
wrapped_message = {"headers": headers, "payload": json.dumps(message)}
|
|
485
|
+
|
|
486
|
+
return wrapped_message
|
|
487
|
+
|
|
249
488
|
def _uninstrument(self, **kwargs):
|
|
250
489
|
try:
|
|
251
490
|
import slim_bindings
|
|
@@ -255,6 +494,21 @@ class SLIMInstrumentor(BaseInstrumentor):
|
|
|
255
494
|
)
|
|
256
495
|
|
|
257
496
|
# Restore the original methods
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
497
|
+
methods_to_restore = [
|
|
498
|
+
"publish",
|
|
499
|
+
"publish_to",
|
|
500
|
+
"request_reply",
|
|
501
|
+
"receive",
|
|
502
|
+
"connect",
|
|
503
|
+
"create_session",
|
|
504
|
+
"invite",
|
|
505
|
+
"set_route",
|
|
506
|
+
]
|
|
507
|
+
|
|
508
|
+
for method_name in methods_to_restore:
|
|
509
|
+
if hasattr(slim_bindings.Slim, method_name):
|
|
510
|
+
original_method = getattr(slim_bindings.Slim, method_name)
|
|
511
|
+
if hasattr(original_method, "__wrapped__"):
|
|
512
|
+
setattr(
|
|
513
|
+
slim_bindings.Slim, method_name, original_method.__wrapped__
|
|
514
|
+
)
|
|
@@ -16,7 +16,7 @@ ioa_observe/sdk/decorators/util.py,sha256=IebvH9gwZN1en3LblYJUh4bAV2STl6xmp8WpZz
|
|
|
16
16
|
ioa_observe/sdk/instrumentations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
ioa_observe/sdk/instrumentations/a2a.py,sha256=ZpqvPl4u-yheQzSdBfxnZhWFZ8ntbKni_uaW3IDyjqw,6309
|
|
18
18
|
ioa_observe/sdk/instrumentations/mcp.py,sha256=vRM3ofnn7AMmry2RrfyZnZVPEutLWiDMghx2TSnm0Wk,18569
|
|
19
|
-
ioa_observe/sdk/instrumentations/slim.py,sha256=
|
|
19
|
+
ioa_observe/sdk/instrumentations/slim.py,sha256=UM_XCuzetOzeDFQ-bFY0Q93OLffH7OsvbgNaS58E0RA,22632
|
|
20
20
|
ioa_observe/sdk/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
21
|
ioa_observe/sdk/logging/logging.py,sha256=HZxW9s8Due7jgiNkdI38cIjv5rC9D-Flta3RQMOnpow,2891
|
|
22
22
|
ioa_observe/sdk/metrics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -41,8 +41,8 @@ ioa_observe/sdk/utils/const.py,sha256=d67dUTAH9UpWvUV9GLBUqn1Sc2knJ55dy-e6YoLrvS
|
|
|
41
41
|
ioa_observe/sdk/utils/in_memory_span_exporter.py,sha256=H_4TRaThMO1H6vUQ0OpQvzJk_fZH0OOsRAM1iZQXsR8,2112
|
|
42
42
|
ioa_observe/sdk/utils/json_encoder.py,sha256=g4NQ0tTqgWssY6I1D7r4zo0G6PiUo61jhofTAw5-jno,639
|
|
43
43
|
ioa_observe/sdk/utils/package_check.py,sha256=1d1MjxhwoEZIx9dumirT2pRsEWgn-m-SI4npDeEalew,576
|
|
44
|
-
ioa_observe_sdk-1.0.
|
|
45
|
-
ioa_observe_sdk-1.0.
|
|
46
|
-
ioa_observe_sdk-1.0.
|
|
47
|
-
ioa_observe_sdk-1.0.
|
|
48
|
-
ioa_observe_sdk-1.0.
|
|
44
|
+
ioa_observe_sdk-1.0.18.dist-info/licenses/LICENSE.md,sha256=55VjUfgjWOS4vv3Cf55gfq-RxjPgRIO2vlgYPUuC5lA,11362
|
|
45
|
+
ioa_observe_sdk-1.0.18.dist-info/METADATA,sha256=o4RDNJkldVU6TtdOHM4ISncUc61APJ_2KITjsmlqKAc,7027
|
|
46
|
+
ioa_observe_sdk-1.0.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
47
|
+
ioa_observe_sdk-1.0.18.dist-info/top_level.txt,sha256=Yt-6Y1olZEDqCs2REeqI30WjYx0pLGQSVqzYmDd67N8,12
|
|
48
|
+
ioa_observe_sdk-1.0.18.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|