def gen_wrapper(*args: P.args, **kwargs: P.kwargs) -> T: # type: ignore[misc] LOGGER.debug("Spanned Generator Function") span = setup(args, kwargs) # CASE 1 ---------------------------------------------------------- if scond.behavior == SpanBehavior.ONLY_END_ON_EXCEPTION: try: with use_span(span, end_on_exit=False): for val in func( *args, **kwargs): # type: ignore[attr-defined] yield val except: # noqa: E722 # pylint: disable=bare-except span.end() raise # CASES 2 & 3 ----------------------------------------------------- elif scond.behavior in (SpanBehavior.END_ON_EXIT, SpanBehavior.DONT_END): end_on_exit = bool(scond.behavior == SpanBehavior.END_ON_EXIT) with use_span(span, end_on_exit=end_on_exit): for val in func(*args, **kwargs): # type: ignore[attr-defined] yield val # ELSE ------------------------------------------------------------ else: raise InvalidSpanBehavior(scond.behavior)
def test_use_span_end_on_exit(self): test_span = TestSpan(trace.INVALID_SPAN_CONTEXT) with trace.use_span(test_span): pass self.assertFalse(test_span.has_ended) with trace.use_span(test_span, end_on_exit=True): pass self.assertTrue(test_span.has_ended)
def _trace_before_publish(self, *args, **kwargs): task = utils.retrieve_task_from_sender(kwargs) task_id = utils.retrieve_task_id_from_message(kwargs) if task is None or task_id is None: return operation_name = f"{_TASK_APPLY_ASYNC}/{task.name}" span = self._tracer.start_span( operation_name, kind=trace.SpanKind.PRODUCER ) # apply some attributes here because most of the data is not available if span.is_recording(): span.set_attribute(_TASK_TAG_KEY, _TASK_APPLY_ASYNC) span.set_attribute(SpanAttributes.MESSAGING_MESSAGE_ID, task_id) span.set_attribute(_TASK_NAME_KEY, task.name) utils.set_attributes_from_context(span, kwargs) activation = trace.use_span(span, end_on_exit=True) activation.__enter__() # pylint: disable=E1101 utils.attach_span(task, task_id, (span, activation), is_publish=True) headers = kwargs.get("headers") if headers: inject(headers)
def fetch_async(tracer, func, _, args, kwargs): start_time = _time_ns() # Return immediately if no args were provided (error) # or original_request is set (meaning we are in a redirect step). if len(args) == 0 or hasattr(args[0], "original_request"): return func(*args, **kwargs) # Force the creation of a HTTPRequest object if needed, # so we can inject the context into the headers. args, kwargs = _normalize_request(args, kwargs) request = args[0] span = tracer.start_span( request.method, kind=trace.SpanKind.CLIENT, start_time=start_time, ) if span.is_recording(): attributes = { "http.url": request.url, "http.method": request.method, } for key, value in attributes.items(): span.set_attribute(key, value) with trace.use_span(span): inject(request.headers) future = func(*args, **kwargs) future.add_done_callback( functools.partial(_finish_tracing_callback, span=span)) return future
def decorated_function( exchange: str, routing_key: str, body: bytes, properties: BasicProperties = None, mandatory: bool = False, ) -> Any: if not properties: properties = BasicProperties(headers={}) ctx = context.get_current() span = _get_span( tracer, channel, properties, span_kind=SpanKind.PRODUCER, task_name="(temporary)", ctx=ctx, operation=None, ) if not span: return original_function( exchange, routing_key, body, properties, mandatory ) with trace.use_span(span, end_on_exit=True): if span.is_recording(): propagate.inject(properties.headers) retval = original_function( exchange, routing_key, body, properties, mandatory ) return retval
def before_enqueue(self, _broker, message, delay): retry_count = message.options.get("retries", 0) operation_name = utils.get_operation_name("before_enqueue", retry_count) span_attributes = {_REMOULADE_MESSAGE_RETRY_COUNT_KEY: retry_count} span = self._tracer.start_span( operation_name, kind=trace.SpanKind.PRODUCER, attributes=span_attributes, ) if span.is_recording(): span.set_attributes({ _REMOULADE_MESSAGE_TAG_KEY: _REMOULADE_MESSAGE_SEND, _REMOULADE_MESSAGE_NAME_KEY: message.actor_name, SpanAttributes.MESSAGING_MESSAGE_ID: message.message_id, }) activation = trace.use_span(span, end_on_exit=True) activation.__enter__() # pylint: disable=E1101 utils.attach_span( self._span_registry, message.message_id, (span, activation), is_publish=True, ) if "trace_ctx" not in message.options: message.options["trace_ctx"] = {} inject(message.options["trace_ctx"])
def decorated_callback( channel: Channel, method: Basic.Deliver, properties: BasicProperties, body: bytes, ) -> Any: if not properties: properties = BasicProperties(headers={}) if properties.headers is None: properties.headers = {} ctx = propagate.extract(properties.headers, getter=_pika_getter) if not ctx: ctx = context.get_current() token = context.attach(ctx) span = _get_span( tracer, channel, properties, destination=method.exchange if method.exchange else method.routing_key, span_kind=SpanKind.CONSUMER, task_name=task_name, operation=MessagingOperationValues.RECEIVE, ) try: with trace.use_span(span, end_on_exit=True): try: consume_hook(span, body, properties) except Exception as hook_exception: # pylint: disable=W0703 _LOG.exception(hook_exception) retval = callback(channel, method, properties, body) finally: context.detach(token) return retval
def _start_span(tracer, handler, start_time) -> _TraceContext: span, token = _start_internal_or_server_span( tracer=tracer, span_name=_get_operation_name(handler, handler.request), start_time=start_time, context_carrier=handler.request.headers, context_getter=textmap.default_getter, ) if span.is_recording(): attributes = _get_attributes_from_request(handler.request) for key, value in attributes.items(): span.set_attribute(key, value) span.set_attribute("tornado.handler", _get_full_handler_name(handler)) if span.is_recording() and span.kind == trace.SpanKind.SERVER: custom_attributes = _collect_custom_request_headers_attributes( handler.request.headers) if len(custom_attributes) > 0: span.set_attributes(custom_attributes) activation = trace.use_span(span, end_on_exit=True) activation.__enter__() # pylint: disable=E1101 ctx = _TraceContext(activation, span, token) setattr(handler, _HANDLER_CONTEXT_KEY, ctx) # finish handler is called after the response is sent back to # the client so it is too late to inject trace response headers # there. propagator = get_global_response_propagator() if propagator: propagator.inject(handler, setter=response_propagation_setter) return ctx
def decorated_function( exchange: str, routing_key: str, body: bytes, properties: BasicProperties = None, mandatory: bool = False, ) -> Any: if not properties: properties = BasicProperties(headers={}) if properties.headers is None: properties.headers = {} span = _get_span( tracer, channel, properties, destination=exchange if exchange else routing_key, span_kind=SpanKind.PRODUCER, task_name="(temporary)", operation=None, ) if not span: return original_function(exchange, routing_key, body, properties, mandatory) with trace.use_span(span, end_on_exit=True): if span.is_recording(): propagate.inject(properties.headers) try: publish_hook(span, body, properties) except Exception as hook_exception: # pylint: disable=W0703 _LOG.exception(hook_exception) retval = original_function(exchange, routing_key, body, properties, mandatory) return retval
def start_as_current_span( self, name: str, context: Optional[context_api.Context] = None, kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL, attributes: types.Attributes = None, links: Sequence[trace_api.Link] = (), start_time: Optional[int] = None, record_exception: bool = True, set_status_on_exception: bool = True, end_on_exit: bool = True, ) -> Iterator[trace_api.Span]: span = self.start_span( name=name, context=context, kind=kind, attributes=attributes, links=links, start_time=start_time, record_exception=record_exception, set_status_on_exception=set_status_on_exception, ) with trace_api.use_span( span, end_on_exit=end_on_exit, record_exception=record_exception, set_status_on_exception=set_status_on_exception, ) as span_context: yield span_context
def __call__(self, environ, start_response): """The WSGI application Args: environ: A WSGI environment. start_response: The WSGI start_response callable. """ token = context.attach(extract(environ, getter=wsgi_getter)) span_name = self.name_callback(environ) span = self.tracer.start_span( span_name, kind=trace.SpanKind.SERVER, attributes=collect_request_attributes(environ), ) try: with trace.use_span(span): start_response = self._create_start_response( span, start_response) iterable = self.wsgi(environ, start_response) return _end_span_after_iterating(iterable, span, self.tracer, token) except Exception as ex: if span.is_recording(): span.set_status(Status(StatusCode.ERROR, str(ex))) span.end() context.detach(token) raise
def _create_processing_span( self, queue_name: str, queue_url: str, receipt_handle: str, message: Dict[str, Any], ) -> None: message_attributes = message.get("MessageAttributes", {}) links = [] ctx = propagate.extract(message_attributes, getter=boto3sqs_getter) parent_span_ctx = trace.get_current_span(ctx).get_span_context() if parent_span_ctx.is_valid: links.append(Link(context=parent_span_ctx)) span = self._tracer.start_span(name=f"{queue_name} process", links=links, kind=SpanKind.CONSUMER) with trace.use_span(span): message_id = message.get("MessageId") Boto3SQSInstrumentor.received_messages_spans[receipt_handle] = span Boto3SQSInstrumentor._enrich_span( span, queue_name, queue_url, message_id=message_id, operation=MessagingOperationValues.PROCESS, )
def _before_request(): if _excluded_urls.url_disabled(flask.request.url): return flask_request_environ = flask.request.environ span_name = name_callback() token = context.attach( extract(flask_request_environ, getter=otel_wsgi.wsgi_getter)) tracer = trace.get_tracer(__name__, __version__) span = tracer.start_span( span_name, kind=trace.SpanKind.SERVER, start_time=flask_request_environ.get(_ENVIRON_STARTTIME_KEY), ) if span.is_recording(): attributes = otel_wsgi.collect_request_attributes( flask_request_environ) if flask.request.url_rule: # For 404 that result from no route found, etc, we # don't have a url_rule. attributes["http.route"] = flask.request.url_rule.rule for key, value in attributes.items(): span.set_attribute(key, value) activation = trace.use_span(span, end_on_exit=True) activation.__enter__() # pylint: disable=E1101 flask_request_environ[_ENVIRON_ACTIVATION_KEY] = activation flask_request_environ[_ENVIRON_SPAN_KEY] = span flask_request_environ[_ENVIRON_TOKEN] = token
def test_start_span_explicit(self): tracer = new_tracer() other_parent = trace._Span( "name", trace_api.SpanContext( trace_id=0x000000000000000000000000DEADBEEF, span_id=0x00000000DEADBEF0, is_remote=False, trace_flags=trace_api.TraceFlags(trace_api.TraceFlags.SAMPLED), ), ) other_parent_context = trace_api.set_span_in_context(other_parent) self.assertEqual(trace_api.get_current_span(), trace_api.INVALID_SPAN) root = tracer.start_span("root") self.assertIsNotNone(root.start_time) self.assertIsNone(root.end_time) # Test with the implicit root span with trace_api.use_span(root, True): self.assertIs(trace_api.get_current_span(), root) with tracer.start_span("stepchild", other_parent_context) as child: # The child's parent should be the one passed in, # not the current span. self.assertNotEqual(child.parent, root) self.assertIs(child.parent, other_parent.get_span_context()) self.assertIsNotNone(child.start_time) self.assertIsNone(child.end_time) # The child should inherit its context from the explicit # parent, not the current span. child_context = child.get_span_context() self.assertEqual( other_parent.get_span_context().trace_id, child_context.trace_id, ) self.assertNotEqual( other_parent.get_span_context().span_id, child_context.span_id, ) self.assertEqual( other_parent.get_span_context().trace_state, child_context.trace_state, ) self.assertEqual( other_parent.get_span_context().trace_flags, child_context.trace_flags, ) # Verify start_span() did not set the current span. self.assertIs(trace_api.get_current_span(), root) # Verify ending the child did not set the current span. self.assertIs(trace_api.get_current_span(), root) self.assertIsNotNone(child.end_time)
def _start_span(tracer, handler, start_time) -> _TraceContext: token = context.attach(extract(handler.request.headers)) span = tracer.start_span( _get_operation_name(handler, handler.request), kind=trace.SpanKind.SERVER, start_time=start_time, ) if span.is_recording(): attributes = _get_attributes_from_request(handler.request) for key, value in attributes.items(): span.set_attribute(key, value) activation = trace.use_span(span, end_on_exit=True) activation.__enter__() # pylint: disable=E1101 ctx = _TraceContext(activation, span, token) setattr(handler, _HANDLER_CONTEXT_KEY, ctx) # finish handler is called after the response is sent back to # the client so it is too late to inject trace response headers # there. propagator = get_global_response_propagator() if propagator: propagator.inject(handler, setter=response_propagation_setter) return ctx
async def async_wrapper(*args: P.args, **kwargs: P.kwargs) -> T: LOGGER.debug("Spanned Async Function") span = setup(args, kwargs) is_iterator_class_anext_method = span.name.endswith( ".__anext__") # type: ignore[attr-defined] reraise_stopasynciteration_outside_contextmanager = False # CASE 1 ---------------------------------------------------------- if scond.behavior == SpanBehavior.ONLY_END_ON_EXCEPTION: try: with use_span(span, end_on_exit=False): try: return await func( *args, **kwargs) # type: ignore[misc, no-any-return] except StopAsyncIteration: # intercept and temporarily suppress StopAsyncIteration if not is_iterator_class_anext_method: raise reraise_stopasynciteration_outside_contextmanager = True except: # noqa: E722 # pylint: disable=bare-except span.end() raise if reraise_stopasynciteration_outside_contextmanager: raise StopAsyncIteration raise RuntimeError("Malformed SpanBehavior Handling") # CASES 2 & 3 ----------------------------------------------------- elif scond.behavior in (SpanBehavior.END_ON_EXIT, SpanBehavior.DONT_END): end_on_exit = bool(scond.behavior == SpanBehavior.END_ON_EXIT) with use_span(span, end_on_exit=end_on_exit): try: return await func( *args, **kwargs) # type: ignore[misc, no-any-return] except StopAsyncIteration: # intercept and temporarily suppress StopAsyncIteration if not is_iterator_class_anext_method: raise reraise_stopasynciteration_outside_contextmanager = True if reraise_stopasynciteration_outside_contextmanager: raise StopAsyncIteration raise RuntimeError("Malformed SpanBehavior Handling") # ELSE ------------------------------------------------------------ else: raise InvalidSpanBehavior(scond.behavior)
def test_with_only_nonrecording_span(self): with trace.use_span(INVALID_SPAN), set_ip_on_next_http_connection( INVALID_SPAN ): resp, body = self.perform_request() assert resp.status == 200 assert body == b"Hello!" self.assert_span(num_spans=0)
def callback(response_future): with trace.use_span(span, end_on_exit=True): code = response_future.code() if code != grpc.StatusCode.OK: rpc_info.error = code return response = response_future.result() rpc_info.response = response
def _end_span_after_iterating(iterable, span, tracer, token): try: with trace.use_span(span): yield from iterable finally: close = getattr(iterable, "close", None) if close: close() span.end() context.detach(token)
async def decorated_publish( message: AbstractMessage, routing_key: str, **kwargs) -> Optional[aiormq.abc.ConfirmationFrameType]: span = self._get_publish_span(message, routing_key) if not span: return await publish(message, routing_key, **kwargs) with trace.use_span(span, end_on_exit=True): if span.is_recording(): propagate.inject(message.properties.headers) return_value = await publish(message, routing_key, **kwargs) return return_value
def test_use_span_exception(self): class TestUseSpanException(Exception): pass test_span = TestSpan(trace.INVALID_SPAN_CONTEXT) exception = TestUseSpanException("test exception") with self.assertRaises(TestUseSpanException): with trace.use_span(test_span): raise exception self.assertEqual(test_span.recorded_exception, exception)
def _before_traversal(event): request = event.request request_environ = request.environ span_name = otel_wsgi.get_default_span_name(request_environ) enabled = request_environ.get(_ENVIRON_ENABLED_KEY) if enabled is None: _logger.warning( "Opentelemetry pyramid tween 'opentelemetry.instrumentation.pyramid.trace_tween_factory'" "was not called. Make sure that the tween is included in 'pyramid.tweens' if" "the tween list was created manually") return if not enabled: # Tracing not enabled, return return start_time = request_environ.get(_ENVIRON_STARTTIME_KEY) tracer = trace.get_tracer(__name__, __version__) if request.matched_route: span_name = request.matched_route.pattern else: span_name = otel_wsgi.get_default_span_name(request_environ) span, token = _start_internal_or_server_span( tracer=tracer, span_name=span_name, start_time=start_time, context_carrier=request_environ, context_getter=otel_wsgi.wsgi_getter, ) if span.is_recording(): attributes = otel_wsgi.collect_request_attributes(request_environ) if request.matched_route: attributes[ SpanAttributes.HTTP_ROUTE] = request.matched_route.pattern for key, value in attributes.items(): span.set_attribute(key, value) if span.kind == trace.SpanKind.SERVER: custom_attributes = ( otel_wsgi.collect_custom_request_headers_attributes( request_environ)) if len(custom_attributes) > 0: span.set_attributes(custom_attributes) activation = trace.use_span(span, end_on_exit=True) activation.__enter__() # pylint: disable=E1101 request_environ[_ENVIRON_ACTIVATION_KEY] = activation request_environ[_ENVIRON_SPAN_KEY] = span if token: request_environ[_ENVIRON_TOKEN] = token
def __call__(self, environ, start_response): """The WSGI application Args: environ: A WSGI environment. start_response: The WSGI start_response callable. """ req_attrs = collect_request_attributes(environ) active_requests_count_attrs = _parse_active_request_count_attrs( req_attrs) duration_attrs = _parse_duration_attrs(req_attrs) span, token = _start_internal_or_server_span( tracer=self.tracer, span_name=get_default_span_name(environ), start_time=None, context_carrier=environ, context_getter=wsgi_getter, attributes=req_attrs, ) if span.is_recording() and span.kind == trace.SpanKind.SERVER: custom_attributes = collect_custom_request_headers_attributes( environ) if len(custom_attributes) > 0: span.set_attributes(custom_attributes) if self.request_hook: self.request_hook(span, environ) response_hook = self.response_hook if response_hook: response_hook = functools.partial(response_hook, span, environ) start = default_timer() self.active_requests_counter.add(1, active_requests_count_attrs) try: with trace.use_span(span): start_response = self._create_start_response( span, start_response, response_hook, duration_attrs) iterable = self.wsgi(environ, start_response) return _end_span_after_iterating(iterable, span, token) except Exception as ex: if span.is_recording(): span.set_status(Status(StatusCode.ERROR, str(ex))) span.end() if token is not None: context.detach(token) raise finally: duration = max(round((default_timer() - start) * 1000), 0) self.duration_histogram.record(duration, duration_attrs) self.active_requests_counter.add(-1, active_requests_count_attrs)
def test_with_nested_nonrecording_span(self): tracer = trace.get_tracer(__name__) with tracer.start_as_current_span( "requests HTTP GET" ) as span, set_ip_on_next_http_connection(span): with trace.use_span(INVALID_SPAN), set_ip_on_next_http_connection( INVALID_SPAN ): resp, body = self.perform_request() assert resp.status == 200 assert body == b"Hello!" span = self.assert_span(num_spans=1) self.assertEqual(span.attributes, {"net.peer.ip": "127.0.0.1"})
def __call__(self, env, start_response): # pylint: disable=E1101 if self._otel_excluded_urls.url_disabled(env.get("PATH_INFO", "/")): return super().__call__(env, start_response) start_time = _time_ns() span, token = _start_internal_or_server_span( tracer=self._otel_tracer, span_name=otel_wsgi.get_default_span_name(env), start_time=start_time, context_carrier=env, context_getter=otel_wsgi.wsgi_getter, ) if span.is_recording(): attributes = otel_wsgi.collect_request_attributes(env) for key, value in attributes.items(): span.set_attribute(key, value) if span.is_recording() and span.kind == trace.SpanKind.SERVER: custom_attributes = ( otel_wsgi.collect_custom_request_headers_attributes(env) ) if len(custom_attributes) > 0: span.set_attributes(custom_attributes) activation = trace.use_span(span, end_on_exit=True) activation.__enter__() env[_ENVIRON_SPAN_KEY] = span env[_ENVIRON_ACTIVATION_KEY] = activation def _start_response(status, response_headers, *args, **kwargs): response = start_response( status, response_headers, *args, **kwargs ) activation.__exit__(None, None, None) if token is not None: context.detach(token) return response try: return super().__call__(env, _start_response) except Exception as exc: activation.__exit__( type(exc), exc, getattr(exc, "__traceback__", None), ) if token is not None: context.detach(token) raise
async def decorated(message: AbstractIncomingMessage): if not is_instrumentation_enabled(): return await callback(message) headers = message.headers or {} ctx = propagate.extract(headers) token = context.attach(ctx) span = self._get_span(message) if not span: return await callback(message) try: with trace.use_span(span, end_on_exit=True): return_value = await callback(message) finally: context.detach(token) return return_value
def test_use_span_set_status(self): class TestUseSpanException(Exception): pass test_span = TestSpan(trace.INVALID_SPAN_CONTEXT) with self.assertRaises(TestUseSpanException): with trace.use_span(test_span): raise TestUseSpanException("test error") self.assertEqual(test_span.recorded_status.status_code, StatusCode.ERROR) self.assertEqual( test_span.recorded_status.description, "TestUseSpanException: test error", )
def activate(self, span: SpanShim, finish_on_close: bool) -> "ScopeShim": """Activates a :class:`SpanShim` and returns a :class:`ScopeShim` which represents the active span. Args: span: A :class:`SpanShim` to be activated. finish_on_close(:obj:`bool`): Determines whether the OpenTelemetry span should be ended when the returned :class:`ScopeShim` is closed. Returns: A :class:`ScopeShim` representing the activated span. """ span_cm = use_span(span.unwrap(), end_on_exit=finish_on_close) return ScopeShim.from_context_manager(self, span_cm=span_cm)
def _before_cur_exec(self, conn, cursor, statement, *args): attrs, found = _get_attributes_from_url(conn.engine.url) if not found: attrs = _get_attributes_from_cursor(self.vendor, cursor, attrs) db_name = attrs.get(SpanAttributes.DB_NAME, "") span = self.tracer.start_span( self._operation_name(db_name, statement), kind=trace.SpanKind.CLIENT, ) self.current_thread_span = self.cursor_mapping[cursor] = span with trace.use_span(span, end_on_exit=False): if span.is_recording(): span.set_attribute(SpanAttributes.DB_STATEMENT, statement) span.set_attribute(SpanAttributes.DB_SYSTEM, self.vendor) for key, value in attrs.items(): span.set_attribute(key, value)
def process_request(self, request): # request.META is a dictionary containing all available HTTP headers # Read more about request.META here: # https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.HttpRequest.META if self._excluded_urls.url_disabled(request.build_absolute_uri("?")): return # pylint:disable=W0212 request._otel_start_time = time() request_meta = request.META token = attach(extract(request_meta, getter=wsgi_getter)) tracer = get_tracer(__name__, __version__) span = tracer.start_span( self._get_span_name(request), kind=SpanKind.SERVER, start_time=request_meta.get( "opentelemetry-instrumentor-django.starttime_key" ), ) attributes = collect_request_attributes(request_meta) if span.is_recording(): attributes = extract_attributes_from_object( request, self._traced_request_attrs, attributes ) for key, value in attributes.items(): span.set_attribute(key, value) activation = use_span(span, end_on_exit=True) activation.__enter__() # pylint: disable=E1101 request.META[self._environ_activation_key] = activation request.META[self._environ_span_key] = span request.META[self._environ_token] = token if _DjangoMiddleware._otel_request_hook: _DjangoMiddleware._otel_request_hook( # pylint: disable=not-callable span, request )