mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2025-08-01 09:13:23 +08:00
147 lines
4.9 KiB
Python
147 lines
4.9 KiB
Python
import grpc
|
|
from ddtrace.vendor import wrapt
|
|
|
|
from ddtrace import config
|
|
from ddtrace.ext import errors
|
|
from ddtrace.compat import to_unicode
|
|
|
|
from ...constants import ANALYTICS_SAMPLE_RATE_KEY
|
|
from ...ext import SpanTypes
|
|
from ...propagation.http import HTTPPropagator
|
|
from . import constants
|
|
from .utils import parse_method_path
|
|
|
|
|
|
def create_server_interceptor(pin):
|
|
def interceptor_function(continuation, handler_call_details):
|
|
if not pin.enabled:
|
|
return continuation(handler_call_details)
|
|
|
|
rpc_method_handler = continuation(handler_call_details)
|
|
return _TracedRpcMethodHandler(pin, handler_call_details, rpc_method_handler)
|
|
|
|
return _ServerInterceptor(interceptor_function)
|
|
|
|
|
|
def _handle_server_exception(server_context, span):
|
|
if server_context is not None and \
|
|
hasattr(server_context, '_state') and \
|
|
server_context._state is not None:
|
|
code = to_unicode(server_context._state.code)
|
|
details = to_unicode(server_context._state.details)
|
|
span.error = 1
|
|
span.set_tag(errors.ERROR_MSG, details)
|
|
span.set_tag(errors.ERROR_TYPE, code)
|
|
|
|
|
|
def _wrap_response_iterator(response_iterator, server_context, span):
|
|
try:
|
|
for response in response_iterator:
|
|
yield response
|
|
except Exception:
|
|
span.set_traceback()
|
|
_handle_server_exception(server_context, span)
|
|
raise
|
|
finally:
|
|
span.finish()
|
|
|
|
|
|
class _TracedRpcMethodHandler(wrapt.ObjectProxy):
|
|
def __init__(self, pin, handler_call_details, wrapped):
|
|
super(_TracedRpcMethodHandler, self).__init__(wrapped)
|
|
self._pin = pin
|
|
self._handler_call_details = handler_call_details
|
|
|
|
def _fn(self, method_kind, behavior, args, kwargs):
|
|
if config.grpc_server.distributed_tracing_enabled:
|
|
headers = dict(self._handler_call_details.invocation_metadata)
|
|
propagator = HTTPPropagator()
|
|
context = propagator.extract(headers)
|
|
|
|
if context.trace_id:
|
|
self._pin.tracer.context_provider.activate(context)
|
|
|
|
tracer = self._pin.tracer
|
|
|
|
span = tracer.trace(
|
|
'grpc',
|
|
span_type=SpanTypes.GRPC,
|
|
service=self._pin.service,
|
|
resource=self._handler_call_details.method,
|
|
)
|
|
|
|
method_path = self._handler_call_details.method
|
|
method_package, method_service, method_name = parse_method_path(method_path)
|
|
span.set_tag(constants.GRPC_METHOD_PATH_KEY, method_path)
|
|
span.set_tag(constants.GRPC_METHOD_PACKAGE_KEY, method_package)
|
|
span.set_tag(constants.GRPC_METHOD_SERVICE_KEY, method_service)
|
|
span.set_tag(constants.GRPC_METHOD_NAME_KEY, method_name)
|
|
span.set_tag(constants.GRPC_METHOD_KIND_KEY, method_kind)
|
|
span.set_tag(constants.GRPC_SPAN_KIND_KEY, constants.GRPC_SPAN_KIND_VALUE_SERVER)
|
|
|
|
sample_rate = config.grpc_server.get_analytics_sample_rate()
|
|
if sample_rate is not None:
|
|
span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate)
|
|
|
|
# access server context by taking second argument as server context
|
|
# if not found, skip using context to tag span with server state information
|
|
server_context = args[1] if isinstance(args[1], grpc.ServicerContext) else None
|
|
|
|
if self._pin.tags:
|
|
span.set_tags(self._pin.tags)
|
|
|
|
try:
|
|
response_or_iterator = behavior(*args, **kwargs)
|
|
|
|
if self.__wrapped__.response_streaming:
|
|
response_or_iterator = _wrap_response_iterator(response_or_iterator, server_context, span)
|
|
except Exception:
|
|
span.set_traceback()
|
|
_handle_server_exception(server_context, span)
|
|
raise
|
|
finally:
|
|
if not self.__wrapped__.response_streaming:
|
|
span.finish()
|
|
|
|
return response_or_iterator
|
|
|
|
def unary_unary(self, *args, **kwargs):
|
|
return self._fn(
|
|
constants.GRPC_METHOD_KIND_UNARY,
|
|
self.__wrapped__.unary_unary,
|
|
args,
|
|
kwargs
|
|
)
|
|
|
|
def unary_stream(self, *args, **kwargs):
|
|
return self._fn(
|
|
constants.GRPC_METHOD_KIND_SERVER_STREAMING,
|
|
self.__wrapped__.unary_stream,
|
|
args,
|
|
kwargs
|
|
)
|
|
|
|
def stream_unary(self, *args, **kwargs):
|
|
return self._fn(
|
|
constants.GRPC_METHOD_KIND_CLIENT_STREAMING,
|
|
self.__wrapped__.stream_unary,
|
|
args,
|
|
kwargs
|
|
)
|
|
|
|
def stream_stream(self, *args, **kwargs):
|
|
return self._fn(
|
|
constants.GRPC_METHOD_KIND_BIDI_STREAMING,
|
|
self.__wrapped__.stream_stream,
|
|
args,
|
|
kwargs
|
|
)
|
|
|
|
|
|
class _ServerInterceptor(grpc.ServerInterceptor):
|
|
def __init__(self, interceptor_function):
|
|
self._fn = interceptor_function
|
|
|
|
def intercept_service(self, continuation, handler_call_details):
|
|
return self._fn(continuation, handler_call_details)
|