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"))
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)
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
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)
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)
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)
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)
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
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)
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, )
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