def _prepare(tracer, func, handler, args, kwargs):
    start_time = time_ns()
    request = handler.request
    if _excluded_urls.url_disabled(request.uri):
        return func(*args, **kwargs)
    _start_span(tracer, handler, start_time)
    return func(*args, **kwargs)
Esempio n. 2
0
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 tracer.use_span(span):
        inject(type(request.headers).__setitem__, request.headers)
        future = func(*args, **kwargs)
        future.add_done_callback(
            functools.partial(_finish_tracing_callback, span=span))
        return future
Esempio n. 3
0
    def _wrapped_app(wrapped_app_environ, start_response):
        # We want to measure the time for route matching, etc.
        # In theory, we could start the span here and use
        # update_name later but that API is "highly discouraged" so
        # we better avoid it.
        wrapped_app_environ[_ENVIRON_STARTTIME_KEY] = time_ns()

        def _start_response(status, response_headers, *args, **kwargs):
            if not _excluded_urls.url_disabled(flask.request.url):
                span = flask.request.environ.get(_ENVIRON_SPAN_KEY)

                if span:
                    otel_wsgi.add_response_attributes(
                        span, status, response_headers
                    )
                else:
                    _logger.warning(
                        "Flask environ's OpenTelemetry span "
                        "missing at _start_response(%s)",
                        status,
                    )

            return start_response(status, response_headers, *args, **kwargs)

        return wsgi_app(wrapped_app_environ, _start_response)
Esempio n. 4
0
    def worker(self):
        timeout = self.schedule_delay_millis / 1e3
        flush_request = None  # type: typing.Optional[_FlushRequest]
        while not self.done:
            with self.condition:
                if self.done:
                    # done flag may have changed, avoid waiting
                    break
                flush_request = self._get_and_unset_flush_request()
                if (len(self.queue) < self.max_export_batch_size
                        and flush_request is None):

                    self.condition.wait(timeout)
                    flush_request = self._get_and_unset_flush_request()
                    if not self.queue:
                        # spurious notification, let's wait again, reset timeout
                        timeout = self.schedule_delay_millis / 1e3
                        self._notify_flush_request_finished(flush_request)
                        flush_request = None
                        continue
                    if self.done:
                        # missing spans will be sent when calling flush
                        break

            # subtract the duration of this export call to the next timeout
            start = time_ns()
            self._export(flush_request)
            end = time_ns()
            duration = (end - start) / 1e9
            timeout = self.schedule_delay_millis / 1e3 - duration

            self._notify_flush_request_finished(flush_request)
            flush_request = None

        # there might have been a new flush request while export was running
        # and before the done flag switched to true
        with self.condition:
            shutdown_flush_request = self._get_and_unset_flush_request()

        # be sure that all spans are sent
        self._drain_queue()
        self._notify_flush_request_finished(flush_request)
        self._notify_flush_request_finished(shutdown_flush_request)
Esempio n. 5
0
    def end(self, end_time: Optional[int] = None) -> None:
        with self._lock:
            if self._start_time is None:
                raise RuntimeError("Calling end() on a not started span.")
            if self._end_time is not None:
                logger.warning("Calling end() on an ended span.")
                return

            self._end_time = end_time if end_time is not None else time_ns()

        self._span_processor.on_end(self._readable_span())
Esempio n. 6
0
    def start(
        self,
        start_time: Optional[int] = None,
        parent_context: Optional[context_api.Context] = None,
    ) -> None:
        with self._lock:
            if self._start_time is not None:
                logger.warning("Calling start() on a started span.")
                return
            self._start_time = (start_time
                                if start_time is not None else time_ns())

        self._span_processor.on_start(self, parent_context=parent_context)
Esempio n. 7
0
 def add_event(
     self,
     name: str,
     attributes: types.Attributes = None,
     timestamp: Optional[int] = None,
 ) -> None:
     _filter_attribute_values(attributes)
     attributes = _create_immutable_attributes(attributes)
     self._add_event(
         Event(
             name=name,
             attributes=attributes,
             timestamp=time_ns() if timestamp is None else timestamp,
         ))
    def worker(self):
        timeout = self.schedule_delay_millis / 1e3
        while not self.done:
            if not self._flushing:
                with self.condition:
                    self.condition.wait(timeout)
                    if not self.check_traces_queue:
                        # spurious notification, let's wait again, reset timeout
                        timeout = self.schedule_delay_millis / 1e3
                        continue
                    if self.done:
                        # missing spans will be sent when calling flush
                        break

            # substract the duration of this export call to the next timeout
            start = time_ns()
            self.export()
            end = time_ns()
            duration = (end - start) / 1e9
            timeout = self.schedule_delay_millis / 1e3 - duration

        # be sure that all spans are sent
        self._drain_queue()
    def trace_tween(request):
        if _excluded_urls.url_disabled(request.url):
            request.environ[_ENVIRON_ENABLED_KEY] = False
            # short-circuit when we don't want to trace anything
            return handler(request)

        request.environ[_ENVIRON_ENABLED_KEY] = True
        request.environ[_ENVIRON_STARTTIME_KEY] = time_ns()

        try:
            response = handler(request)
            response_or_exception = response
        except HTTPException as exc:
            # If the exception is a pyramid HTTPException,
            # that's still valuable information that isn't necessarily
            # a 500. For instance, HTTPFound is a 302.
            # As described in docs, Pyramid exceptions are all valid
            # response types
            response_or_exception = exc
            raise
        finally:
            span = request.environ.get(_ENVIRON_SPAN_KEY)
            enabled = request.environ.get(_ENVIRON_ENABLED_KEY)
            if not span and enabled:
                _logger.warning(
                    "Pyramid environ's OpenTelemetry span missing."
                    "If the OpenTelemetry tween was added manually, make sure"
                    "PyramidInstrumentor().instrument_config(config) is called"
                )
            elif enabled:
                otel_wsgi.add_response_attributes(
                    span,
                    response_or_exception.status,
                    response_or_exception.headers,
                )

                activation = request.environ.get(_ENVIRON_ACTIVATION_KEY)

                if isinstance(response_or_exception, HTTPException):
                    activation.__exit__(
                        type(response_or_exception),
                        response_or_exception,
                        getattr(response_or_exception, "__traceback__", None),
                    )
                else:
                    activation.__exit__(None, None, None)

                context.detach(request.environ.get(_ENVIRON_TOKEN))

        return response
Esempio n. 10
0
    def force_flush(self, timeout_millis: int = 30000) -> bool:
        """Sequentially calls force_flush on all underlying
        :class:`SpanProcessor`

        Args:
            timeout_millis: The maximum amount of time over all span processors
                to wait for spans to be exported. In case the first n span
                processors exceeded the timeout followup span processors will be
                skipped.

        Returns:
            True if all span processors flushed their spans within the
            given timeout, False otherwise.
        """
        deadline_ns = time_ns() + timeout_millis * 1000000
        for sp in self._span_processors:
            current_time_ns = time_ns()
            if current_time_ns >= deadline_ns:
                return False

            if not sp.force_flush((deadline_ns - current_time_ns) // 1000000):
                return False

        return True
Esempio n. 11
0
    def test_events(self):
        self.assertEqual(trace_api.get_current_span(), trace_api.INVALID_SPAN)

        with self.tracer.start_as_current_span("root") as root:
            # only event name
            root.add_event("event0")

            # event name and attributes
            root.add_event("event1", {
                "name": "pluto",
                "some_bools": [True, False]
            })

            # event name, attributes and timestamp
            now = time_ns()
            root.add_event("event2", {"name": ["birthday"]}, now)

            mutable_list = ["original_contents"]
            root.add_event("event3", {"name": mutable_list})

            self.assertEqual(len(root.events), 4)

            self.assertEqual(root.events[0].name, "event0")
            self.assertEqual(root.events[0].attributes, {})

            self.assertEqual(root.events[1].name, "event1")
            self.assertEqual(
                root.events[1].attributes,
                {
                    "name": "pluto",
                    "some_bools": (True, False)
                },
            )

            self.assertEqual(root.events[2].name, "event2")
            self.assertEqual(root.events[2].attributes,
                             {"name": ("birthday", )})
            self.assertEqual(root.events[2].timestamp, now)

            self.assertEqual(root.events[3].name, "event3")
            self.assertEqual(root.events[3].attributes,
                             {"name": ("original_contents", )})
            mutable_list = ["new_contents"]
            self.assertEqual(root.events[3].attributes,
                             {"name": ("original_contents", )})
    def __call__(self, env, start_response):
        if _excluded_urls.url_disabled(env.get("PATH_INFO", "/")):
            return super().__call__(env, start_response)

        start_time = time_ns()

        token = context.attach(extract(otel_wsgi.carrier_getter, env))
        span = self._tracer.start_span(
            otel_wsgi.get_default_span_name(env),
            kind=trace.SpanKind.SERVER,
            start_time=start_time,
        )
        if span.is_recording():
            attributes = otel_wsgi.collect_request_attributes(env)
            for key, value in attributes.items():
                span.set_attribute(key, value)

        activation = self._tracer.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):
            otel_wsgi.add_response_attributes(span, status, response_headers)
            response = start_response(
                status, response_headers, *args, **kwargs
            )
            activation.__exit__(None, None, 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),
            )
            context.detach(token)
            raise
def _finish_span(tracer, handler, error=None):
    status_code = handler.get_status()
    reason = getattr(handler, "_reason")
    finish_args = (None, None, None)
    ctx = getattr(handler, _HANDLER_CONTEXT_KEY, None)

    if error:
        if isinstance(error, tornado.web.HTTPError):
            status_code = error.status_code
            if not ctx and status_code == 404:
                ctx = _start_span(tracer, handler, time_ns())
        if status_code != 404:
            finish_args = (
                type(error),
                error,
                getattr(error, "__traceback__", None),
            )
            status_code = 500
            reason = None

    if not ctx:
        return

    if ctx.span.is_recording():
        if reason:
            ctx.span.set_attribute("http.status_text", reason)
        ctx.span.set_attribute("http.status_code", status_code)
        ctx.span.set_status(
            Status(
                status_code=http_status_to_status_code(status_code),
                description=reason,
            ))

    ctx.activation.__exit__(*finish_args)
    context.detach(ctx.token)
    delattr(handler, _HANDLER_CONTEXT_KEY)
Esempio n. 14
0
    def test_time_seconds_from_ns(self):
        time_nanoseconds = time_ns()
        result = util.time_seconds_from_ns(time_nanoseconds)

        self.assertEqual(result, time_nanoseconds / 1e9)
Esempio n. 15
0
 def __init__(self, name: str, timestamp: Optional[int] = None) -> None:
     self._name = name
     if timestamp is None:
         self._timestamp = time_ns()
     else:
         self._timestamp = timestamp