Ejemplo n.º 1
0
    def call(self, method, url, body, headers, *args, **kwargs):
        _tracer = execution_context.get_opencensus_tracer()
        blacklist_hostnames = execution_context.get_opencensus_attr(
            'blacklist_hostnames')
        dest_url = '{}:{}'.format(self.host, self.port)
        if utils.disable_tracing_hostname(dest_url, blacklist_hostnames):
            return request_func(self, method, url, body,
                                headers, *args, **kwargs)
        _span = _tracer.start_span()
        _span.span_kind = span_module.SpanKind.CLIENT
        _span.name = '[httplib]{}'.format(request_func.__name__)

        # Add the request url to attributes
        _tracer.add_attribute_to_current_span(HTTP_URL, url)

        # Add the request method to attributes
        _tracer.add_attribute_to_current_span(HTTP_METHOD, method)

        # Store the current span id to thread local.
        execution_context.set_opencensus_attr(
            'httplib/current_span_id', _span.span_id)
        try:
            headers = headers.copy()
            headers.update(_tracer.propagator.to_headers(
                _span.context_tracer.span_context))
        except Exception:  # pragma: NO COVER
            pass
        return request_func(self, method, url, body, headers, *args, **kwargs)
    def _before_request(self):
        """A function to be run before each request.

        See: http://flask.pocoo.org/docs/0.12/api/#flask.Flask.before_request
        """
        # Do not trace if the url is blacklisted
        if utils.disable_tracing_url(flask.request.url, self.blacklist_paths):
            return

        try:
            span_context = self.propagator.from_headers(flask.request.headers)

            tracer = tracer_module.Tracer(
                span_context=span_context,
                sampler=self.sampler,
                exporter=self.exporter,
                propagator=self.propagator)

            span = tracer.start_span()
            span.span_kind = span_module.SpanKind.SERVER
            # Set the span name as the name of the current module name
            span.name = '[{}]{}'.format(
                flask.request.method,
                flask.request.url)
            tracer.add_attribute_to_current_span(
                HTTP_METHOD, flask.request.method)
            tracer.add_attribute_to_current_span(
                HTTP_URL, str(flask.request.url))
            execution_context.set_opencensus_attr(
                'blacklist_hostnames',
                self.blacklist_hostnames)
        except Exception:  # pragma: NO COVER
            log.error('Failed to trace request', exc_info=True)
    def test_get_and_set_full_context(self):
        mock_tracer_get = mock.Mock()
        mock_span_get = mock.Mock()
        execution_context.set_opencensus_tracer(mock_tracer_get)
        execution_context.set_current_span(mock_span_get)

        execution_context.set_opencensus_attr("test", "test_value")

        tracer, span, attrs = execution_context.get_opencensus_full_context()

        self.assertEqual(mock_tracer_get, tracer)
        self.assertEqual(mock_span_get, span)
        self.assertEqual({"test": "test_value"}, attrs)

        mock_tracer_set = mock.Mock()
        mock_span_set = mock.Mock()

        execution_context.set_opencensus_full_context(mock_tracer_set,
                                                      mock_span_set, None)
        self.assertEqual(mock_tracer_set,
                         execution_context.get_opencensus_tracer())
        self.assertEqual(mock_span_set, execution_context.get_current_span())
        self.assertEqual({}, execution_context.get_opencensus_attrs())

        execution_context.set_opencensus_full_context(
            mock_tracer_set, mock_span_set, {"test": "test_value"})
        self.assertEqual("test_value",
                         execution_context.get_opencensus_attr("test"))
Ejemplo n.º 4
0
    def process_request(self, request):
        """Called on each request, before Django decides which view to execute.

        :type request: :class:`~django.http.request.HttpRequest`
        :param request: Django http request.
        """
        # Do not trace if the url is blacklisted
        if utils.disable_tracing_url(request.path, self._blacklist_paths):
            return

        # Add the request to thread local
        execution_context.set_opencensus_attr(REQUEST_THREAD_LOCAL_KEY,
                                              request)

        try:
            # Start tracing this request
            span_context = self.propagator.from_headers(
                _DjangoMetaWrapper(_get_django_request().META))

            # Reload the tracer with the new span context
            tracer = tracer_module.Tracer(span_context=span_context,
                                          sampler=self.sampler,
                                          exporter=self.exporter,
                                          propagator=self.propagator)

            # Span name is being set at process_view
            span = tracer.start_span()
            span.span_kind = span_module.SpanKind.SERVER
            tracer.add_attribute_to_current_span(
                attribute_key=HTTP_METHOD, attribute_value=request.method)
            tracer.add_attribute_to_current_span(attribute_key=HTTP_URL,
                                                 attribute_value=request.path)
        except Exception:  # pragma: NO COVER
            log.error('Failed to trace request', exc_info=True)
Ejemplo n.º 5
0
    def emit(self, span_datas):
        """
        :type span_datas: list of :class:
            `~opencensus.trace.span_data.SpanData`
        :param list of opencensus.trace.span_data.SpanData span_datas:
            SpanData tuples to emit
        """
        envelopes = [self.span_data_to_envelope(sd) for sd in span_datas]

        # TODO: prevent requests being tracked
        blacklist_hostnames = execution_context.get_opencensus_attr(
            'blacklist_hostnames', )
        execution_context.set_opencensus_attr(
            'blacklist_hostnames',
            ['dc.services.visualstudio.com'],
        )
        response = requests.post(
            url=self.options.endpoint,
            data=json.dumps(envelopes),
            headers={
                'Accept': 'application/json',
                'Content-Type': 'application/json; charset=utf-8',
            },
            timeout=self.options.timeout,
        )
        execution_context.set_opencensus_attr(
            'blacklist_hostnames',
            blacklist_hostnames,
        )
        response = response  # noqa
Ejemplo n.º 6
0
    def process_request(self, request):
        """Called on each request, before Django decides which view to execute.

        :type request: :class:`~django.http.request.HttpRequest`
        :param request: Django http request.
        """
        # Add the request to thread local
        execution_context.set_opencensus_attr(REQUEST_THREAD_LOCAL_KEY,
                                              request)

        try:
            # Start tracing this request
            header = get_django_header()
            span_context = self.propagator.from_header(header)

            # Reload the tracer with the new span context
            tracer = request_tracer.RequestTracer(span_context=span_context,
                                                  sampler=self.sampler,
                                                  reporter=self.reporter,
                                                  propagator=self.propagator)

            tracer.start_trace()

            # Span name is being set at process_view
            tracer.start_span()
            tracer.add_label_to_spans(label_key=HTTP_METHOD,
                                      label_value=request.method)
            tracer.add_label_to_spans(label_key=HTTP_URL,
                                      label_value=request.path)
        except Exception:  # pragma: NO COVER
            log.error('Failed to trace request', exc_info=True)
    def test_has_attrs(self):
        key = 'key'
        value = 'value'

        execution_context.set_opencensus_attr(key, value)

        result = execution_context.get_opencensus_attr(key)

        self.assertEqual(result, value)
Ejemplo n.º 8
0
def set_parent(value):
    """Context manager to set parent execution context. """

    old = get_parent()
    execution_context.set_opencensus_attr(_threadlocal_parent, value)
    try:
        yield
    finally:
        execution_context.set_opencensus_attr(_threadlocal_parent, old)
Ejemplo n.º 9
0
def _log_exception(func, handler, args, kwargs):
    value = args[1] if len(args) == 3 else None
    if value is None:
        return func(*args, **kwargs)

    tracer = execution_context.get_opencensus_tracer()
    if not isinstance(value, HTTPError) or 500 <= value.status_code <= 599:
        span = execution_context.get_current_span()
        span.add_attribute(attribute_key=HTTP_STATUS_CODE,
                           attribute_value=str(handler.get_status()))
        tracer.finish()

        execution_context.set_opencensus_attr(TORNADO_EXCEPTION, True)

    return func(*args, **kwargs)
Ejemplo n.º 10
0
    def call(self, method, url, *args, **kwargs):
        _tracer = execution_context.get_opencensus_tracer()
        _span = _tracer.start_span()
        _span.name = '[httplib]{}'.format(request_func.__name__)

        # Add the request url to attributes
        _tracer.add_attribute_to_current_span(HTTP_URL, url)

        # Add the request method to attributes
        _tracer.add_attribute_to_current_span(HTTP_METHOD, method)

        # Store the current span id to thread local.
        execution_context.set_opencensus_attr('httplib/current_span_id',
                                              _span.span_id)

        return request_func(self, method, url, *args, **kwargs)
Ejemplo n.º 11
0
    async def dispatch(self, request: Request, call_next):

        # Do not trace if the url is in the exclude list
        if utils.disable_tracing_url(str(request.url), self.excludelist_paths):
            return await call_next(request)

        try:
            span_context = self.propagator.from_headers(request.headers)

            tracer = tracer_module.Tracer(
                span_context=span_context,
                sampler=self.sampler,
                exporter=self.exporter,
                propagator=self.propagator,
            )
        except Exception:  # pragma: NO COVER
            module_logger.error("Failed to trace request", exc_info=True)
            return await call_next(request)

        try:
            span = tracer.start_span()
            span.span_kind = span_module.SpanKind.SERVER
            span.name = "[{}]{}".format(request.method, request.url)

            tracer.add_attribute_to_current_span(HTTP_HOST,
                                                 request.url.hostname)
            tracer.add_attribute_to_current_span(HTTP_METHOD, request.method)
            tracer.add_attribute_to_current_span(HTTP_PATH, request.url.path)
            tracer.add_attribute_to_current_span(HTTP_ROUTE, request.url.path)
            tracer.add_attribute_to_current_span(HTTP_URL, str(request.url))

            execution_context.set_opencensus_attr("excludelist_hostnames",
                                                  self.excludelist_hostnames)
        except Exception:  # pragma: NO COVER
            module_logger.error("Failed to trace request", exc_info=True)

        response = await call_next(request)
        try:
            tracer.add_attribute_to_current_span(HTTP_STATUS_CODE,
                                                 response.status_code)
        except Exception:  # pragma: NO COVER
            module_logger.error("Failed to trace response", exc_info=True)
        finally:
            tracer.end_span()
            return response
Ejemplo n.º 12
0
    def call(self, method, url, body, headers, *args, **kwargs):
        _tracer = execution_context.get_opencensus_tracer()
        _span = _tracer.start_span()
        _span.name = '[httplib]{}'.format(request_func.__name__)

        # Add the request url to attributes
        _tracer.add_attribute_to_current_span(HTTP_URL, url)

        # Add the request method to attributes
        _tracer.add_attribute_to_current_span(HTTP_METHOD, method)

        # Store the current span id to thread local.
        execution_context.set_opencensus_attr(
            'httplib/current_span_id', _span.span_id)
        try:
            headers = headers.copy()
            headers.update(_tracer.propagator.to_headers(
                _span.context_tracer.span_context))
        except Exception:  # pragma: NO COVER
            pass
        return request_func(self, method, url, body, headers, *args, **kwargs)
    def process_request(self, request):
        """Called on each request, before Django decides which view to execute.

        :type request: :class:`~django.http.request.HttpRequest`
        :param request: Django http request.
        """
        # Do not trace if the url is excludelisted
        if utils.disable_tracing_url(request.path, self.excludelist_paths):
            return

        # Add the request to thread local
        execution_context.set_opencensus_attr(REQUEST_THREAD_LOCAL_KEY,
                                              request)

        execution_context.set_opencensus_attr('excludelist_hostnames',
                                              self.excludelist_hostnames)

        try:
            # Start tracing this request
            span_context = self.propagator.from_headers(
                _DjangoMetaWrapper(_get_django_request().META))

            # Reload the tracer with the new span context
            tracer = tracer_module.Tracer(span_context=span_context,
                                          sampler=self.sampler,
                                          exporter=self.exporter,
                                          propagator=self.propagator)

            # Span name is being set at process_view
            span = tracer.start_span()
            span.span_kind = span_module.SpanKind.SERVER
            tracer.add_attribute_to_current_span(
                attribute_key=HTTP_HOST, attribute_value=request.get_host())
            tracer.add_attribute_to_current_span(
                attribute_key=HTTP_METHOD, attribute_value=request.method)
            tracer.add_attribute_to_current_span(attribute_key=HTTP_PATH,
                                                 attribute_value=str(
                                                     request.path))
            tracer.add_attribute_to_current_span(attribute_key=HTTP_ROUTE,
                                                 attribute_value=str(
                                                     request.path))
            tracer.add_attribute_to_current_span(
                attribute_key=HTTP_URL,
                attribute_value=str(request.build_absolute_uri()))

            # Add the span to thread local
            # in some cases (exceptions, timeouts) currentspan in
            # response event will be one of a child spans.
            # let's keep reference to 'django' span and
            # use it in response event
            execution_context.set_opencensus_attr(SPAN_THREAD_LOCAL_KEY, span)

        except Exception:  # pragma: NO COVER
            log.error('Failed to trace request', exc_info=True)
Ejemplo n.º 14
0
    def _transmit_without_retry(self, envelopes):
        # Contains logic from transport._transmit
        # TODO: Remove this function from exporter and
        # consolidate with transport._transmit to cover
        # all exporter use cases.
        # Uses cases pertain to properly handling failures
        # and implementing a retry policy for this exporter
        # TODO: implement retry policy
        """
        Transmit the data envelopes to the ingestion service.
        Does not perform retry logic. For partial success and
        non-retryable failure, simply outputs result to logs.
        This function should never throw exception.
        """
        blacklist_hostnames = execution_context.get_opencensus_attr(
            'blacklist_hostnames', )
        execution_context.set_opencensus_attr(
            'blacklist_hostnames',
            ['dc.services.visualstudio.com'],
        )
        try:
            response = requests.post(
                url=self.options.endpoint,
                data=json.dumps(envelopes),
                headers={
                    'Accept': 'application/json',
                    'Content-Type': 'application/json; charset=utf-8',
                },
                timeout=self.options.timeout,
            )
        except Exception as ex:
            # No retry policy, log output
            logger.warning('Transient client side error %s.', ex)
            return
        finally:
            execution_context.set_opencensus_attr(
                'blacklist_hostnames',
                blacklist_hostnames,
            )

        text = 'N/A'
        data = None
        # Handle the possible results from the response
        if response is None:
            logger.warning('Error: cannot read response.')
            return
        try:
            status_code = response.status_code
        except Exception as ex:
            logger.warning('Error while reading response status code %s.', ex)
            return
        try:
            text = response.text
        except Exception as ex:
            logger.warning('Error while reading response body %s.', ex)
            return
        try:
            data = json.loads(text)
        except Exception as ex:
            logger.warning(
                'Error while loading ' + 'json from response body %s.', ex)
            return
        if status_code == 200:
            logger.info('Transmission succeeded: %s.', text)
            return
        # Check for retryable partial content
        if status_code == 206:
            if data:
                try:
                    retryable_envelopes = []
                    for error in data['errors']:
                        if error['statusCode'] in (
                                429,  # Too Many Requests
                                500,  # Internal Server Error
                                503,  # Service Unavailable
                        ):
                            retryable_envelopes.append(
                                envelopes[error['index']])
                        else:
                            logger.error(
                                'Data drop %s: %s %s.',
                                error['statusCode'],
                                error['message'],
                                envelopes[error['index']],
                            )
                    # show the envelopes that can be
                    # retried manually for visibility
                    if retryable_envelopes:
                        logger.warning(
                            'Error while processing data. Data dropped. ' +
                            'Consider manually retrying for envelopes: %s.',
                            retryable_envelopes)
                    return
                except Exception:
                    logger.exception('Error while processing %s: %s.',
                                     status_code, text)
                    return
        # Check for non-tryable result
        if status_code in (
                206,  # Partial Content
                429,  # Too Many Requests
                500,  # Internal Server Error
                503,  # Service Unavailable
        ):
            # server side error (retryable)
            logger.warning(
                'Transient server side error %s: %s. ' +
                'Consider manually trying.',
                status_code,
                text,
            )
        else:
            # server side error (non-retryable)
            logger.error(
                'Non-retryable server side error %s: %s.',
                status_code,
                text,
            )
Ejemplo n.º 15
0
 def _transmit(self, envelopes):
     """
     Transmit the data envelopes to the ingestion service.
     Return a negative value for partial success or non-retryable failure.
     Return 0 if all envelopes have been successfully ingested.
     Return the next retry time in seconds for retryable failure.
     This function should never throw exception.
     """
     # TODO: prevent requests being tracked
     blacklist_hostnames = execution_context.get_opencensus_attr(
         'blacklist_hostnames',
     )
     execution_context.set_opencensus_attr(
         'blacklist_hostnames',
         ['dc.services.visualstudio.com'],
     )
     try:
         response = requests.post(
             url=self.options.endpoint,
             data=json.dumps(envelopes),
             headers={
                 'Accept': 'application/json',
                 'Content-Type': 'application/json; charset=utf-8',
             },
             timeout=self.options.timeout,
         )
     except Exception as ex:  # TODO: consider RequestException
         logger.warning('Transient client side error %s.', ex)
         # client side error (retryable)
         return self.options.minimum_retry_interval
     finally:
         execution_context.set_opencensus_attr(
             'blacklist_hostnames',
             blacklist_hostnames,
         )
     text = 'N/A'
     data = None
     try:
         text = response.text
     except Exception as ex:
         logger.warning('Error while reading response body %s.', ex)
     else:
         try:
             data = json.loads(text)
         except Exception:
             pass
     if response.status_code == 200:
         logger.info('Transmission succeeded: %s.', text)
         return 0
     if response.status_code == 206:  # Partial Content
         # TODO: store the unsent data
         if data:
             try:
                 resend_envelopes = []
                 for error in data['errors']:
                     if error['statusCode'] in (
                             429,  # Too Many Requests
                             500,  # Internal Server Error
                             503,  # Service Unavailable
                     ):
                         resend_envelopes.append(envelopes[error['index']])
                     else:
                         logger.error(
                             'Data drop %s: %s %s.',
                             error['statusCode'],
                             error['message'],
                             envelopes[error['index']],
                         )
                 if resend_envelopes:
                     self.storage.put(resend_envelopes)
             except Exception as ex:
                 logger.error(
                     'Error while processing %s: %s %s.',
                     response.status_code,
                     text,
                     ex,
                 )
             return -response.status_code
         # cannot parse response body, fallback to retry
     if response.status_code in (
             206,  # Partial Content
             429,  # Too Many Requests
             500,  # Internal Server Error
             503,  # Service Unavailable
     ):
         logger.warning(
             'Transient server side error %s: %s.',
             response.status_code,
             text,
         )
         # server side error (retryable)
         return self.options.minimum_retry_interval
     logger.error(
         'Non-retryable server side error %s: %s.',
         response.status_code,
         text,
     )
     # server side error (non-retryable)
     return -response.status_code