def test_detach_out_of_order(self): t1 = context.attach(context.set_value("c", 1)) self.assertEqual(context.get_current(), {"c": 1}) t2 = context.attach(context.set_value("c", 2)) self.assertEqual(context.get_current(), {"c": 2}) context.detach(t1) self.assertEqual(context.get_current(), {}) context.detach(t2) self.assertEqual(context.get_current(), {"c": 1})
def test_context(self): self.assertIsNone(context.get_value("say")) empty = context.get_current() second = context.set_value("say", "foo") self.assertEqual(context.get_value("say", context=second), "foo") do_work() self.assertEqual(context.get_value("say"), "bar") third = context.get_current() self.assertIsNone(context.get_value("say", context=empty)) self.assertEqual(context.get_value("say", context=second), "foo") self.assertEqual(context.get_value("say", context=third), "bar")
def test_context(self): key1 = context.create_key("say") self.assertIsNone(context.get_value(key1)) empty = context.get_current() second = context.set_value(key1, "foo") self.assertEqual(context.get_value(key1, context=second), "foo") key2 = _do_work() self.assertEqual(context.get_value(key2), "bar") third = context.get_current() self.assertIsNone(context.get_value(key1, context=empty)) self.assertEqual(context.get_value(key1, context=second), "foo") self.assertEqual(context.get_value(key2, context=third), "bar")
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 extract( self, getter: Getter[TextMapPropagatorT], carrier: TextMapPropagatorT, context: typing.Optional[Context] = None, ) -> Context: if context is None: context = get_current() header = getter.get(carrier, self.TRACE_ID_KEY) if not header: return trace.set_span_in_context(trace.INVALID_SPAN, context) fields = _extract_first_element(header).split(":") context = self._extract_baggage(getter, carrier, context) if len(fields) != 4: return trace.set_span_in_context(trace.INVALID_SPAN, context) trace_id, span_id, _parent_id, flags = fields if (trace_id == trace.INVALID_TRACE_ID or span_id == trace.INVALID_SPAN_ID): return trace.set_span_in_context(trace.INVALID_SPAN, context) span = trace.DefaultSpan( trace.SpanContext( trace_id=int(trace_id, 16), span_id=int(span_id, 16), is_remote=True, trace_flags=trace.TraceFlags( int(flags, 16) & trace.TraceFlags.SAMPLED), )) return trace.set_span_in_context(span, context)
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 extract( self, get_from_carrier: Getter[HTTPTextFormatT], carrier: HTTPTextFormatT, context: typing.Optional[Context] = None, ) -> Context: return get_current()
def _start_internal_or_server_span( tracer, span_name, start_time, context_carrier, context_getter ): """Returns internal or server span along with the token which can be used by caller to reset context Args: tracer : tracer in use by given instrumentation library name (string): name of the span start_time : start time of the span context_carrier : object which contains values that are used to construct a Context. This object must be paired with an appropriate getter which understands how to extract a value from it. context_getter : an object which contains a get function that can retrieve zero or more values from the carrier and a keys function that can get all the keys from carrier. """ token = ctx = span_kind = None if trace.get_current_span() is trace.INVALID_SPAN: ctx = extract(context_carrier, getter=context_getter) token = context.attach(ctx) span_kind = trace.SpanKind.SERVER else: ctx = context.get_current() span_kind = trace.SpanKind.INTERNAL span = tracer.start_span( name=span_name, context=ctx, kind=span_kind, start_time=start_time, ) return span, token
def extract( self, carrier: CarrierT, context: typing.Optional[Context] = None, getter: Getter = default_getter, ) -> Context: return get_current()
def extract( self, getter: Getter[TextMapPropagatorT], carrier: TextMapPropagatorT, context: typing.Optional[Context] = None, ) -> Context: return get_current()
def _inject(self, values): """Test helper""" ctx = get_current() for k, v in values.items(): ctx = baggage.set_baggage(k, v, context=ctx) output = {} self.propagator.inject(output, context=ctx) return output.get("baggage")
def _inject(self, span=None): """Test helper""" ctx = get_current() if span is not None: ctx = trace.set_span_in_context(span, ctx) output = {} self.propagator.inject(dict.__setitem__, output, context=ctx) return output.get(_TRACE_CONTEXT_HEADER_NAME)
def _inject(self, values): """Test helper""" ctx = get_current() for k, v in values.items(): ctx = baggage.set_baggage(k, v, context=ctx) output = {} self.propagator.inject(dict.__setitem__, output, context=ctx) return output.get("otcorrelations")
async def __call__(self, scope, receive, send): """The ASGI application Args: scope: A ASGI environment. receive: An awaitable callable yielding dictionaries send: An awaitable callable taking a single dictionary as argument. """ if scope["type"] not in ("http", "websocket"): return await self.app(scope, receive, send) _, _, url = get_host_port_url_tuple(scope) if self.excluded_urls and self.excluded_urls.url_disabled(url): return await self.app(scope, receive, send) token = ctx = span_kind = None if trace.get_current_span() is INVALID_SPAN: ctx = extract(scope, getter=asgi_getter) token = context.attach(ctx) span_kind = SpanKind.SERVER else: ctx = context.get_current() span_kind = SpanKind.INTERNAL span_name, additional_attributes = self.default_span_details(scope) try: with self.tracer.start_as_current_span( span_name, context=ctx, kind=span_kind, ) as current_span: if current_span.is_recording(): attributes = collect_request_attributes(scope) attributes.update(additional_attributes) for key, value in attributes.items(): current_span.set_attribute(key, value) if callable(self.server_request_hook): self.server_request_hook(current_span, scope) otel_receive = self._get_otel_receive(span_name, scope, receive) otel_send = self._get_otel_send( current_span, span_name, scope, send, ) await self.app(scope, otel_receive, otel_send) finally: if token: context.detach(token)
def with_current_context(cls, func): # type: (Callable) -> Callable """Passes the current spans to the new context the function will be run in. :param func: The function that will be run in the new context :return: The target the pass in instead of the function """ # returns the current Context object context = get_current() def call_with_current_context(*args, **kwargs): try: token = attach(context) return func(*args, **kwargs) finally: detach(token) return call_with_current_context
def _before_request(): if excluded_urls and excluded_urls.url_disabled(flask.request.url): return flask_request_environ = flask.request.environ span_name = get_default_span_name() token = ctx = span_kind = None if trace.get_current_span() is trace.INVALID_SPAN: ctx = extract(flask_request_environ, getter=otel_wsgi.wsgi_getter) token = context.attach(ctx) span_kind = trace.SpanKind.SERVER else: ctx = context.get_current() span_kind = trace.SpanKind.INTERNAL span = tracer.start_span( span_name, ctx, kind=span_kind, start_time=flask_request_environ.get(_ENVIRON_STARTTIME_KEY), ) if request_hook: request_hook(span, flask_request_environ) 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[ SpanAttributes.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 extract( self, get_from_carrier: httptextformat.Getter[ httptextformat.HTTPTextFormatT], carrier: httptextformat.HTTPTextFormatT, context: typing.Optional[Context] = None, ) -> Context: """Extract CorrelationContext from the carrier. See `opentelemetry.trace.propagation.httptextformat.HTTPTextFormat.extract` """ if context is None: context = get_current() header = _extract_first_element( get_from_carrier(carrier, self._CORRELATION_CONTEXT_HEADER_NAME)) if not header or len(header) > self.MAX_HEADER_LENGTH: return context correlations = header.split(",") total_correlations = self.MAX_PAIRS for correlation in correlations: if total_correlations <= 0: return context total_correlations -= 1 if len(correlation) > self.MAX_PAIR_LENGTH: continue try: name, value = correlation.split("=", 1) except Exception: # pylint: disable=broad-except continue context = correlationcontext.set_correlation( urllib.parse.unquote(name).strip(), urllib.parse.unquote(value).strip(), context=context, ) return context
def extract( self, getter: textmap.Getter[textmap.TextMapPropagatorT], carrier: textmap.TextMapPropagatorT, context: typing.Optional[Context] = None, ) -> Context: """Extract Baggage from the carrier. See `opentelemetry.propagators.textmap.TextMapPropagator.extract` """ if context is None: context = get_current() header = _extract_first_element( getter.get(carrier, self._BAGGAGE_HEADER_NAME) ) if not header or len(header) > self.MAX_HEADER_LENGTH: return context baggage_entries = header.split(",") total_baggage_entries = self.MAX_PAIRS for entry in baggage_entries: if total_baggage_entries <= 0: return context total_baggage_entries -= 1 if len(entry) > self.MAX_PAIR_LENGTH: continue try: name, value = entry.split("=", 1) except Exception: # pylint: disable=broad-except continue context = baggage.set_baggage( urllib.parse.unquote(name).strip(), urllib.parse.unquote(value).strip(), context=context, ) return context
def decorated_callback( channel: Channel, method: Basic.Deliver, properties: BasicProperties, body: bytes, ) -> Any: if not properties: properties = BasicProperties(headers={}) ctx = propagate.extract(properties.headers, getter=_pika_getter) if not ctx: ctx = context.get_current() span = _get_span( tracer, channel, properties, span_kind=SpanKind.CONSUMER, task_name=task_name, ctx=ctx, operation=MessagingOperationValues.RECEIVE, ) with trace.use_span(span, end_on_exit=True): retval = callback(channel, method, properties, body) return retval
def on_start(self, span: "Span", parent_context: Optional[context_api.Context] = None) -> None: """ Span listener that will: 1. copy attributes from parent span to :param span: :param parent_context: :return: """ current_span = trace_api.get_current_span(context_api.get_current()) wrapped_span = telemetry.Span(span) if current_span and not isinstance(current_span, trace_api.DefaultSpan): # copy parent span's attributes into this span for key, value in current_span.attributes.items(): if Attributes.propagte(key): span.set_attribute(key, value) # set/overwrite any span-specific attributes/labels wrapped_span.set(Attributes.TRACE_ID, str(span.context.trace_id)) wrapped_span.set(Attributes.TRACE_SPAN_ID, str(span.context.span_id)) wrapped_span.set(Attributes.TRACE_IS_REMOTE, span.context.is_remote) wrapped_span.set(Attributes.TRACE_CATEGORY, wrapped_span.category) wrapped_span.set(Attributes.TRACE_NAME, wrapped_span.qname) for key, value in Environment.attributes.items(): wrapped_span.set_attribute(key, value) for key, value in Environment.labels.items(): wrapped_span.set_label(key, value) super().on_start(span, parent_context)
def test_inject_empty_context(self): """If the current context has no span, don't add headers""" new_carrier = {} self.get_propagator().inject(new_carrier, get_current()) assert len(new_carrier) == 0
def test_inject_empty_context(): """If the current context has no span, don't add headers""" new_carrier = {} FORMAT.inject(dict.__setitem__, new_carrier, get_current()) assert len(new_carrier) == 0
def setUp(self) -> None: self.previous_context = context.get_current()
def extract( self, carrier: textmap.CarrierT, context: Optional[Context] = None, getter: textmap.Getter = textmap.default_getter, ) -> Context: """Extract Baggage from the carrier. See `opentelemetry.propagators.textmap.TextMapPropagator.extract` """ if context is None: context = get_current() header = _extract_first_element( getter.get(carrier, self._BAGGAGE_HEADER_NAME) ) if not header: return context if len(header) > self._MAX_HEADER_LENGTH: _logger.warning( "Baggage header `%s` exceeded the maximum number of bytes per baggage-string", header, ) return context baggage_entries = split(_DELIMITER_PATTERN, header) total_baggage_entries = self._MAX_PAIRS if len(baggage_entries) > self._MAX_PAIRS: _logger.warning( "Baggage header `%s` exceeded the maximum number of list-members", header, ) for entry in baggage_entries: if len(entry) > self._MAX_PAIR_LENGTH: _logger.warning( "Baggage entry `%s` exceeded the maximum number of bytes per list-member", entry, ) continue if not entry: # empty string continue try: name, value = entry.split("=", 1) except Exception: # pylint: disable=broad-except _logger.warning( "Baggage list-member `%s` doesn't match the format", entry ) continue name = unquote_plus(name).strip().lower() value = unquote_plus(value).strip() if not _is_valid_pair(name, value): _logger.warning("Invalid baggage entry: `%s`", entry) continue context = set_baggage( name, value, context=context, ) total_baggage_entries -= 1 if total_baggage_entries == 0: break return context
def test_context_is_immutable(self): with self.assertRaises(ValueError): # ensure a context context.get_current()["test"] = "cant-change-immutable"