def set_code(self, code): self.code = code # use details if we already have it, otherwise the status description details = self.details or code.value[1] self._active_span.set_status( Status(status_code=StatusCode(code.value[0]), description=details)) return self._servicer_context.set_code(code)
def _apply_response(span: Span, response: urllib3.response.HTTPResponse) -> None: if not span.is_recording(): return span.set_attribute("http.status_code", response.status) span.set_attribute("http.status_text", response.reason) span.set_status(Status(http_status_to_status_code(response.status)))
def process_response( self, req, resp, resource, req_succeeded=None ): # pylint:disable=R0201 span = req.env.get(_ENVIRON_SPAN_KEY) if not span or not span.is_recording(): return status = resp.status reason = None if resource is None: status = "404" reason = "NotFound" exc_type, exc, _ = sys.exc_info() if exc_type and not req_succeeded: if "HTTPNotFound" in exc_type.__name__: status = "404" reason = "NotFound" else: status = "500" reason = "{}: {}".format(exc_type.__name__, exc) status = status.split(" ")[0] try: status_code = int(status) except ValueError: pass finally: span.set_attribute("http.status_code", status_code) span.set_status( Status( status_code=http_status_to_status_code(status_code), description=reason, ) )
def intercept_unary(self, request, metadata, client_info, invoker): if not metadata: mutable_metadata = OrderedDict() else: mutable_metadata = OrderedDict(metadata) with self._start_guarded_span(client_info.full_method) as guarded_span: _inject_span_context(mutable_metadata) metadata = tuple(mutable_metadata.items()) rpc_info = RpcInfo( full_method=client_info.full_method, metadata=metadata, timeout=client_info.timeout, request=request, ) try: result = invoker(request, metadata) except grpc.RpcError as err: guarded_span.generated_span.set_status(Status( StatusCode.ERROR)) guarded_span.generated_span.set_attribute( "rpc.grpc.status_code", err.code().value[0]) raise err return self._trace_result(guarded_span, rpc_info, result)
def use_span( self, span: trace_api.Span, end_on_exit: bool = False, ) -> Iterator[trace_api.Span]: try: token = context_api.attach(context_api.set_value(SPAN_KEY, span)) try: yield span finally: context_api.detach(token) except Exception as exc: # pylint: disable=broad-except # Record the exception as an event if isinstance(span, Span) and span.is_recording(): # pylint:disable=protected-access if span._record_exception: span.record_exception(exc) # Records status if use_span is used # i.e. with tracer.start_as_current_span() as span: if (span.status.status_code is StatusCode.UNSET and span._set_status_on_exception): span.set_status( Status( status_code=StatusCode.ERROR, description="{}: {}".format( type(exc).__name__, exc), )) raise finally: if end_on_exit: span.end()
def instrumented_request(self, method, url, *args, **kwargs): if context.get_value("suppress_instrumentation"): return wrapped(self, method, url, *args, **kwargs) # See # https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md#http-client try: parsed_url = urlparse(url) except ValueError as exc: # Invalid URL path = "<Unparsable URL: {}>".format(exc) else: if parsed_url is None: path = "<URL parses to None>" path = parsed_url.path with tracer.start_as_current_span(path, kind=SpanKind.CLIENT) as span: span.set_attribute("component", "http") span.set_attribute("http.method", method.upper()) span.set_attribute("http.url", url) headers = kwargs.setdefault("headers", {}) propagators.inject(type(headers).__setitem__, headers) result = wrapped(self, method, url, *args, **kwargs) # *** PROCEED span.set_attribute("http.status_code", result.status_code) span.set_attribute("http.status_text", result.reason) span.set_status( Status(_http_status_to_canonical_code(result.status_code))) return result
def __exit__( self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType], ) -> None: """Ends context manager and calls `end` on the `Span`.""" if exc_val is not None: # Record the exception as an event # pylint:disable=protected-access if self._record_exception: self.record_exception(exception=exc_val, escaped=True) # Records status if span is used as context manager # i.e. with tracer.start_span() as span: if ( self.status.status_code is StatusCode.UNSET and self._set_status_on_exception ): self.set_status( Status( status_code=StatusCode.ERROR, description="{}: {}".format( exc_type.__name__, exc_val ), ) ) super().__exit__(exc_type, exc_val, exc_tb)
def started(self, event: monitoring.CommandStartedEvent): """ Method to handle a pymongo CommandStartedEvent """ if not self.is_enabled: return command = event.command.get(event.command_name, "") name = event.command_name statement = event.command_name if command: name += "." + str(command) statement += " " + str(command) try: span = self._tracer.start_span(name, kind=SpanKind.CLIENT) if span.is_recording(): span.set_attribute(SpanAttributes.DB_SYSTEM, DbSystemValues.MONGODB.value) span.set_attribute(SpanAttributes.DB_NAME, event.database_name) span.set_attribute(SpanAttributes.DB_STATEMENT, statement) if event.connection_id is not None: span.set_attribute(SpanAttributes.NET_PEER_NAME, event.connection_id[0]) span.set_attribute(SpanAttributes.NET_PEER_PORT, event.connection_id[1]) # Add Span to dictionary self._span_dict[_get_span_dict_key(event)] = span except Exception as ex: # noqa pylint: disable=broad-except if span is not None and span.is_recording(): span.set_status(Status(StatusCode.ERROR, str(ex))) span.end() self._pop_span(event)
def intercept_stream(self, request_or_iterator, metadata, client_info, invoker): if client_info.is_server_stream: return self._intercept_server_stream(request_or_iterator, metadata, client_info, invoker) if not metadata: mutable_metadata = OrderedDict() else: mutable_metadata = OrderedDict(metadata) with self._start_guarded_span(client_info.full_method) as guarded_span: inject(mutable_metadata, setter=_carrier_setter) metadata = tuple(mutable_metadata.items()) rpc_info = RpcInfo( full_method=client_info.full_method, metadata=metadata, timeout=client_info.timeout, request=request_or_iterator, ) rpc_info.request = request_or_iterator try: result = invoker(request_or_iterator, metadata) except grpc.RpcError as err: guarded_span.generated_span.set_status(Status( StatusCode.ERROR)) guarded_span.generated_span.set_attribute( SpanAttributes.RPC_GRPC_STATUS_CODE, err.code().value[0], ) raise err return self._trace_result(guarded_span, rpc_info, result)
def __init__( self, name: str = None, context: trace_api.SpanContext = None, parent: Optional[trace_api.SpanContext] = None, resource: Resource = Resource.create({}), attributes: types.Attributes = None, events: Sequence[Event] = None, links: Sequence[trace_api.Link] = (), kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL, instrumentation_info: InstrumentationInfo = None, status: Status = Status(StatusCode.UNSET), start_time: Optional[int] = None, end_time: Optional[int] = None, ) -> None: self._name = name self._context = context self._kind = kind self._instrumentation_info = instrumentation_info self._parent = parent self._start_time = start_time self._end_time = end_time self._attributes = attributes self._events = events self._links = links self._resource = resource self._status = status
def _intercept_server_stream(self, request_or_iterator, metadata, client_info, invoker): if not metadata: mutable_metadata = OrderedDict() else: mutable_metadata = OrderedDict(metadata) with self._start_span(client_info.full_method) as span: inject(mutable_metadata, setter=_carrier_setter) metadata = tuple(mutable_metadata.items()) rpc_info = RpcInfo( full_method=client_info.full_method, metadata=metadata, timeout=client_info.timeout, ) if client_info.is_client_stream: rpc_info.request = request_or_iterator try: result = invoker(request_or_iterator, metadata) for response in result: yield response except grpc.RpcError as err: span.set_status(Status(StatusCode.ERROR)) span.set_attribute(SpanAttributes.RPC_GRPC_STATUS_CODE, err.code().value[0]) raise err
async def _do_execute(self, func, instance, args, kwargs): tracer = getattr(asyncpg, _APPLIED) exception = None params = getattr(instance, "_params", {}) name = args[0] if args[0] else params.get("database", "postgresql") with tracer.start_as_current_span(name, kind=SpanKind.CLIENT) as span: if span.is_recording(): span_attributes = _hydrate_span_from_args( instance, args[0], args[1:] if self.capture_parameters else None, ) for attribute, value in span_attributes.items(): span.set_attribute(attribute, value) try: result = await func(*args, **kwargs) except Exception as exc: # pylint: disable=W0703 exception = exc raise finally: if span.is_recording() and exception is not None: span.set_status(Status(StatusCode.ERROR)) return result
def __call__(self, environ, start_response): """The WSGI application Args: environ: A WSGI environment. start_response: The WSGI start_response callable. """ token = context.attach(propagators.extract(carrier_getter, environ)) span_name = self.name_callback(environ) span = self.tracer.start_span( span_name, kind=trace.SpanKind.SERVER, attributes=collect_request_attributes(environ), ) try: with self.tracer.use_span(span): start_response = self._create_start_response( span, start_response) iterable = self.wsgi(environ, start_response) return _end_span_after_iterating(iterable, span, self.tracer, token) except Exception as ex: if span.is_recording(): span.set_status(Status(StatusCode.ERROR, str(ex))) span.end() context.detach(token) raise
def use_span(self, span: trace_api.Span, end_on_exit: bool = False) -> Iterator[trace_api.Span]: try: token = context_api.attach(context_api.set_value(SPAN_KEY, span)) try: yield span finally: context_api.detach(token) except Exception as error: # pylint: disable=broad-except if (isinstance(span, Span) and span.status is None and span._set_status_on_exception # pylint:disable=protected-access # noqa ): span.set_status( Status( canonical_code=StatusCanonicalCode.UNKNOWN, description="{}: {}".format( type(error).__name__, error), )) raise finally: if end_on_exit: span.end()
def instrumented_request(self, method, url, *args, **kwargs): if context.get_value("suppress_instrumentation"): return wrapped(self, method, url, *args, **kwargs) # See # https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md#http-client try: parsed_url = urlparse(url) span_name = parsed_url.path except ValueError as exc: # Invalid URL span_name = "<Unparsable URL: {}>".format(exc) exception = None with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT) as span: span.set_attribute("component", "http") span.set_attribute("http.method", method.upper()) span.set_attribute("http.url", url) headers = kwargs.setdefault("headers", {}) propagators.inject(type(headers).__setitem__, headers) try: result = wrapped(self, method, url, *args, **kwargs) # *** PROCEED except Exception as exc: # pylint: disable=W0703 exception = exc result = getattr(exc, "response", None) if exception is not None: span.set_status(Status( _exception_to_canonical_code(exception))) if result is not None: span.set_attribute("http.status_code", result.status_code) span.set_attribute("http.status_text", result.reason) span.set_status( Status(_http_status_to_canonical_code(result.status_code))) if span_callback is not None: span_callback(span, result) if exception is not None: raise exception.with_traceback(exception.__traceback__) return result
def _instrumented_requests_call(method: str, url: str, call_wrapped, get_or_create_headers): if context.get_value("suppress_instrumentation") or context.get_value( _SUPPRESS_REQUESTS_INSTRUMENTATION_KEY): return call_wrapped() # See # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-client method = method.upper() span_name = "" if name_callback is not None: span_name = name_callback(method, url) if not span_name or not isinstance(span_name, str): span_name = get_default_span_name(method) labels = {} labels["http.method"] = method labels["http.url"] = url with get_tracer(__name__, __version__, tracer_provider).start_as_current_span( span_name, kind=SpanKind.CLIENT) as span: exception = None if span.is_recording(): span.set_attribute("http.method", method) span.set_attribute("http.url", url) headers = get_or_create_headers() inject(type(headers).__setitem__, headers) token = context.attach( context.set_value(_SUPPRESS_REQUESTS_INSTRUMENTATION_KEY, True)) try: result = call_wrapped() # *** PROCEED except Exception as exc: # pylint: disable=W0703 exception = exc result = getattr(exc, "response", None) finally: context.detach(token) if isinstance(result, Response): if span.is_recording(): span.set_attribute("http.status_code", result.status_code) span.set_attribute("http.status_text", result.reason) span.set_status( Status(http_status_to_status_code(result.status_code))) labels["http.status_code"] = str(result.status_code) if result.raw and result.raw.version: labels["http.flavor"] = (str(result.raw.version)[:1] + "." + str(result.raw.version)[:-1]) if span_callback is not None: span_callback(span, result) if exception is not None: raise exception.with_traceback(exception.__traceback__) return result
def test_export_protobuf_max_tag_length(self): service_name = "test-service" span_context = trace_api.SpanContext( 0x0E0C63257DE34C926F9EFCD03927272E, 0x04BF92DEEFC58C92, is_remote=False, trace_flags=TraceFlags(TraceFlags.SAMPLED), ) span = trace._Span( name="test-span", context=span_context, ) span.start() span.resource = Resource({}) # added here to preserve order span.set_attribute("k1", "v" * 500) span.set_attribute("k2", "v" * 50) span.set_status(Status(StatusCode.ERROR, "Example description")) span.end() exporter = ZipkinSpanExporter( service_name, transport_format=TRANSPORT_FORMAT_PROTOBUF, ) mock_post = MagicMock() with patch("requests.post", mock_post): mock_post.return_value = MockResponse(200) status = exporter.export([span]) self.assertEqual(SpanExportResult.SUCCESS, status) # pylint: disable=unsubscriptable-object kwargs = mock_post.call_args[1] actual_spans = zipkin_pb2.ListOfSpans.FromString(kwargs["data"]) span_tags = actual_spans.spans[0].tags self.assertEqual(len(span_tags["k1"]), 128) self.assertEqual(len(span_tags["k2"]), 50) exporter = ZipkinSpanExporter( service_name, transport_format=TRANSPORT_FORMAT_PROTOBUF, max_tag_value_length=2, ) mock_post = MagicMock() with patch("requests.post", mock_post): mock_post.return_value = MockResponse(200) status = exporter.export([span]) self.assertEqual(SpanExportResult.SUCCESS, status) # pylint: disable=unsubscriptable-object kwargs = mock_post.call_args[1] actual_spans = zipkin_pb2.ListOfSpans.FromString(kwargs["data"]) span_tags = actual_spans.spans[0].tags self.assertEqual(len(span_tags["k1"]), 2) self.assertEqual(len(span_tags["k2"]), 2)
def instrumented_request(self, method, url, *args, **kwargs): if context.get_value("suppress_instrumentation"): return wrapped(self, method, url, *args, **kwargs) # See # https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/http.md#http-client span_name = "HTTP {}".format(method) exception = None with get_tracer(__name__, __version__, tracer_provider).start_as_current_span( span_name, kind=SpanKind.CLIENT) as span: span.set_attribute("component", "http") span.set_attribute("http.method", method.upper()) span.set_attribute("http.url", url) headers = kwargs.get("headers", {}) or {} propagators.inject(type(headers).__setitem__, headers) kwargs["headers"] = headers try: result = wrapped(self, method, url, *args, **kwargs) # *** PROCEED except Exception as exc: # pylint: disable=W0703 exception = exc result = getattr(exc, "response", None) if exception is not None: span.set_status(Status( _exception_to_canonical_code(exception))) span.record_exception(exception) if result is not None: span.set_attribute("http.status_code", result.status_code) span.set_attribute("http.status_text", result.reason) span.set_status( Status(http_status_to_canonical_code(result.status_code))) if span_callback is not None: span_callback(span, result) if exception is not None: raise exception.with_traceback(exception.__traceback__) return result
def abort(self, code, details): self.code = code self.details = details self._active_span.set_attribute("rpc.grpc.status_code", code.name) self._active_span.set_status( Status(status_code=StatusCode.ERROR, description=details) ) return self._servicer_context.abort(code, details)
def traced_execution(self, query_method: typing.Callable[..., typing.Any], *args: typing.Tuple[typing.Any, typing.Any], **kwargs: typing.Dict[typing.Any, typing.Any]): with self._db_api_integration.get_tracer().start_as_current_span( self._db_api_integration.name, kind=SpanKind.CLIENT) as span: self._populate_span(span, *args) try: result = query_method(*args, **kwargs) if span.is_recording(): span.set_status(Status(StatusCanonicalCode.OK)) return result except Exception as ex: # pylint: disable=broad-except if span.is_recording(): span.set_status( Status(StatusCanonicalCode.UNKNOWN, str(ex))) raise ex
def set_details(self, details): self.details = details self._active_span.set_status( Status( status_code=StatusCode(self.code.value[0]), description=details, )) return self._servicer_context.set_details(details)
def succeeded(self, event: monitoring.CommandSucceededEvent): span = self._get_span(event) if span is not None: span.set_attribute("db.mongo.duration_micros", event.duration_micros) span.set_status(Status(StatusCanonicalCode.OK, event.reply)) span.end() self._remove_span(event)
def failed(self, event: monitoring.CommandFailedEvent): span = self._get_span(event) if span is not None: span.set_attribute("db.mongo.duration_micros", event.duration_micros) span.set_status(Status(StatusCanonicalCode.UNKNOWN, event.failure)) span.end() self._remove_span(event)
def __init__( self, name: str, context: trace_api.SpanContext, parent: Optional[trace_api.SpanContext] = None, sampler: Optional[sampling.Sampler] = None, trace_config: None = None, # TODO resource: Resource = Resource.create({}), attributes: types.Attributes = None, events: Sequence[Event] = None, links: Sequence[trace_api.Link] = (), kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL, span_processor: SpanProcessor = SpanProcessor(), instrumentation_info: InstrumentationInfo = None, record_exception: bool = True, set_status_on_exception: bool = True, ) -> None: self.name = name self.context = context self.parent = parent self.sampler = sampler self.trace_config = trace_config self.resource = resource self.kind = kind self._record_exception = record_exception self._set_status_on_exception = set_status_on_exception self.span_processor = span_processor self.status = Status(StatusCode.UNSET) self._lock = threading.Lock() _filter_attribute_values(attributes) if not attributes: self.attributes = self._new_attributes() else: self.attributes = BoundedDict.from_map( SPAN_ATTRIBUTE_COUNT_LIMIT, attributes ) self.events = self._new_events() if events: for event in events: _filter_attribute_values(event.attributes) # pylint: disable=protected-access event._attributes = _create_immutable_attributes( event.attributes ) self.events.append(event) if links is None: self.links = self._new_links() else: self.links = BoundedList.from_seq(SPAN_LINK_COUNT_LIMIT, links) self._end_time = None # type: Optional[int] self._start_time = None # type: Optional[int] self.instrumentation_info = instrumentation_info
def add_response_attributes(span, start_response_status, response_headers): # pylint: disable=unused-argument """Adds HTTP response attributes to span using the arguments passed to a PEP3333-conforming start_response callable.""" status_code, status_text = start_response_status.split(" ", 1) span.set_attribute("http.status_text", status_text) try: status_code = int(status_code) except ValueError: span.set_status( Status( StatusCanonicalCode.UNKNOWN, "Non-integer HTTP status: " + repr(status_code), )) else: span.set_attribute("http.status_code", status_code) span.set_status(Status(http_status_to_canonical_code(status_code)))
def add_response_attributes(span, start_response_status, response_headers): # pylint: disable=unused-argument """Adds HTTP response attributes to span using the arguments passed to a PEP3333-conforming start_response callable.""" if not span.is_recording(): return status_code, _ = start_response_status.split(" ", 1) try: status_code = int(status_code) except ValueError: span.set_status( Status( StatusCode.ERROR, "Non-integer HTTP status: " + repr(status_code), )) else: span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code) span.set_status(Status(http_status_to_status_code(status_code)))
def set_status_code(span, status_code): """Adds HTTP response attributes to span using the status_code argument.""" if not span.is_recording(): return try: status_code = int(status_code) except ValueError: span.set_status( Status( StatusCode.ERROR, "Non-integer HTTP status: " + repr(status_code), ) ) else: span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code) span.set_status( Status(http_status_to_status_code(status_code, server_span=True)) )
def test_invalid_description(self): with self.assertLogs(level=WARNING) as warning: status = Status(description={"test": "val"}) # type: ignore self.assertIs(status.status_code, StatusCode.UNSET) self.assertEqual(status.description, None) self.assertIn( "Invalid status description type, expected str", warning.output[0], )
def set_details(self, details): self.details = details if self.code != grpc.StatusCode.OK: self._active_span.set_status( Status( status_code=StatusCode.ERROR, description="{}:{}".format(self.code, details), )) return self._servicer_context.set_details(details)
def _instrumented_requests_call( method: str, url: str, call_wrapped, get_or_create_headers ): if context.get_value( _SUPPRESS_INSTRUMENTATION_KEY ) or context.get_value(_SUPPRESS_HTTP_INSTRUMENTATION_KEY): return call_wrapped() # See # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-client method = method.upper() span_name = "" if name_callback is not None: span_name = name_callback(method, url) if not span_name or not isinstance(span_name, str): span_name = get_default_span_name(method) url = remove_url_credentials(url) with tracer.start_as_current_span( span_name, kind=SpanKind.CLIENT ) as span: exception = None if span.is_recording(): span.set_attribute(SpanAttributes.HTTP_METHOD, method) span.set_attribute(SpanAttributes.HTTP_URL, url) headers = get_or_create_headers() inject(headers) token = context.attach( context.set_value(_SUPPRESS_HTTP_INSTRUMENTATION_KEY, True) ) try: result = call_wrapped() # *** PROCEED except Exception as exc: # pylint: disable=W0703 exception = exc result = getattr(exc, "response", None) finally: context.detach(token) if isinstance(result, Response): if span.is_recording(): span.set_attribute( SpanAttributes.HTTP_STATUS_CODE, result.status_code ) span.set_status( Status(http_status_to_status_code(result.status_code)) ) if span_callback is not None: span_callback(span, result) if exception is not None: raise exception.with_traceback(exception.__traceback__) return result