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"))
Пример #2
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)
Пример #3
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
Пример #4
0
def _get_django_request():
    """Get Django request from thread local.

    :rtype: str
    :returns: Django request.
    """
    return execution_context.get_opencensus_attr(REQUEST_THREAD_LOCAL_KEY)
Пример #5
0
def _get_django_span():
    """Get Django span from thread local.

    :rtype: str
    :returns: Django request.
    """
    return execution_context.get_opencensus_attr(SPAN_THREAD_LOCAL_KEY)
Пример #6
0
    def call(url, *args, **kwargs):
        blacklist_hostnames = execution_context.get_opencensus_attr(
            'blacklist_hostnames')
        parsed_url = urlparse(url)
        if parsed_url.port is None:
            dest_url = parsed_url.hostname
        else:
            dest_url = '{}:{}'.format(parsed_url.hostname, parsed_url.port)
        if utils.disable_tracing_hostname(dest_url, blacklist_hostnames):
            return requests_func(url, *args, **kwargs)

        _tracer = execution_context.get_opencensus_tracer()
        _span = _tracer.start_span()
        _span.name = '[requests]{}'.format(requests_func.__name__)
        _span.span_kind = span_module.SpanKind.CLIENT

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

        result = requests_func(url, *args, **kwargs)

        # Add the status code to attributes
        _tracer.add_attribute_to_current_span(HTTP_STATUS_CODE,
                                              str(result.status_code))

        _tracer.end_span()
        return result
    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)
Пример #8
0
    def call(url, *args, **kwargs):
        # Check if request was sent from an exporter. If so, do not wrap.
        if execution_context.is_exporter():
            return requests_func(url, *args, **kwargs)
        excludelist_hostnames = execution_context.get_opencensus_attr(
            'excludelist_hostnames')
        parsed_url = urlparse(url)
        if parsed_url.port is None:
            dest_url = parsed_url.hostname
        else:
            dest_url = '{}:{}'.format(parsed_url.hostname, parsed_url.port)
        if utils.disable_tracing_hostname(dest_url, excludelist_hostnames):
            return requests_func(url, *args, **kwargs)

        path = parsed_url.path if parsed_url.path else '/'

        _tracer = execution_context.get_opencensus_tracer()
        _span = _tracer.start_span()
        _span.name = '{}'.format(path)
        _span.span_kind = span_module.SpanKind.CLIENT

        # Add the component type to attributes
        _tracer.add_attribute_to_current_span("component", "HTTP")

        # Add the requests host to attributes
        _tracer.add_attribute_to_current_span(HTTP_HOST, dest_url)

        # Add the requests method to attributes
        _tracer.add_attribute_to_current_span(HTTP_METHOD,
                                              requests_func.__name__.upper())

        # Add the requests path to attributes
        _tracer.add_attribute_to_current_span(HTTP_PATH, path)

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

        try:
            result = requests_func(url, *args, **kwargs)
        except requests.Timeout:
            _span.set_status(exceptions_status.TIMEOUT)
            raise
        except requests.URLRequired:
            _span.set_status(exceptions_status.INVALID_URL)
            raise
        except Exception as e:
            _span.set_status(exceptions_status.unknown(e))
            raise
        else:
            # Add the status code to attributes
            _tracer.add_attribute_to_current_span(HTTP_STATUS_CODE,
                                                  result.status_code)
            _span.set_status(utils.status_from_http_code(result.status_code))
            return result
        finally:
            _tracer.end_span()
Пример #9
0
def _on_finish(func, handler, args, kwargs):
    if execution_context.get_opencensus_attr(TORNADO_EXCEPTION) is not None:
        return

    tracer = execution_context.get_opencensus_tracer()
    span = execution_context.get_current_span()
    span.add_attribute(attribute_key=HTTP_STATUS_CODE,
                       attribute_value=str(handler.get_status()))
    tracer.finish()
    return func(*args, **kwargs)
Пример #10
0
    def call(self, *args, **kwargs):
        _tracer = execution_context.get_opencensus_tracer()
        current_span_id = execution_context.get_opencensus_attr(
            'httplib/current_span_id')

        span = _tracer.current_span()

        # No corresponding request span is found, request not traced.
        if span.span_id != current_span_id:
            return response_func(self, *args, **kwargs)

        result = response_func(self, *args, **kwargs)

        # Add the status code to attributes
        _tracer.add_attribute_to_current_span(HTTP_STATUS_CODE,
                                              str(result.status))

        _tracer.end_span()
        return result
Пример #11
0
def wrap_session_request(wrapped, instance, args, kwargs):
    """Wrap the session function to trace it."""
    # Check if request was sent from an exporter. If so, do not wrap.
    if execution_context.is_exporter():
        return wrapped(*args, **kwargs)

    method = kwargs.get('method') or args[0]
    url = kwargs.get('url') or args[1]

    blacklist_hostnames = execution_context.get_opencensus_attr(
        'blacklist_hostnames')
    parsed_url = urlparse(url)
    if parsed_url.port is None:
        dest_url = parsed_url.hostname
    else:
        dest_url = '{}:{}'.format(parsed_url.hostname, parsed_url.port)
    if utils.disable_tracing_hostname(dest_url, blacklist_hostnames):
        return wrapped(*args, **kwargs)

    _tracer = execution_context.get_opencensus_tracer()
    _span = _tracer.start_span()

    _span.name = '[requests]{}'.format(method)
    _span.span_kind = span_module.SpanKind.CLIENT

    try:
        tracer_headers = _tracer.propagator.to_headers(_tracer.span_context)
        kwargs.setdefault('headers', {}).update(tracer_headers)
    except Exception:  # pragma: NO COVER
        pass

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

    result = wrapped(*args, **kwargs)

    # Add the status code to attributes
    _tracer.add_attribute_to_current_span(HTTP_STATUS_CODE,
                                          str(result.status_code))

    _tracer.end_span()
    return result
Пример #12
0
    def call(self, *args, **kwargs):
        # Check if request was sent from an exporter. If so, do not wrap.
        if execution_context.is_exporter():
            return response_func(self, *args, **kwargs)
        _tracer = execution_context.get_opencensus_tracer()
        current_span_id = execution_context.get_opencensus_attr(
            'httplib/current_span_id')

        span = _tracer.current_span()

        # No corresponding request span is found, request not traced.
        if not span or span.span_id != current_span_id:
            return response_func(self, *args, **kwargs)

        result = response_func(self, *args, **kwargs)

        # Add the status code to attributes
        _tracer.add_attribute_to_current_span(HTTP_STATUS_CODE, result.status)

        _tracer.end_span()
        return result
Пример #13
0
def wrap_session_request(wrapped, instance, args, kwargs):
    """Wrap the session function to trace it."""
    # Check if request was sent from an exporter. If so, do not wrap.
    if execution_context.is_exporter():
        return wrapped(*args, **kwargs)

    method = kwargs.get('method') or args[0]
    url = kwargs.get('url') or args[1]

    excludelist_hostnames = execution_context.get_opencensus_attr(
        'excludelist_hostnames')
    parsed_url = urlparse(url)
    if parsed_url.port is None:
        dest_url = parsed_url.hostname
    else:
        dest_url = '{}:{}'.format(parsed_url.hostname, parsed_url.port)
    if utils.disable_tracing_hostname(dest_url, excludelist_hostnames):
        return wrapped(*args, **kwargs)

    path = parsed_url.path if parsed_url.path else '/'

    _tracer = execution_context.get_opencensus_tracer()
    _span = _tracer.start_span()

    _span.name = '{}'.format(path)
    _span.span_kind = span_module.SpanKind.CLIENT

    try:
        tracer_headers = _tracer.propagator.to_headers(_tracer.span_context)
        kwargs.setdefault('headers', {}).update(tracer_headers)
    except Exception:  # pragma: NO COVER
        pass

    # Add the component type to attributes
    _tracer.add_attribute_to_current_span("component", "HTTP")

    # Add the requests host to attributes
    _tracer.add_attribute_to_current_span(HTTP_HOST, dest_url)

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

    # Add the requests path to attributes
    _tracer.add_attribute_to_current_span(HTTP_PATH, path)

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

    try:
        result = wrapped(*args, **kwargs)
    except requests.Timeout:
        _span.set_status(exceptions_status.TIMEOUT)
        raise
    except requests.URLRequired:
        _span.set_status(exceptions_status.INVALID_URL)
        raise
    except Exception as e:
        _span.set_status(exceptions_status.unknown(e))
        raise
    else:
        # Add the status code to attributes
        _tracer.add_attribute_to_current_span(HTTP_STATUS_CODE,
                                              result.status_code)
        _span.set_status(utils.status_from_http_code(result.status_code))
        return result
    finally:
        _tracer.end_span()
Пример #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,
            )
Пример #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
Пример #16
0
def _trace_db_call(execute, sql, params, many, context):
    explain_mode = execution_context.get_opencensus_attr('explain_mode')

    if "EXPLAIN" in sql:
        logger.debug("_trace_db_call: Processing EXPLAIN statement")
        return execute(sql, params, many, context)
    
    logger.debug(f"_trace_db_call: {sql}")

    tracer = _get_current_tracer()
    if not tracer:
        return execute(sql, params, many, context)

    vendor = context['connection'].vendor
    alias = context['connection'].alias

    span = tracer.start_span()
    try:
        (sql_command, subcommand, *_) = sql.split(maxsplit=2)
        logger.info(f"subcommand: '{subcommand}'")
        if "COUNT(*)" == subcommand:
            span.name = '{}.COUNT'.format(vendor)
        else:
            span.name = '{}.{}'.format(vendor, sql_command)                
    except Exception:
        span.name = '{}.OTHER'.format(vendor)
        logger.warning("Could not parse SQL statement for detailed tracing", exc_info=True)

    span.span_kind = span_module.SpanKind.CLIENT

    tracer.add_attribute_to_current_span('component', vendor)
    tracer.add_attribute_to_current_span('db.instance', alias)
    tracer.add_attribute_to_current_span('db.statement', sql)
    tracer.add_attribute_to_current_span('db.type', 'sql')
    tracer.add_attribute_to_current_span('python.traceback', common_utils.get_traceback())

    # EXPLAIN is expensive and needs to be explicitly enabled
    if explain_mode is not None:
        try:
            with connections[alias].cursor() as cursor:
                # EXPLAIN ANALYZE only works under certain circumstances
                if "analyze" == explain_mode and "postgresql" == vendor and "SELECT" == sql_command:
                    cursor.execute("EXPLAIN ANALYZE {0}".format(sql), params)
                else:
                    cursor.execute("EXPLAIN {0}".format(sql), params)
                planresult = cursor.fetchall()

            logger.debug("EXPLAIN plan: {0}".format(planresult))
            tracer.add_attribute_to_current_span('db.plan', "{0}".format(planresult))
        except Exception: # pragma: NO COVER
            logger.warning("Could not retrieve EXPLAIN plan", exc_info=True)
            tracer.add_attribute_to_current_span('db.plan', 'not available')
            pass        

    try:
        result = execute(sql, params, many, context)
    except Exception as exc:  # pragma: NO COVER
        status = status_module.Status.from_exception(exc)
        span.set_status(status)
        raise
    else:
        return result
    finally:
        tracer.end_span()
Пример #17
0
def get_parent():
    """Returns parent execution context. """
    return execution_context.get_opencensus_attr(_threadlocal_parent)
    def test_no_attrs(self):
        key = 'key'

        result = execution_context.get_opencensus_attr(key)

        self.assertIsNone(result)