Closed
Description
How do you use Sentry?
Sentry Saas (sentry.io)
Version
2.27.0
Steps to Reproduce
- Install
sentry-sdk[grpcio]
in version 2.27.0 - Install
opentelemetry-instrumentation-grpc==0.53b0
- Setup
sentry
withintegrations=[GRPCIntegration()]
- Run gRPC server with instrumentation like
opentelemetry-instrument python -m app.grpc_api.grpc_server
Expected Result
Server runs without an issue and Sentry GRPCIntegration works
Actual Result
An exception is raised in opentelemetry-instrument
File "/Users/Tosuto/PycharmProjects/service/app/grpc_api/grpc_server.py", line 91, in start_server
self.grpc_server = grpc.aio.server()
^^^^^^^^^^^^^^^^^
File "/Users/Tosuto/.pyenv/versions/3.12.7/envs/3.12.7/lib/python3.12/site-packages/sentry_sdk/integrations/grpc/__init__.py", line 131, in patched_aio_server
return func(*args, interceptors=interceptors, **kwargs) # type: ignore
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/Tosuto/.pyenv/versions/3.12.7/envs/3.12.7/lib/python3.12/site-packages/opentelemetry/instrumentation/grpc/__init__.py", line 391, in server
kwargs["interceptors"].insert(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'tuple' object has no attribute 'insert'
It appears that Sentry
in _wrap_async_server
passes tuple
when opentelemetry-instrumentation
expects list
, see:
# Sentry
def _wrap_async_server(func: Callable[P, AsyncServer]) -> Callable[P, AsyncServer]:
"""Wrapper for asynchronous server."""
@wraps(func)
def patched_aio_server( # type: ignore
*args: P.args,
interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None,
**kwargs: P.kwargs,
) -> Server:
server_interceptor = AsyncServerInterceptor()
interceptors = (server_interceptor, *(interceptors or [])) # uses Tuple here
return func(*args, interceptors=interceptors, **kwargs) # type: ignore
return patched_aio_server # type: ignore
# OpenTelemetry
if "interceptors" in kwargs:
# add our interceptor as the first
kwargs["interceptors"].insert( # Expects list here but Sentry already patched it with tuple
0,
aio_server_interceptor(
tracer_provider=tracer_provider, filter_=self._filter
),
)
else:
kwargs["interceptors"] = [
aio_server_interceptor(
tracer_provider=tracer_provider, filter_=self._filter
)
]
return self._original_func(*args, **kwargs)
And that's kinda strange because in non async wraps, Sentry uses list, see:
def _wrap_sync_server(func: Callable[P, Server]) -> Callable[P, Server]:
"""Wrapper for synchronous server."""
@wraps(func)
def patched_server( # type: ignore
*args: P.args,
interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None,
**kwargs: P.kwargs,
) -> Server:
interceptors = [
interceptor
for interceptor in interceptors or []
if not isinstance(interceptor, ServerInterceptor)
]
server_interceptor = ServerInterceptor()
interceptors = [server_interceptor, *(interceptors or [])] # Sentry uses list here
return func(*args, interceptors=interceptors, **kwargs) # type: ignore
return patched_server # type: ignore
Metadata
Metadata
Assignees
Labels
Type
Projects
Status
No status