Skip to content

Commit 06b545a

Browse files
committed
spyne: rpc-server adaptation
Signed-off-by: Varsha GS <[email protected]>
1 parent e28499a commit 06b545a

File tree

3 files changed

+46
-213
lines changed

3 files changed

+46
-213
lines changed

src/instana/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ def boot_agent() -> None:
188188
sqlalchemy, # noqa: F401
189189
starlette, # noqa: F401
190190
urllib3, # noqa: F401
191-
spyne,
191+
spyne, # noqa: F401
192192
)
193193
from instana.instrumentation.aiohttp import (
194194
client as aiohttp_client, # noqa: F401

src/instana/instrumentation/spyne.py

+16-25
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,41 @@
33
try:
44
import spyne
55
import wrapt
6-
from typing import TYPE_CHECKING, Dict, Any, Callable, Tuple, Iterable
7-
from types import SimpleNamespace
6+
from typing import TYPE_CHECKING, Dict, Any, Callable, Tuple, Iterable, Type, Optional
87

9-
from opentelemetry.semconv.trace import SpanAttributes
8+
from types import SimpleNamespace
109

1110
from instana.log import logger
1211
from instana.singletons import agent, tracer
1312
from instana.propagators.format import Format
1413
from instana.util.secrets import strip_secrets_from_query
15-
from instana.util.traceutils import extract_custom_headers
1614

1715
if TYPE_CHECKING:
1816
from instana.span.span import InstanaSpan
1917
from spyne.application import Application
2018
from spyne.server.wsgi import WsgiApplication
2119

2220
def set_span_attributes(span: "InstanaSpan", headers: Dict[str, Any]) -> None:
23-
if "REQUEST_METHOD" in headers:
24-
span.set_attribute(SpanAttributes.HTTP_METHOD, headers["REQUEST_METHOD"])
2521
if "PATH_INFO" in headers:
26-
span.set_attribute(SpanAttributes.HTTP_URL, headers["PATH_INFO"])
22+
span.set_attribute("rpc.call", headers["PATH_INFO"])
2723
if "QUERY_STRING" in headers and len(headers["QUERY_STRING"]):
2824
scrubbed_params = strip_secrets_from_query(
2925
headers["QUERY_STRING"],
3026
agent.options.secrets_matcher,
3127
agent.options.secrets_list,
3228
)
33-
span.set_attribute("http.params", scrubbed_params)
34-
if "HTTP_HOST" in headers:
35-
span.set_attribute("http.host", headers["HTTP_HOST"])
29+
span.set_attribute("rpc.params", scrubbed_params)
30+
if "REMOTE_ADDR" in headers:
31+
span.set_attribute("rpc.host", headers["REMOTE_ADDR"])
32+
if "SERVER_PORT" in headers:
33+
span.set_attribute("rpc.port", headers["SERVER_PORT"])
3634

37-
def set_response_status_code(span: "InstanaSpan", response_string: str) -> None:
35+
def record_error(span: "InstanaSpan", response_string: str, error: Optional[Type[Exception]]) -> None:
3836
resp_code = int(response_string.split()[0])
3937

4038
if 500 <= resp_code:
41-
span.mark_as_errored()
39+
span.record_exception(error)
4240

43-
span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, int(resp_code))
4441

4542
@wrapt.patch_function_wrapper("spyne.server.wsgi", "WsgiApplication.handle_error")
4643
def handle_error_with_instana(
@@ -55,22 +52,19 @@ def handle_error_with_instana(
5552
if ctx.udc and ctx.udc.span:
5653
return wrapped(*args, **kwargs)
5754

58-
headers = ctx.in_document
55+
headers = ctx.transport.req_env
5956
span_context = tracer.extract(Format.HTTP_HEADERS, headers)
6057

61-
with tracer.start_as_current_span("spyne", span_context=span_context) as span:
62-
extract_custom_headers(span, headers, format=True)
63-
58+
with tracer.start_as_current_span("rpc-server", span_context=span_context) as span:
6459
set_span_attributes(span, headers)
6560

6661
response_headers = ctx.transport.resp_headers
6762

68-
extract_custom_headers(span, response_headers, format=False)
6963
tracer.inject(span.context, Format.HTTP_HEADERS, response_headers)
7064

7165
response = wrapped(*args, **kwargs)
7266

73-
set_response_status_code(span, ctx.transport.resp_code)
67+
record_error(span, ctx.transport.resp_code, ctx.in_error or ctx.out_error)
7468
return response
7569

7670
@wrapt.patch_function_wrapper(
@@ -87,7 +81,7 @@ def finalize_with_instana(
8781

8882
if ctx.udc and ctx.udc.span and response_string:
8983
span = ctx.udc.span
90-
set_response_status_code(span, response_string)
84+
record_error(span, response_string, ctx.in_error or ctx.out_error)
9185
if span.is_recording():
9286
span.end()
9387

@@ -102,22 +96,19 @@ def process_request_with_instana(
10296
kwargs: Dict[str, Any],
10397
) -> None:
10498
ctx = args[0]
105-
headers = ctx.in_document
99+
headers = ctx.transport.req_env
106100
span_context = tracer.extract(Format.HTTP_HEADERS, headers)
107101

108102
with tracer.start_as_current_span(
109-
"spyne",
103+
"rpc-server",
110104
span_context=span_context,
111105
end_on_exit=False,
112106
) as span:
113-
extract_custom_headers(span, headers, format=True)
114-
115107
set_span_attributes(span, headers)
116108

117109
response = wrapped(*args, **kwargs)
118110
response_headers = ctx.transport.resp_headers
119111

120-
extract_custom_headers(span, response_headers, format=False)
121112
tracer.inject(span.context, Format.HTTP_HEADERS, response_headers)
122113

123114
## Store the span in the user defined context object offered by Spyne

tests/frameworks/test_spyne.py

+29-187
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,11 @@ def test_get_request(self) -> None:
7878
assert spyne_span.ec is None
7979

8080
# spyne
81-
assert spyne_span.n == "spyne"
82-
assert (
83-
"127.0.0.1:" + str(testenv["spyne_port"]) == spyne_span.data["http"]["host"]
84-
)
85-
assert spyne_span.data["http"]["url"] == "/hello"
86-
assert spyne_span.data["http"]["method"] == "GET"
87-
assert spyne_span.data["http"]["status"] == 200
88-
assert spyne_span.data["http"]["error"] is None
81+
assert spyne_span.n == "rpc-server"
82+
assert spyne_span.data["rpc"]["host"] == "127.0.0.1"
83+
assert spyne_span.data["rpc"]["call"] == "/hello"
84+
assert spyne_span.data["rpc"]["port"] == str(testenv["spyne_port"])
85+
assert spyne_span.data["rpc"]["error"] is None
8986
assert spyne_span.stack is None
9087

9188
def test_secret_scrubbing(self) -> None:
@@ -137,161 +134,14 @@ def test_secret_scrubbing(self) -> None:
137134
assert spyne_span.ec is None
138135

139136
# spyne
140-
assert spyne_span.n == "spyne"
141-
assert (
142-
"127.0.0.1:" + str(testenv["spyne_port"]) == spyne_span.data["http"]["host"]
143-
)
144-
assert spyne_span.data["http"]["url"] == "/say_hello"
145-
assert spyne_span.data["http"]["params"] == "name=World&times=4&secret=<redacted>"
146-
assert spyne_span.data["http"]["method"] == "GET"
147-
assert spyne_span.data["http"]["status"] == 200
148-
assert spyne_span.data["http"]["error"] is None
149-
assert spyne_span.stack is None
150-
151-
def test_request_header_capture(self) -> None:
152-
# Hack together a manual custom headers list
153-
original_extra_http_headers = agent.options.extra_http_headers
154-
agent.options.extra_http_headers = ["X-Capture-This-Too", "X-Capture-That-Too"]
155-
156-
request_headers = {
157-
"X-Capture-This-Too": "this too",
158-
"X-Capture-That-Too": "that too",
159-
}
160-
161-
with tracer.start_as_current_span("test"):
162-
response = self.http.request("GET", testenv["spyne_server"] + "/hello", headers=request_headers)
163-
164-
spans = self.recorder.queued_spans()
165-
166-
assert len(spans) == 3
167-
168-
spyne_span = spans[0]
169-
urllib3_span = spans[1]
170-
test_span = spans[2]
171-
172-
assert response.status == 200
173-
174-
assert "X-INSTANA-T" in response.headers
175-
assert int(response.headers["X-INSTANA-T"], 16)
176-
assert response.headers["X-INSTANA-T"] == hex_id(spyne_span.t)
177-
178-
assert "X-INSTANA-S" in response.headers
179-
assert int(response.headers["X-INSTANA-S"], 16)
180-
assert response.headers["X-INSTANA-S"] == hex_id(spyne_span.s)
181-
182-
assert "X-INSTANA-L" in response.headers
183-
assert response.headers["X-INSTANA-L"] == "1"
184-
185-
assert "Server-Timing" in response.headers
186-
server_timing_value = f"intid;desc={hex_id(spyne_span.t)}"
187-
assert response.headers["Server-Timing"] == server_timing_value
188-
189-
# Same traceId
190-
assert test_span.t == urllib3_span.t
191-
assert urllib3_span.t == spyne_span.t
192-
193-
# Parent relationships
194-
assert urllib3_span.p == test_span.s
195-
assert spyne_span.p == urllib3_span.s
196-
197-
assert spyne_span.sy is None
198-
assert urllib3_span.sy is None
199-
assert test_span.sy is None
200-
201-
# Error logging
202-
assert test_span.ec is None
203-
assert urllib3_span.ec is None
204-
assert spyne_span.ec is None
205-
206-
# spyne
207-
assert spyne_span.n == "spyne"
208-
assert (
209-
"127.0.0.1:" + str(testenv["spyne_port"]) == spyne_span.data["http"]["host"]
210-
)
211-
assert spyne_span.data["http"]["url"] == "/hello"
212-
assert spyne_span.data["http"]["method"] == "GET"
213-
assert spyne_span.data["http"]["status"] == 200
214-
assert spyne_span.data["http"]["error"] is None
215-
assert spyne_span.stack is None
216-
217-
# custom headers
218-
assert "X-Capture-This-Too" in spyne_span.data["http"]["header"]
219-
assert spyne_span.data["http"]["header"]["X-Capture-This-Too"] == "this too"
220-
assert "X-Capture-That-Too" in spyne_span.data["http"]["header"]
221-
assert spyne_span.data["http"]["header"]["X-Capture-That-Too"] == "that too"
222-
223-
agent.options.extra_http_headers = original_extra_http_headers
224-
225-
def test_response_header_capture(self) -> None:
226-
# Hack together a manual custom headers list
227-
original_extra_http_headers = agent.options.extra_http_headers
228-
agent.options.extra_http_headers = ["X-Capture-This", "X-Capture-That"]
229-
230-
with tracer.start_as_current_span("test"):
231-
response = self.http.request("GET", testenv["spyne_server"] + "/response_headers")
232-
233-
spans = self.recorder.queued_spans()
234-
235-
assert len(spans) == 3
236-
237-
spyne_span = spans[0]
238-
urllib3_span = spans[1]
239-
test_span = spans[2]
240-
241-
assert response.status == 200
242-
243-
assert "X-INSTANA-T" in response.headers
244-
assert int(response.headers["X-INSTANA-T"], 16)
245-
assert response.headers["X-INSTANA-T"] == hex_id(spyne_span.t)
246-
247-
assert "X-INSTANA-S" in response.headers
248-
assert int(response.headers["X-INSTANA-S"], 16)
249-
assert response.headers["X-INSTANA-S"] == hex_id(spyne_span.s)
250-
251-
assert "X-INSTANA-L" in response.headers
252-
assert response.headers["X-INSTANA-L"] == "1"
253-
254-
assert "Server-Timing" in response.headers
255-
server_timing_value = f"intid;desc={hex_id(spyne_span.t)}"
256-
assert response.headers["Server-Timing"] == server_timing_value
257-
258-
# Same traceId
259-
assert test_span.t == urllib3_span.t
260-
assert urllib3_span.t == spyne_span.t
261-
262-
# Parent relationships
263-
assert urllib3_span.p == test_span.s
264-
assert spyne_span.p == urllib3_span.s
265-
266-
# Synthetic
267-
assert spyne_span.sy is None
268-
assert urllib3_span.sy is None
269-
assert test_span.sy is None
270-
271-
# Error logging
272-
assert test_span.ec is None
273-
assert urllib3_span.ec is None
274-
assert spyne_span.ec is None
275-
276-
# spyne
277-
assert spyne_span.n == "spyne"
278-
assert (
279-
"127.0.0.1:" + str(testenv["spyne_port"]) == spyne_span.data["http"]["host"]
280-
)
281-
assert spyne_span.data["http"]["url"] == "/response_headers"
282-
assert spyne_span.data["http"]["method"] == "GET"
283-
assert spyne_span.data["http"]["status"] == 200
284-
assert spyne_span.data["http"]["error"] is None
137+
assert spyne_span.n == "rpc-server"
138+
assert spyne_span.data["rpc"]["host"] == "127.0.0.1"
139+
assert spyne_span.data["rpc"]["call"] == "/say_hello"
140+
assert spyne_span.data["rpc"]["params"] == "name=World&times=4&secret=<redacted>"
141+
assert spyne_span.data["rpc"]["port"] == str(testenv["spyne_port"])
142+
assert spyne_span.data["rpc"]["error"] is None
285143
assert spyne_span.stack is None
286144

287-
# custom headers
288-
assert "X-Capture-This" in spyne_span.data["http"]["header"]
289-
assert spyne_span.data["http"]["header"]["X-Capture-This"] == "this"
290-
assert "X-Capture-That" in spyne_span.data["http"]["header"]
291-
assert spyne_span.data["http"]["header"]["X-Capture-That"] == "that"
292-
293-
agent.options.extra_http_headers = original_extra_http_headers
294-
295145
def test_custom_404(self) -> None:
296146
with tracer.start_as_current_span("test"):
297147
response = self.http.request("GET", testenv["spyne_server"] + "/custom_404?user_id=9876")
@@ -307,7 +157,7 @@ def test_custom_404(self) -> None:
307157
test_span = spans[3]
308158

309159
assert response
310-
assert response.status == 404
160+
assert response.status == 404
311161

312162
assert "X-INSTANA-T" in response.headers
313163
assert int(response.headers["X-INSTANA-T"], 16)
@@ -343,14 +193,12 @@ def test_custom_404(self) -> None:
343193
assert spyne_span.ec is None
344194

345195
# spyne
346-
assert spyne_span.n == "spyne"
347-
assert (
348-
"127.0.0.1:" + str(testenv["spyne_port"]) == spyne_span.data["http"]["host"]
349-
)
350-
assert spyne_span.data["http"]["url"] == "/custom_404"
351-
assert spyne_span.data["http"]["method"] == "GET"
352-
assert spyne_span.data["http"]["status"] == 404
353-
assert spyne_span.data["http"]["error"] is None
196+
assert spyne_span.n == "rpc-server"
197+
assert spyne_span.data["rpc"]["host"] == "127.0.0.1"
198+
assert spyne_span.data["rpc"]["call"] == "/custom_404"
199+
assert spyne_span.data["rpc"]["port"] == str(testenv["spyne_port"])
200+
assert spyne_span.data["rpc"]["params"] == "user_id=9876"
201+
assert spyne_span.data["rpc"]["error"] is None
354202
assert spyne_span.stack is None
355203

356204
# urllib3
@@ -379,7 +227,7 @@ def test_404(self) -> None:
379227
test_span = spans[2]
380228

381229
assert response
382-
assert response.status == 404
230+
assert response.status == 404
383231

384232
assert "X-INSTANA-T" in response.headers
385233
assert int(response.headers["X-INSTANA-T"], 16)
@@ -415,14 +263,11 @@ def test_404(self) -> None:
415263
assert spyne_span.ec is None
416264

417265
# spyne
418-
assert spyne_span.n == "spyne"
419-
assert (
420-
"127.0.0.1:" + str(testenv["spyne_port"]) == spyne_span.data["http"]["host"]
421-
)
422-
assert spyne_span.data["http"]["url"] == "/11111"
423-
assert spyne_span.data["http"]["method"] == "GET"
424-
assert spyne_span.data["http"]["status"] == 404
425-
assert spyne_span.data["http"]["error"] is None
266+
assert spyne_span.n == "rpc-server"
267+
assert spyne_span.data["rpc"]["host"] == "127.0.0.1"
268+
assert spyne_span.data["rpc"]["call"] == "/11111"
269+
assert spyne_span.data["rpc"]["port"] == str(testenv["spyne_port"])
270+
assert spyne_span.data["rpc"]["error"] is None
426271
assert spyne_span.stack is None
427272

428273
# urllib3
@@ -487,12 +332,9 @@ def test_500(self) -> None:
487332
assert spyne_span.ec == 1
488333

489334
# spyne
490-
assert spyne_span.n == "spyne"
491-
assert (
492-
"127.0.0.1:" + str(testenv["spyne_port"]) == spyne_span.data["http"]["host"]
493-
)
494-
assert spyne_span.data["http"]["url"] == "/exception"
495-
assert spyne_span.data["http"]["method"] == "GET"
496-
assert spyne_span.data["http"]["status"] == 500
497-
assert spyne_span.data["http"]["error"] is None
335+
assert spyne_span.n == "rpc-server"
336+
assert spyne_span.data["rpc"]["host"] == "127.0.0.1"
337+
assert spyne_span.data["rpc"]["call"] == "/exception"
338+
assert spyne_span.data["rpc"]["port"] == str(testenv["spyne_port"])
339+
assert spyne_span.data["rpc"]["error"]
498340
assert spyne_span.stack is None

0 commit comments

Comments
 (0)