From 27e4c2ce34156bd0dd967cafb79c91aa1e85f498 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Wed, 3 Apr 2024 15:20:57 -0700 Subject: [PATCH 01/42] Update .pylintrc --- .pylintrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.pylintrc b/.pylintrc index 5ea4385ea0..114dadef75 100644 --- a/.pylintrc +++ b/.pylintrc @@ -81,6 +81,7 @@ disable=missing-docstring, missing-module-docstring, # temp-pylint-upgrade import-error, # needed as a workaround as reported here: https://github.com/open-telemetry/opentelemetry-python-contrib/issues/290 cyclic-import, + not-context-manager # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option From a674535691db63612ff7a903830fc44320426733 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Tue, 9 Apr 2024 16:12:00 -0700 Subject: [PATCH 02/42] wsgi, requests --- CHANGELOG.md | 5 + .../instrumentation/requests/__init__.py | 12 +-- .../instrumentation/wsgi/__init__.py | 83 ++++++++++++---- .../opentelemetry/instrumentation/_semconv.py | 96 ++++++++++++++++--- .../src/opentelemetry/util/http/__init__.py | 9 +- 5 files changed, 162 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c361881fd..754e16e67d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Rename `type` attribute to `asgi.event.type` in `opentelemetry-instrumentation-asgi` ([#2300](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2300)) +### Added + +- `opentelemetry-instrumentation-flask`, `opentelemetry-instrumentation-wsgi` Implement new semantic convention opt-in with stable http semantic conventions + ([#2002](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2002)) + ## Version 1.24.0/0.45b0 (2024-03-28) ### Added diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index d0150d57b7..cd8ebfe7be 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -70,11 +70,11 @@ _OpenTelemetryStabilitySignalType, _report_new, _report_old, - _set_http_hostname, + _set_http_host, _set_http_method, _set_http_net_peer_name, _set_http_network_protocol_version, - _set_http_port, + _set_http_peer_port_client, _set_http_scheme, _set_http_status_code, _set_http_url, @@ -174,14 +174,14 @@ def get_or_create_headers(): metric_labels, parsed_url.scheme, sem_conv_opt_in_mode ) if parsed_url.hostname: - _set_http_hostname( + _set_http_host( metric_labels, parsed_url.hostname, sem_conv_opt_in_mode ) _set_http_net_peer_name( metric_labels, parsed_url.hostname, sem_conv_opt_in_mode ) if _report_new(sem_conv_opt_in_mode): - _set_http_hostname( + _set_http_host( span_attributes, parsed_url.hostname, sem_conv_opt_in_mode, @@ -191,11 +191,11 @@ def get_or_create_headers(): _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS ] = parsed_url.hostname if parsed_url.port: - _set_http_port( + _set_http_peer_port_client( metric_labels, parsed_url.port, sem_conv_opt_in_mode ) if _report_new(sem_conv_opt_in_mode): - _set_http_port( + _set_http_peer_port_client( span_attributes, parsed_url.port, sem_conv_opt_in_mode ) # Use semconv library when available diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 50d4f03dff..77a9984528 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -213,6 +213,19 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he from timeit import default_timer from opentelemetry import context, trace +from opentelemetry.instrumentation._semconv import ( + _OpenTelemetryStabilityMode, + _report_old, + _set_http_flavor_version, + _set_http_method, + _set_http_net_host, + _set_http_net_host_port, + _set_http_peer_ip, + _set_http_peer_port_client_server, + _set_http_scheme, + _set_http_target, + _set_http_user_agent, +) from opentelemetry.instrumentation.utils import ( _start_internal_or_server_span, http_status_to_status_code, @@ -296,53 +309,81 @@ def setifnotnone(dic, key, value): dic[key] = value -def collect_request_attributes(environ): +def collect_request_attributes( + environ, + sem_conv_opt_in_mode: _OpenTelemetryStabilityMode = _OpenTelemetryStabilityMode.DEFAULT + ): """Collects HTTP request attributes from the PEP3333-conforming WSGI environ and returns a dictionary to be used as span creation attributes. """ - - result = { - SpanAttributes.HTTP_METHOD: sanitize_method( - environ.get("REQUEST_METHOD") + result = {} + _set_http_method( + result, + environ.get("REQUEST_METHOD", ""), + sanitize_method( + environ.get("REQUEST_METHOD", "") ), - SpanAttributes.HTTP_SERVER_NAME: environ.get("SERVER_NAME"), - SpanAttributes.HTTP_SCHEME: environ.get("wsgi.url_scheme"), - } + sem_conv_opt_in_mode, + ) + # old semconv v1.12.0 + server_name = environ.get("SERVER_NAME") + if _report_old(): + result[SpanAttributes.HTTP_SERVER_NAME] = server_name + + _set_http_scheme( + result, + environ.get("wsgi.url_scheme"), + sem_conv_opt_in_mode, + ) + host = environ.get("HTTP_HOST") host_port = environ.get("SERVER_PORT") - if host_port is not None and not host_port == "": - result.update({SpanAttributes.NET_HOST_PORT: int(host_port)}) + if host: + _set_http_net_host(result, host, sem_conv_opt_in_mode) + # old semconv v1.12.0 + if _report_old(): + result[SpanAttributes.HTTP_HOST] = host + if host_port: + _set_http_net_host_port( + result, + int(host_port), + sem_conv_opt_in_mode, + ) + - setifnotnone(result, SpanAttributes.HTTP_HOST, environ.get("HTTP_HOST")) target = environ.get("RAW_URI") if target is None: # Note: `"" or None is None` target = environ.get("REQUEST_URI") if target is not None: - result[SpanAttributes.HTTP_TARGET] = target + _set_http_target(result, target, sem_conv_opt_in_mode) else: - result[SpanAttributes.HTTP_URL] = remove_url_credentials( - wsgiref_util.request_uri(environ) - ) + # old semconv v1.20.0 + if _report_old(): + result[SpanAttributes.HTTP_URL] = remove_url_credentials( + wsgiref_util.request_uri(environ) + ) remote_addr = environ.get("REMOTE_ADDR") if remote_addr: - result[SpanAttributes.NET_PEER_IP] = remote_addr + _set_http_peer_ip(result, target, sem_conv_opt_in_mode) + + peer_port = environ.get("REMOTE_PORT") + if peer_port: + _set_http_peer_port_client_server(result, peer_port, sem_conv_opt_in_mode) + remote_host = environ.get("REMOTE_HOST") if remote_host and remote_host != remote_addr: result[SpanAttributes.NET_PEER_NAME] = remote_host user_agent = environ.get("HTTP_USER_AGENT") if user_agent is not None and len(user_agent) > 0: - result[SpanAttributes.HTTP_USER_AGENT] = user_agent + _set_http_user_agent(result, user_agent, sem_conv_opt_in_mode) - setifnotnone( - result, SpanAttributes.NET_PEER_PORT, environ.get("REMOTE_PORT") - ) flavor = environ.get("SERVER_PROTOCOL", "") if flavor.upper().startswith(_HTTP_VERSION_PREFIX): flavor = flavor[len(_HTTP_VERSION_PREFIX) :] if flavor: - result[SpanAttributes.HTTP_FLAVOR] = flavor + _set_http_flavor_version(result, flavor, sem_conv_opt_in_mode) return result diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index fbfc92cf21..4ca818faea 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -17,6 +17,7 @@ from enum import Enum from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.util.http import _parse_url_query # TODO: will come through semconv package once updated _SPAN_ATTRIBUTES_ERROR_TYPE = "error.type" @@ -90,6 +91,15 @@ def _set_http_method(result, original, normalized, sem_conv_opt_in_mode): ) +def _set_http_status_code(result, code, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_int_attribute(result, SpanAttributes.HTTP_STATUS_CODE, code) + if _report_new(sem_conv_opt_in_mode): + set_int_attribute( + result, SpanAttributes.HTTP_RESPONSE_STATUS_CODE, code + ) + + def _set_http_url(result, url, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_URL, url) @@ -100,17 +110,17 @@ def _set_http_url(result, url, sem_conv_opt_in_mode): def _set_http_scheme(result, scheme, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_SCHEME, scheme) - # TODO: Support opt-in for scheme in new semconv - # if _report_new(sem_conv_opt_in_mode): - # set_string_attribute(result, SpanAttributes.URL_SCHEME, scheme) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.URL_SCHEME, scheme) -def _set_http_hostname(result, hostname, sem_conv_opt_in_mode): +def _set_http_host(result, host, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): - set_string_attribute(result, SpanAttributes.HTTP_HOST, hostname) + set_string_attribute(result, SpanAttributes.HTTP_HOST, host) if _report_new(sem_conv_opt_in_mode): - set_string_attribute(result, SpanAttributes.SERVER_ADDRESS, hostname) + set_string_attribute(result, SpanAttributes.SERVER_ADDRESS, host) +# Client def _set_http_net_peer_name(result, peer_name, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): @@ -119,29 +129,85 @@ def _set_http_net_peer_name(result, peer_name, sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.SERVER_ADDRESS, peer_name) -def _set_http_port(result, port, sem_conv_opt_in_mode): +def _set_http_peer_port_client(result, port, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_int_attribute(result, SpanAttributes.NET_PEER_PORT, port) if _report_new(sem_conv_opt_in_mode): set_int_attribute(result, SpanAttributes.SERVER_PORT, port) -def _set_http_status_code(result, code, sem_conv_opt_in_mode): +def _set_http_network_protocol_version(result, version, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): - set_int_attribute(result, SpanAttributes.HTTP_STATUS_CODE, code) + set_string_attribute(result, SpanAttributes.HTTP_FLAVOR, version) if _report_new(sem_conv_opt_in_mode): - set_int_attribute( - result, SpanAttributes.HTTP_RESPONSE_STATUS_CODE, code + set_string_attribute( + result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version ) +# Server -def _set_http_network_protocol_version(result, version, sem_conv_opt_in_mode): +def _set_http_net_host(result, host, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.NET_HOST_NAME, host) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.SERVER_ADDRESS, host) + + +def _set_http_net_host_port(result, port, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_int_attribute(result, SpanAttributes.NET_HOST_PORT, port) + if _report_new(sem_conv_opt_in_mode): + set_int_attribute(result, SpanAttributes.SERVER_PORT, port) + + +def _set_http_target(result, target, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.HTTP_TARGET, target) + if _report_new(sem_conv_opt_in_mode): + path, query = _parse_url_query(target) + if path: + set_string_attribute( + result, SpanAttributes.URL_PATH, path + ) + if query: + set_string_attribute( + result, SpanAttributes.URL_QUERY, query + ) + + +def _set_http_peer_ip(result, ip, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.NET_PEER_IP, ip) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.CLIENT_ADDRESS, ip) + + +def _set_http_peer_port_client_server(result, port, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_int_attribute(result, SpanAttributes.NET_PEER_PORT, port) + if _report_new(sem_conv_opt_in_mode): + set_int_attribute(result, SpanAttributes.CLIENT_PORT, port) + + +def _set_http_user_agent(result, user_agent, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.HTTP_USER_AGENT, user_agent) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.USER_AGENT_ORIGINAL, user_agent) + + +def _set_http_net_peer_name(result, name, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.NET_PEER_NAME, name) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.CLIENT_ADDRESS, name) + + +def _set_http_flavor_version(result, version, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_FLAVOR, version) if _report_new(sem_conv_opt_in_mode): - set_string_attribute( - result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version - ) + set_string_attribute(result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version) _OTEL_SEMCONV_STABILITY_OPT_IN_KEY = "OTEL_SEMCONV_STABILITY_OPT_IN" diff --git a/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py b/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py index 523f9400b1..1f7ce98937 100644 --- a/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py +++ b/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py @@ -218,7 +218,7 @@ def sanitize_method(method: Optional[str]) -> Optional[str]: ] ): return method - return "UNKNOWN" + return "_OTHER" def get_custom_headers(env_var: str) -> list[str]: @@ -245,3 +245,10 @@ def _parse_duration_attrs(req_attrs): for key in _duration_attrs.intersection(req_attrs.keys()) } return duration_attrs + + +def _parse_url_query(url: str): + parsed_url = urlparse(url) + path = parsed_url.path + query_params = parsed_url.query + return path, query_params From 9bb6f8ec20f7308975b61d83494c390a0ab27528 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Wed, 10 Apr 2024 12:08:51 -0700 Subject: [PATCH 03/42] wsgi metrics --- .../instrumentation/requests/__init__.py | 18 +- .../instrumentation/wsgi/__init__.py | 82 ++++--- .../opentelemetry/instrumentation/_semconv.py | 200 +++++++++++------- 3 files changed, 198 insertions(+), 102 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index cd8ebfe7be..96d7a5f928 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -63,7 +63,9 @@ _SPAN_ATTRIBUTES_ERROR_TYPE, _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS, _SPAN_ATTRIBUTES_NETWORK_PEER_PORT, - _filter_duration_attrs, + _client_duration_attrs_new, + _client_duration_attrs_old, + _filter_semconv_duration_attrs, _get_schema_url, _OpenTelemetrySemanticConventionStability, _OpenTelemetryStabilityMode, @@ -284,16 +286,22 @@ def get_or_create_headers(): ).__qualname__ if duration_histogram_old is not None: - duration_attrs_old = _filter_duration_attrs( - metric_labels, _OpenTelemetryStabilityMode.DEFAULT + duration_attrs_old = _filter_semconv_duration_attrs( + metric_labels, + _client_duration_attrs_old, + _client_duration_attrs_new, + _OpenTelemetryStabilityMode.DEFAULT ) duration_histogram_old.record( max(round(elapsed_time * 1000), 0), attributes=duration_attrs_old, ) if duration_histogram_new is not None: - duration_attrs_new = _filter_duration_attrs( - metric_labels, _OpenTelemetryStabilityMode.HTTP + duration_attrs_new = _filter_semconv_duration_attrs( + metric_labels, + _client_duration_attrs_old, + _client_duration_attrs_new, + _OpenTelemetryStabilityMode.HTTP ) duration_histogram_new.record( elapsed_time, attributes=duration_attrs_new diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 77a9984528..386e157e11 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -214,8 +214,18 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he from opentelemetry import context, trace from opentelemetry.instrumentation._semconv import ( + _METRIC_ATTRIBUTES_SERVER_DURATION_NAME, _OpenTelemetryStabilityMode, + _SPAN_ATTRIBUTES_ERROR_TYPE, + _get_schema_url, + _filter_semconv_active_request_count_attr, + _filter_semconv_duration_attrs, _report_old, + _report_new, + _server_active_requests_count_attrs_new, + _server_active_requests_count_attrs_old, + _server_duration_attrs_new, + _server_duration_attrs_old, _set_http_flavor_version, _set_http_method, _set_http_net_host, @@ -311,7 +321,7 @@ def setifnotnone(dic, key, value): def collect_request_attributes( environ, - sem_conv_opt_in_mode: _OpenTelemetryStabilityMode = _OpenTelemetryStabilityMode.DEFAULT + sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, ): """Collects HTTP request attributes from the PEP3333-conforming WSGI environ and returns a dictionary to be used as span creation attributes. @@ -327,7 +337,7 @@ def collect_request_attributes( ) # old semconv v1.12.0 server_name = environ.get("SERVER_NAME") - if _report_old(): + if _report_old(sem_conv_opt_in_mode): result[SpanAttributes.HTTP_SERVER_NAME] = server_name _set_http_scheme( @@ -341,7 +351,7 @@ def collect_request_attributes( if host: _set_http_net_host(result, host, sem_conv_opt_in_mode) # old semconv v1.12.0 - if _report_old(): + if _report_old(sem_conv_opt_in_mode): result[SpanAttributes.HTTP_HOST] = host if host_port: _set_http_net_host_port( @@ -358,7 +368,7 @@ def collect_request_attributes( _set_http_target(result, target, sem_conv_opt_in_mode) else: # old semconv v1.20.0 - if _report_old(): + if _report_old(sem_conv_opt_in_mode): result[SpanAttributes.HTTP_URL] = remove_url_credentials( wsgiref_util.request_uri(environ) ) @@ -451,24 +461,30 @@ def _parse_status_code(resp_status): return None -def _parse_active_request_count_attrs(req_attrs): - active_requests_count_attrs = {} - for attr_key in _active_requests_count_attrs: - if req_attrs.get(attr_key) is not None: - active_requests_count_attrs[attr_key] = req_attrs[attr_key] - return active_requests_count_attrs +def _parse_active_request_count_attrs(req_attrs, sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT): + return _filter_semconv_active_request_count_attr( + req_attrs, + _server_active_requests_count_attrs_old, + _server_active_requests_count_attrs_new, + sem_conv_opt_in_mode, + ) -def _parse_duration_attrs(req_attrs): - duration_attrs = {} - for attr_key in _duration_attrs: - if req_attrs.get(attr_key) is not None: - duration_attrs[attr_key] = req_attrs[attr_key] - return duration_attrs +def _parse_duration_attrs(req_attrs, sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT): + return _filter_semconv_duration_attrs( + req_attrs, + _server_duration_attrs_old, + _server_duration_attrs_new, + sem_conv_opt_in_mode, + ) def add_response_attributes( - span, start_response_status, response_headers + span, + start_response_status, + response_headers, + sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, + duration_attrs = None, ): # pylint: disable=unused-argument """Adds HTTP response attributes to span using the arguments passed to a PEP3333-conforming start_response callable. @@ -505,6 +521,8 @@ def get_default_span_name(environ): The span name. """ method = sanitize_method(environ.get("REQUEST_METHOD", "").strip()) + if method == "_OTHER": + return "HTTP" path = environ.get("PATH_INFO", "").strip() if method and path: return f"{method} {path}" @@ -535,29 +553,41 @@ def __init__( response_hook=None, tracer_provider=None, meter_provider=None, + sem_conv_opt_in_mode: _OpenTelemetryStabilityMode = _OpenTelemetryStabilityMode.DEFAULT, ): self.wsgi = wsgi self.tracer = trace.get_tracer( __name__, __version__, tracer_provider, - schema_url="https://opentelemetry.io/schemas/1.11.0", + schema_url=_get_schema_url(sem_conv_opt_in_mode), ) self.meter = get_meter( __name__, __version__, meter_provider, - schema_url="https://opentelemetry.io/schemas/1.11.0", - ) - self.duration_histogram = self.meter.create_histogram( - name=MetricInstruments.HTTP_SERVER_DURATION, - unit="ms", - description="Duration of HTTP client requests.", + schema_url=_get_schema_url(sem_conv_opt_in_mode), ) + self.duration_histogram_old = None + if _report_old(sem_conv_opt_in_mode): + self.duration_histogram_old = self.meter.create_histogram( + name=MetricInstruments.HTTP_SERVER_DURATION, + unit="ms", + description="measures the duration of the inbound HTTP request", + ) + self.duration_histogram_new = None + if _report_new(sem_conv_opt_in_mode): + self.duration_histogram_new = self.meter.create_histogram( + name=_METRIC_ATTRIBUTES_SERVER_DURATION_NAME, + unit="s", + description="measures the duration of the inbound HTTP request", + ) + # We don't need a separate active request counter for old/new semantic conventions + # because the new attributes are a subset of the old attributes self.active_requests_counter = self.meter.create_up_down_counter( name=MetricInstruments.HTTP_SERVER_ACTIVE_REQUESTS, - unit="requests", - description="measures the number of concurrent HTTP requests that are currently in-flight", + unit="{request}", + description="Number of active HTTP server requests.", ) self.request_hook = request_hook self.response_hook = response_hook diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index 4ca818faea..0441319be1 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -24,6 +24,7 @@ _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS = "network.peer.address" _SPAN_ATTRIBUTES_NETWORK_PEER_PORT = "network.peer.port" _METRIC_ATTRIBUTES_CLIENT_DURATION_NAME = "http.client.request.duration" +_METRIC_ATTRIBUTES_SERVER_DURATION_NAME = "http.server.request.duration" _client_duration_attrs_old = [ SpanAttributes.HTTP_STATUS_CODE, @@ -42,17 +43,122 @@ SpanAttributes.NETWORK_PROTOCOL_VERSION, SpanAttributes.SERVER_ADDRESS, SpanAttributes.SERVER_PORT, - # TODO: Support opt-in for scheme in new semconv - # SpanAttributes.URL_SCHEME, + SpanAttributes.URL_SCHEME, ] +_server_duration_attrs_old = [ + SpanAttributes.HTTP_METHOD, + SpanAttributes.HTTP_HOST, + SpanAttributes.HTTP_SCHEME, + SpanAttributes.HTTP_STATUS_CODE, + SpanAttributes.HTTP_FLAVOR, + SpanAttributes.HTTP_SERVER_NAME, + SpanAttributes.NET_HOST_NAME, + SpanAttributes.NET_HOST_PORT, +] + +_server_duration_attrs_new = [ + _SPAN_ATTRIBUTES_ERROR_TYPE, + SpanAttributes.HTTP_REQUEST_METHOD, + SpanAttributes.HTTP_RESPONSE_STATUS_CODE, + SpanAttributes.HTTP_ROUTE, + SpanAttributes.NETWORK_PROTOCOL_VERSION, + SpanAttributes.URL_SCHEME, +] + +_server_active_requests_count_attrs_old = [ + SpanAttributes.HTTP_METHOD, + SpanAttributes.HTTP_HOST, + SpanAttributes.HTTP_SCHEME, + SpanAttributes.HTTP_FLAVOR, + SpanAttributes.HTTP_SERVER_NAME, + SpanAttributes.NET_HOST_NAME, + SpanAttributes.NET_HOST_PORT, +] + +_server_active_requests_count_attrs_new = [ + SpanAttributes.HTTP_REQUEST_METHOD, + SpanAttributes.URL_SCHEME, +] + +_OTEL_SEMCONV_STABILITY_OPT_IN_KEY = "OTEL_SEMCONV_STABILITY_OPT_IN" + + +class _OpenTelemetryStabilitySignalType: + HTTP = "http" + + +class _OpenTelemetryStabilityMode(Enum): + # http - emit the new, stable HTTP and networking conventions ONLY + HTTP = "http" + # http/dup - emit both the old and the stable HTTP and networking conventions + HTTP_DUP = "http/dup" + # default - continue emitting old experimental HTTP and networking conventions + DEFAULT = "default" + + +def _report_new(mode): + return mode.name != _OpenTelemetryStabilityMode.DEFAULT.name -def _filter_duration_attrs(attrs, sem_conv_opt_in_mode): + +def _report_old(mode): + return mode.name != _OpenTelemetryStabilityMode.HTTP.name + + +class _OpenTelemetrySemanticConventionStability: + _initialized = False + _lock = threading.Lock() + _OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING = {} + + @classmethod + def _initialize(cls): + with _OpenTelemetrySemanticConventionStability._lock: + if not _OpenTelemetrySemanticConventionStability._initialized: + # Users can pass in comma delimited string for opt-in options + # Only values for http stability are supported for now + opt_in = os.environ.get(_OTEL_SEMCONV_STABILITY_OPT_IN_KEY, "") + opt_in_list = [] + if opt_in: + opt_in_list = [s.strip() for s in opt_in.split(",")] + http_opt_in = _OpenTelemetryStabilityMode.DEFAULT + if opt_in_list: + # Process http opt-in + # http/dup takes priority over http + if ( + _OpenTelemetryStabilityMode.HTTP_DUP.value + in opt_in_list + ): + http_opt_in = _OpenTelemetryStabilityMode.HTTP_DUP + elif _OpenTelemetryStabilityMode.HTTP.value in opt_in_list: + http_opt_in = _OpenTelemetryStabilityMode.HTTP + _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING[ + _OpenTelemetryStabilitySignalType.HTTP + ] = http_opt_in + _OpenTelemetrySemanticConventionStability._initialized = True + + @classmethod + # Get OpenTelemetry opt-in mode based off of signal type (http, messaging, etc.) + def _get_opentelemetry_stability_opt_in_mode( + cls, + signal_type: _OpenTelemetryStabilitySignalType, + ) -> _OpenTelemetryStabilityMode: + return _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING.get( + signal_type, _OpenTelemetryStabilityMode.DEFAULT + ) + + +def _filter_semconv_duration_attrs( + attrs, + old_attrs, + new_attrs, + sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, +): filtered_attrs = {} + # duration is two different metrics depending on sem_conv_opt_in_mode, so no DUP attributes allowed_attributes = ( - _client_duration_attrs_new + new_attrs if sem_conv_opt_in_mode == _OpenTelemetryStabilityMode.HTTP - else _client_duration_attrs_old + else old_attrs ) for key, val in attrs.items(): if key in allowed_attributes: @@ -60,6 +166,24 @@ def _filter_duration_attrs(attrs, sem_conv_opt_in_mode): return filtered_attrs +def _filter_semconv_active_request_count_attr( + attrs, + old_attrs, + new_attrs, + sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, +): + filtered_attrs = {} + if _report_old(sem_conv_opt_in_mode): + for key, val in attrs.items(): + if key in old_attrs: + filtered_attrs[key] = val + if _report_new(sem_conv_opt_in_mode): + for key, val in attrs.items(): + if key in new_attrs: + filtered_attrs[key] = val + return filtered_attrs + + def set_string_attribute(result, key, value): if value: result[key] = value @@ -210,72 +334,6 @@ def _set_http_flavor_version(result, version, sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version) -_OTEL_SEMCONV_STABILITY_OPT_IN_KEY = "OTEL_SEMCONV_STABILITY_OPT_IN" - - -class _OpenTelemetryStabilitySignalType: - HTTP = "http" - - -class _OpenTelemetryStabilityMode(Enum): - # http - emit the new, stable HTTP and networking conventions ONLY - HTTP = "http" - # http/dup - emit both the old and the stable HTTP and networking conventions - HTTP_DUP = "http/dup" - # default - continue emitting old experimental HTTP and networking conventions - DEFAULT = "default" - - -def _report_new(mode): - return mode.name != _OpenTelemetryStabilityMode.DEFAULT.name - - -def _report_old(mode): - return mode.name != _OpenTelemetryStabilityMode.HTTP.name - - -class _OpenTelemetrySemanticConventionStability: - _initialized = False - _lock = threading.Lock() - _OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING = {} - - @classmethod - def _initialize(cls): - with _OpenTelemetrySemanticConventionStability._lock: - if not _OpenTelemetrySemanticConventionStability._initialized: - # Users can pass in comma delimited string for opt-in options - # Only values for http stability are supported for now - opt_in = os.environ.get(_OTEL_SEMCONV_STABILITY_OPT_IN_KEY, "") - opt_in_list = [] - if opt_in: - opt_in_list = [s.strip() for s in opt_in.split(",")] - http_opt_in = _OpenTelemetryStabilityMode.DEFAULT - if opt_in_list: - # Process http opt-in - # http/dup takes priority over http - if ( - _OpenTelemetryStabilityMode.HTTP_DUP.value - in opt_in_list - ): - http_opt_in = _OpenTelemetryStabilityMode.HTTP_DUP - elif _OpenTelemetryStabilityMode.HTTP.value in opt_in_list: - http_opt_in = _OpenTelemetryStabilityMode.HTTP - _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING[ - _OpenTelemetryStabilitySignalType.HTTP - ] = http_opt_in - _OpenTelemetrySemanticConventionStability._initialized = True - - @classmethod - # Get OpenTelemetry opt-in mode based off of signal type (http, messaging, etc.) - def _get_opentelemetry_stability_opt_in_mode( - cls, - signal_type: _OpenTelemetryStabilitySignalType, - ) -> _OpenTelemetryStabilityMode: - return _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING.get( - signal_type, _OpenTelemetryStabilityMode.DEFAULT - ) - - # Get schema version based off of opt-in mode def _get_schema_url(mode: _OpenTelemetryStabilityMode) -> str: if mode is _OpenTelemetryStabilityMode.DEFAULT: From 01d2f5bcadda931437abaa076054dc7fd25b4c0e Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Wed, 10 Apr 2024 16:24:40 -0700 Subject: [PATCH 04/42] wsgi resp --- .../instrumentation/requests/__init__.py | 4 +- .../instrumentation/wsgi/__init__.py | 64 ++++++++++--------- .../opentelemetry/instrumentation/_semconv.py | 37 ++++++++++- 3 files changed, 70 insertions(+), 35 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index 96d7a5f928..54b718f678 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -74,7 +74,7 @@ _report_old, _set_http_host, _set_http_method, - _set_http_net_peer_name, + _set_http_net_peer_name_client, _set_http_network_protocol_version, _set_http_peer_port_client, _set_http_scheme, @@ -179,7 +179,7 @@ def get_or_create_headers(): _set_http_host( metric_labels, parsed_url.hostname, sem_conv_opt_in_mode ) - _set_http_net_peer_name( + _set_http_net_peer_name_client( metric_labels, parsed_url.hostname, sem_conv_opt_in_mode ) if _report_new(sem_conv_opt_in_mode): diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 386e157e11..5e55fb40d1 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -230,15 +230,16 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he _set_http_method, _set_http_net_host, _set_http_net_host_port, + _set_http_net_peer_name_server, _set_http_peer_ip, - _set_http_peer_port_client_server, + _set_http_peer_port_server, _set_http_scheme, _set_http_target, _set_http_user_agent, + _set_status, ) from opentelemetry.instrumentation.utils import ( _start_internal_or_server_span, - http_status_to_status_code, ) from opentelemetry.instrumentation.wsgi.version import __version__ from opentelemetry.metrics import get_meter @@ -379,11 +380,11 @@ def collect_request_attributes( peer_port = environ.get("REMOTE_PORT") if peer_port: - _set_http_peer_port_client_server(result, peer_port, sem_conv_opt_in_mode) + _set_http_peer_port_server(result, peer_port, sem_conv_opt_in_mode) remote_host = environ.get("REMOTE_HOST") if remote_host and remote_host != remote_addr: - result[SpanAttributes.NET_PEER_NAME] = remote_host + _set_http_net_peer_name_server(result, remote_host, sem_conv_opt_in_mode) user_agent = environ.get("HTTP_USER_AGENT") if user_agent is not None and len(user_agent) > 0: @@ -491,23 +492,17 @@ def add_response_attributes( """ if not span.is_recording(): return - status_code, _ = start_response_status.split(" ", 1) + status_code_str, _ = start_response_status.split(" ", 1) + status_code = 0 try: - status_code = int(status_code) + status_code = int(status_code_str) 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)) - ) - + status_code = -1 + if duration_attrs is None: + duration_attrs = {} + _set_status(span, duration_attrs, status_code_str, status_code, sem_conv_opt_in_mode) + def get_default_span_name(environ): """ @@ -591,17 +586,15 @@ def __init__( ) self.request_hook = request_hook self.response_hook = response_hook + self._sem_conv_opt_in_mode = sem_conv_opt_in_mode @staticmethod def _create_start_response( - span, start_response, response_hook, duration_attrs + span, start_response, response_hook, duration_attrs, sem_conv_opt_in_mode, ): @functools.wraps(start_response) def _start_response(status, response_headers, *args, **kwargs): - add_response_attributes(span, status, response_headers) - status_code = _parse_status_code(status) - if status_code is not None: - duration_attrs[SpanAttributes.HTTP_STATUS_CODE] = status_code + add_response_attributes(span, status, response_headers, duration_attrs, sem_conv_opt_in_mode) if span.is_recording() and span.kind == trace.SpanKind.SERVER: custom_attributes = collect_custom_response_headers_attributes( response_headers @@ -622,11 +615,11 @@ def __call__(self, environ, start_response): environ: A WSGI environment. start_response: The WSGI start_response callable. """ - req_attrs = collect_request_attributes(environ) + req_attrs = collect_request_attributes(environ, self._sem_conv_opt_in_mode) active_requests_count_attrs = _parse_active_request_count_attrs( - req_attrs + req_attrs, + self._sem_conv_opt_in_mode, ) - duration_attrs = _parse_duration_attrs(req_attrs) span, token = _start_internal_or_server_span( tracer=self.tracer, @@ -655,20 +648,31 @@ def __call__(self, environ, start_response): try: with trace.use_span(span): start_response = self._create_start_response( - span, start_response, response_hook, duration_attrs + span, + start_response, + response_hook, + req_attrs, + self._sem_conv_opt_in_mode, ) iterable = self.wsgi(environ, start_response) return _end_span_after_iterating(iterable, span, token) except Exception as ex: if span.is_recording(): - span.set_status(Status(StatusCode.ERROR, str(ex))) + if _report_new(self._sem_conv_opt_in_mode): + span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, type(ex).__qualname__ ) + span.set_status(Status(StatusCode.ERROR, str(ex))) span.end() if token is not None: context.detach(token) raise finally: - duration = max(round((default_timer() - start) * 1000), 0) - self.duration_histogram.record(duration, duration_attrs) + duration_s = default_timer() - start + if self.duration_histogram_old: + duration_attrs_old = _parse_duration_attrs(req_attrs, _OpenTelemetryStabilityMode.DEFAULT) + self.duration_histogram_old.record(max(round(duration_s * 1000), 0), duration_attrs_old) + if self.duration_histogram_new: + duration_attrs_new = _parse_duration_attrs(req_attrs, _OpenTelemetryStabilityMode.HTTP) + self.duration_histogram_new.record(max(round(duration_s * 1000), 0), duration_attrs_new) self.active_requests_counter.add(-1, active_requests_count_attrs) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index 0441319be1..c6f6ce25cf 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -16,6 +16,10 @@ import threading from enum import Enum +from opentelemetry.instrumentation.utils import ( + http_status_to_status_code, +) +from opentelemetry.trace.status import Status, StatusCode from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.util.http import _parse_url_query @@ -246,7 +250,7 @@ def _set_http_host(result, host, sem_conv_opt_in_mode): # Client -def _set_http_net_peer_name(result, peer_name, sem_conv_opt_in_mode): +def _set_http_net_peer_name_client(result, peer_name, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.NET_PEER_NAME, peer_name) if _report_new(sem_conv_opt_in_mode): @@ -306,7 +310,7 @@ def _set_http_peer_ip(result, ip, sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.CLIENT_ADDRESS, ip) -def _set_http_peer_port_client_server(result, port, sem_conv_opt_in_mode): +def _set_http_peer_port_server(result, port, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_int_attribute(result, SpanAttributes.NET_PEER_PORT, port) if _report_new(sem_conv_opt_in_mode): @@ -320,7 +324,7 @@ def _set_http_user_agent(result, user_agent, sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.USER_AGENT_ORIGINAL, user_agent) -def _set_http_net_peer_name(result, name, sem_conv_opt_in_mode): +def _set_http_net_peer_name_server(result, name, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.NET_PEER_NAME, name) if _report_new(sem_conv_opt_in_mode): @@ -334,6 +338,33 @@ def _set_http_flavor_version(result, version, sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version) +def _set_status(span, metrics_attributes, status_code_str, status_code, sem_conv_opt_in_mode): + if (status_code < 0): + if _report_new(sem_conv_opt_in_mode): + span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, status_code_str) + metrics_attributes[_SPAN_ATTRIBUTES_ERROR_TYPE] = status_code_str + + span.set_status( + Status( + StatusCode.ERROR, + "Non-integer HTTP status: " + status_code_str, + ) + ) + else: + status = http_status_to_status_code(status_code, server_span=True) + + if _report_old(sem_conv_opt_in_mode): + span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code) + metrics_attributes[SpanAttributes.HTTP_STATUS_CODE] = status_code + if _report_new(sem_conv_opt_in_mode): + span.set_attribute(SpanAttributes.HTTP_RESPONSE_STATUS_CODE, status_code) + metrics_attributes[SpanAttributes.HTTP_RESPONSE_STATUS_CODE] = status_code + if status == StatusCode.ERROR: + span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, status_code_str) + metrics_attributes[_SPAN_ATTRIBUTES_ERROR_TYPE] = status_code_str + span.set_status(Status(status)) + + # Get schema version based off of opt-in mode def _get_schema_url(mode: _OpenTelemetryStabilityMode) -> str: if mode is _OpenTelemetryStabilityMode.DEFAULT: From c453f077eb22f5842c74226ac291a7a073a235e5 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 11 Apr 2024 13:03:54 -0700 Subject: [PATCH 05/42] fix wsgi tests --- .../tests/test_falcon.py | 10 ++--- .../instrumentation/wsgi/__init__.py | 39 +++++-------------- .../tests/test_wsgi_middleware.py | 28 ++++++++----- 3 files changed, 33 insertions(+), 44 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 2245dbfd80..74cd77c8c4 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -27,9 +27,9 @@ get_global_response_propagator, set_global_response_propagator, ) -from opentelemetry.instrumentation.wsgi import ( - _active_requests_count_attrs, - _duration_attrs, +from opentelemetry.instrumentation._semconv import ( + _server_active_requests_count_attrs_old, + _server_duration_attrs_old ) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, @@ -53,8 +53,8 @@ "http.server.duration", ] _recommended_attrs = { - "http.server.active_requests": _active_requests_count_attrs, - "http.server.duration": _duration_attrs, + "http.server.active_requests": _server_active_requests_count_attrs_old, + "http.server.duration": _server_duration_attrs_old, } diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 5e55fb40d1..7e789871ea 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -263,26 +263,6 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he _CARRIER_KEY_PREFIX = "HTTP_" _CARRIER_KEY_PREFIX_LEN = len(_CARRIER_KEY_PREFIX) -# List of recommended attributes -_duration_attrs = [ - SpanAttributes.HTTP_METHOD, - SpanAttributes.HTTP_HOST, - SpanAttributes.HTTP_SCHEME, - SpanAttributes.HTTP_STATUS_CODE, - SpanAttributes.HTTP_FLAVOR, - SpanAttributes.HTTP_SERVER_NAME, - SpanAttributes.NET_HOST_NAME, - SpanAttributes.NET_HOST_PORT, -] - -_active_requests_count_attrs = [ - SpanAttributes.HTTP_METHOD, - SpanAttributes.HTTP_HOST, - SpanAttributes.HTTP_SCHEME, - SpanAttributes.HTTP_FLAVOR, - SpanAttributes.HTTP_SERVER_NAME, -] - class WSGIGetter(Getter[dict]): def get( @@ -354,18 +334,18 @@ def collect_request_attributes( # old semconv v1.12.0 if _report_old(sem_conv_opt_in_mode): result[SpanAttributes.HTTP_HOST] = host - if host_port: - _set_http_net_host_port( - result, - int(host_port), - sem_conv_opt_in_mode, - ) + if host_port: + _set_http_net_host_port( + result, + int(host_port), + sem_conv_opt_in_mode, + ) target = environ.get("RAW_URI") if target is None: # Note: `"" or None is None` target = environ.get("REQUEST_URI") - if target is not None: + if target: _set_http_target(result, target, sem_conv_opt_in_mode) else: # old semconv v1.20.0 @@ -483,9 +463,8 @@ def _parse_duration_attrs(req_attrs, sem_conv_opt_in_mode = _OpenTelemetryStabil def add_response_attributes( span, start_response_status, - response_headers, - sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, duration_attrs = None, + sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, ): # pylint: disable=unused-argument """Adds HTTP response attributes to span using the arguments passed to a PEP3333-conforming start_response callable. @@ -594,7 +573,7 @@ def _create_start_response( ): @functools.wraps(start_response) def _start_response(status, response_headers, *args, **kwargs): - add_response_attributes(span, status, response_headers, duration_attrs, sem_conv_opt_in_mode) + add_response_attributes(span, status, duration_attrs, sem_conv_opt_in_mode) if span.is_recording() and span.kind == trace.SpanKind.SERVER: custom_attributes = collect_custom_response_headers_attributes( response_headers diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index f74dd67867..5563e21df9 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -20,6 +20,12 @@ import opentelemetry.instrumentation.wsgi as otel_wsgi from opentelemetry import trace as trace_api +from opentelemetry.instrumentation._semconv import ( + _server_active_requests_count_attrs_new, + _server_active_requests_count_attrs_old, + _server_duration_attrs_new, + _server_duration_attrs_old, +) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, NumberDataPoint, @@ -131,9 +137,13 @@ def wsgi_with_repeat_custom_response_headers(environ, start_response): "http.server.active_requests", "http.server.duration", ] -_recommended_attrs = { - "http.server.active_requests": otel_wsgi._active_requests_count_attrs, - "http.server.duration": otel_wsgi._duration_attrs, +_recommended_metrics_attrs_old = { + "http.server.active_requests": _server_active_requests_count_attrs_old, + "http.server.duration": _server_duration_attrs_old, +} +_recommended_metrics_attrs_new = { + "http.server.active_requests": _server_active_requests_count_attrs_new, + "http.server.duration": _server_duration_attrs_new, } @@ -179,6 +189,7 @@ def validate_response( SpanAttributes.HTTP_FLAVOR: "1.0", SpanAttributes.HTTP_URL: "http://127.0.0.1/", SpanAttributes.HTTP_STATUS_CODE: 200, + SpanAttributes.NET_HOST_NAME: "127.0.0.1", } expected_attributes.update(span_attributes or {}) if http_method is not None: @@ -294,7 +305,7 @@ def test_wsgi_metrics(self): number_data_point_seen = True for attr in point.attributes: self.assertIn( - attr, _recommended_attrs[metric.name] + attr, _recommended_metrics_attrs_old[metric.name] ) self.assertTrue(number_data_point_seen and histogram_data_point_seen) @@ -303,7 +314,7 @@ def test_nonstandard_http_method(self): app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi) response = app(self.environ, self.start_response) self.validate_response( - response, span_name="UNKNOWN /", http_method="UNKNOWN" + response, span_name="HTTP", http_method="_OTHER" ) @mock.patch.dict( @@ -349,6 +360,7 @@ def test_request_attributes(self): SpanAttributes.HTTP_SCHEME: "http", SpanAttributes.HTTP_SERVER_NAME: "127.0.0.1", SpanAttributes.HTTP_FLAVOR: "1.0", + SpanAttributes.NET_HOST_NAME: "127.0.0.1", }, ) @@ -439,10 +451,8 @@ def test_request_attributes_with_faux_scheme_relative_raw_uri(self): def test_request_attributes_pathless(self): self.environ["RAW_URI"] = "" expected = {SpanAttributes.HTTP_TARGET: ""} - self.assertGreaterEqual( - otel_wsgi.collect_request_attributes(self.environ).items(), - expected.items(), - ) + self.assertIsNone(otel_wsgi.collect_request_attributes(self.environ) +.get(SpanAttributes.HTTP_TARGET)) def test_request_attributes_with_full_request_uri(self): self.environ["HTTP_HOST"] = "127.0.0.1:8080" From b93458d9c926f59b919910329a106f9d1fd60714 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 11 Apr 2024 13:43:24 -0700 Subject: [PATCH 06/42] util --- .../src/opentelemetry/instrumentation/wsgi/__init__.py | 4 +++- .../src/opentelemetry/instrumentation/_semconv.py | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 7e789871ea..8696d534ab 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -248,6 +248,7 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.trace.status import Status, StatusCode from opentelemetry.util.http import ( + _parse_url_query, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE, @@ -346,7 +347,8 @@ def collect_request_attributes( if target is None: # Note: `"" or None is None` target = environ.get("REQUEST_URI") if target: - _set_http_target(result, target, sem_conv_opt_in_mode) + path, query = _parse_url_query(target) + _set_http_target(result, target, path, query, sem_conv_opt_in_mode) else: # old semconv v1.20.0 if _report_old(sem_conv_opt_in_mode): diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index c6f6ce25cf..09f4a1c90b 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -21,7 +21,6 @@ ) from opentelemetry.trace.status import Status, StatusCode from opentelemetry.semconv.trace import SpanAttributes -from opentelemetry.util.http import _parse_url_query # TODO: will come through semconv package once updated _SPAN_ATTRIBUTES_ERROR_TYPE = "error.type" @@ -288,11 +287,10 @@ def _set_http_net_host_port(result, port, sem_conv_opt_in_mode): set_int_attribute(result, SpanAttributes.SERVER_PORT, port) -def _set_http_target(result, target, sem_conv_opt_in_mode): +def _set_http_target(result, target, path, query, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_TARGET, target) if _report_new(sem_conv_opt_in_mode): - path, query = _parse_url_query(target) if path: set_string_attribute( result, SpanAttributes.URL_PATH, path From a4f4fc81752fa1ced94f6aab90e9b43477a1c5fe Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 11 Apr 2024 14:12:59 -0700 Subject: [PATCH 07/42] fix tests --- .../tests/test_falcon.py | 11 +++++++---- .../tests/test_programmatic.py | 14 +++++++------- .../tests/test_requests_integration.py | 2 ++ .../opentelemetry/instrumentation/wsgi/__init__.py | 1 + 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 74cd77c8c4..06b27f95fb 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -125,7 +125,7 @@ def _test_method(self, method): SpanAttributes.NET_HOST_PORT: 80, SpanAttributes.HTTP_HOST: "falconframework.org", SpanAttributes.HTTP_TARGET: "/", - SpanAttributes.NET_PEER_PORT: "65133", + SpanAttributes.NET_PEER_PORT: 65133, SpanAttributes.HTTP_FLAVOR: "1.1", "falcon.resource": "HelloWorldResource", SpanAttributes.HTTP_STATUS_CODE: 201, @@ -156,7 +156,7 @@ def test_404(self): SpanAttributes.NET_HOST_PORT: 80, SpanAttributes.HTTP_HOST: "falconframework.org", SpanAttributes.HTTP_TARGET: "/", - SpanAttributes.NET_PEER_PORT: "65133", + SpanAttributes.NET_PEER_PORT: 65133, SpanAttributes.HTTP_FLAVOR: "1.1", SpanAttributes.HTTP_STATUS_CODE: 404, }, @@ -193,7 +193,7 @@ def test_500(self): SpanAttributes.NET_HOST_PORT: 80, SpanAttributes.HTTP_HOST: "falconframework.org", SpanAttributes.HTTP_TARGET: "/", - SpanAttributes.NET_PEER_PORT: "65133", + SpanAttributes.NET_PEER_PORT: 65133, SpanAttributes.HTTP_FLAVOR: "1.1", SpanAttributes.HTTP_STATUS_CODE: 500, }, @@ -226,7 +226,7 @@ def test_url_template(self): SpanAttributes.NET_HOST_PORT: 80, SpanAttributes.HTTP_HOST: "falconframework.org", SpanAttributes.HTTP_TARGET: "/", - SpanAttributes.NET_PEER_PORT: "65133", + SpanAttributes.NET_PEER_PORT: 65133, SpanAttributes.HTTP_FLAVOR: "1.1", "falcon.resource": "UserResource", SpanAttributes.HTTP_STATUS_CODE: 200, @@ -336,6 +336,7 @@ def test_falcon_metric_values(self): "http.flavor": "1.1", "http.server_name": "falconframework.org", "net.host.port": 80, + "net.host.name": "falconframework.org", "http.status_code": 404, } expected_requests_count_attributes = { @@ -344,6 +345,8 @@ def test_falcon_metric_values(self): "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "falconframework.org", + "net.host.name": "falconframework.org", + "net.host.port": 80, } start = default_timer() self.client().simulate_get("/hello/756") diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py index dec265907f..00a37d2339 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py +++ b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py @@ -18,17 +18,17 @@ from flask import Flask, request from opentelemetry import trace +from opentelemetry.instrumentation._semconv import ( + _server_duration_attrs_old, + _server_active_requests_count_attrs_old, +) from opentelemetry.instrumentation.flask import FlaskInstrumentor from opentelemetry.instrumentation.propagators import ( TraceResponsePropagator, get_global_response_propagator, set_global_response_propagator, ) -from opentelemetry.instrumentation.wsgi import ( - OpenTelemetryMiddleware, - _active_requests_count_attrs, - _duration_attrs, -) +from opentelemetry.instrumentation.wsgi import OpenTelemetryMiddleware from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, NumberDataPoint, @@ -69,8 +69,8 @@ def expected_attributes(override_attributes): "http.server.duration", ] _recommended_attrs = { - "http.server.active_requests": _active_requests_count_attrs, - "http.server.duration": _duration_attrs, + "http.server.active_requests": _server_active_requests_count_attrs_old, + "http.server.duration": _server_duration_attrs_old, } diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index 8817053068..81b8995be5 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -730,6 +730,7 @@ def test_basic_metric_new_semconv(self): SpanAttributes.SERVER_PORT: 8000, SpanAttributes.HTTP_REQUEST_METHOD: "GET", SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.1", + SpanAttributes.URL_SCHEME: "http", } for ( resource_metrics @@ -766,6 +767,7 @@ def test_basic_metric_both_semconv(self): SpanAttributes.SERVER_PORT: 8000, SpanAttributes.HTTP_REQUEST_METHOD: "GET", SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.1", + SpanAttributes.URL_SCHEME: "http", } for ( diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 8696d534ab..46f807b706 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -465,6 +465,7 @@ def _parse_duration_attrs(req_attrs, sem_conv_opt_in_mode = _OpenTelemetryStabil def add_response_attributes( span, start_response_status, + response_headers, duration_attrs = None, sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, ): # pylint: disable=unused-argument From fe479782241970e9aea5553cd5942c9c0ac0b25e Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 11 Apr 2024 14:26:15 -0700 Subject: [PATCH 08/42] fix wsgi --- .../src/opentelemetry/instrumentation/wsgi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 46f807b706..bb60fdcbe4 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -576,7 +576,7 @@ def _create_start_response( ): @functools.wraps(start_response) def _start_response(status, response_headers, *args, **kwargs): - add_response_attributes(span, status, duration_attrs, sem_conv_opt_in_mode) + add_response_attributes(span, status, response_headers, duration_attrs, sem_conv_opt_in_mode) if span.is_recording() and span.kind == trace.SpanKind.SERVER: custom_attributes = collect_custom_response_headers_attributes( response_headers From b469c5c469b3529692d0f44f157a0dc7ccad574e Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 11 Apr 2024 14:44:28 -0700 Subject: [PATCH 09/42] pyramid --- .../tests/test_automatic.py | 13 +++++++++---- .../tests/test_programmatic.py | 1 + .../instrumentation/requests/__init__.py | 5 ++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py index 2c3ec85e18..4715e0b461 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py +++ b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py @@ -18,6 +18,10 @@ from pyramid.config import Configurator from opentelemetry import trace +from opentelemetry.instrumentation._semconv import ( + _server_active_requests_count_attrs_old, + _server_duration_attrs_old, +) from opentelemetry.instrumentation.pyramid import PyramidInstrumentor from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, @@ -31,8 +35,6 @@ OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE, - _active_requests_count_attrs, - _duration_attrs, ) # pylint: disable=import-error @@ -43,8 +45,8 @@ "http.server.duration", ] _recommended_attrs = { - "http.server.active_requests": _active_requests_count_attrs, - "http.server.duration": _duration_attrs, + "http.server.active_requests": _server_active_requests_count_attrs_old, + "http.server.duration": _server_duration_attrs_old, } @@ -213,6 +215,7 @@ def test_basic_metric_success(self): "http.server_name": "localhost", "net.host.port": 80, "http.status_code": 200, + "net.host.name": "localhost", } expected_requests_count_attributes = { "http.method": "GET", @@ -220,6 +223,8 @@ def test_basic_metric_success(self): "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "localhost", + "net.host.name": "localhost", + "net.host.port": 80, } metrics_list = self.memory_metrics_reader.get_metrics_data() for metric in ( diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py index 478eab1937..c566c301d8 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py +++ b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py @@ -37,6 +37,7 @@ def expected_attributes(override_attributes): SpanAttributes.HTTP_SERVER_NAME: "localhost", SpanAttributes.HTTP_SCHEME: "http", SpanAttributes.NET_HOST_PORT: 80, + SpanAttributes.NET_HOST_NAME: "localhost", SpanAttributes.HTTP_HOST: "localhost", SpanAttributes.HTTP_TARGET: "/", SpanAttributes.HTTP_FLAVOR: "1.1", diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index 54b718f678..0415c80082 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -349,7 +349,10 @@ def get_default_span_name(method): Returns: span name """ - return sanitize_method(method.upper().strip()) + method = sanitize_method(method.upper().strip()) + if method == "_OTHER": + return "HTTP" + return method class RequestsInstrumentor(BaseInstrumentor): From d63584032d601b11e56ebe15c802299a2e3f8f48 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 11 Apr 2024 14:54:43 -0700 Subject: [PATCH 10/42] flacon --- .../tests/test_falcon.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 06b27f95fb..185bec0072 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -16,7 +16,7 @@ from unittest.mock import Mock, patch import pytest -from falcon import __version__ as _falcon_verison +from falcon import __version__ as _falcon_version from falcon import testing from packaging import version as package_version @@ -135,9 +135,15 @@ def _test_method(self, method): # In falcon>3, NET_PEER_IP is not set to anything by default to # https://github.com/falconry/falcon/blob/5233d0abed977d9dab78ebadf305f5abe2eef07c/falcon/testing/helpers.py#L1168-L1172 # noqa if SpanAttributes.NET_PEER_IP in span.attributes: - self.assertEqual( - span.attributes[SpanAttributes.NET_PEER_IP], "127.0.0.1" - ) + if _falcon_version < 3: + self.assertEqual( + span.attributes[SpanAttributes.NET_PEER_IP], "127.0.0.1" + ) + else: + self.assertEqual( + span.attributes[SpanAttributes.NET_PEER_IP], "/" + ) + self.memory_exporter.clear() def test_404(self): @@ -526,7 +532,7 @@ def test_custom_request_header_not_added_in_internal_span(self): self.assertNotIn(key, span.attributes) @pytest.mark.skipif( - condition=package_version.parse(_falcon_verison) + condition=package_version.parse(_falcon_version) < package_version.parse("2.0.0"), reason="falcon<2 does not implement custom response headers", ) @@ -561,7 +567,7 @@ def test_custom_response_header_added_in_server_span(self): self.assertNotIn(key, span.attributes) @pytest.mark.skipif( - condition=package_version.parse(_falcon_verison) + condition=package_version.parse(_falcon_version) < package_version.parse("2.0.0"), reason="falcon<2 does not implement custom response headers", ) From 715f282e7340a41b38d4628e008f2554a59e5d79 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 11 Apr 2024 15:21:07 -0700 Subject: [PATCH 11/42] fix tests --- .../tests/test_falcon.py | 2 +- .../tests/test_programmatic.py | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 185bec0072..773b060de5 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -135,7 +135,7 @@ def _test_method(self, method): # In falcon>3, NET_PEER_IP is not set to anything by default to # https://github.com/falconry/falcon/blob/5233d0abed977d9dab78ebadf305f5abe2eef07c/falcon/testing/helpers.py#L1168-L1172 # noqa if SpanAttributes.NET_PEER_IP in span.attributes: - if _falcon_version < 3: + if package_version.parse(_falcon_version) < 3: self.assertEqual( span.attributes[SpanAttributes.NET_PEER_IP], "127.0.0.1" ) diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py index 00a37d2339..cb6b849d88 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py +++ b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py @@ -54,6 +54,7 @@ def expected_attributes(override_attributes): SpanAttributes.HTTP_SERVER_NAME: "localhost", SpanAttributes.HTTP_SCHEME: "http", SpanAttributes.NET_HOST_PORT: 80, + SpanAttributes.NET_HOST_NAME: "localhost", SpanAttributes.HTTP_HOST: "localhost", SpanAttributes.HTTP_TARGET: "/", SpanAttributes.HTTP_FLAVOR: "1.1", @@ -358,6 +359,7 @@ def test_basic_metric_success(self): "http.server_name": "localhost", "net.host.port": 80, "http.status_code": 200, + "net.host.name": "localhost", } expected_requests_count_attributes = { "http.method": "GET", @@ -365,6 +367,8 @@ def test_basic_metric_success(self): "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "localhost", + "net.host.name": "localhost", + "net.host.port": 80, } self._assert_basic_metric( expected_duration_attributes, @@ -374,20 +378,23 @@ def test_basic_metric_success(self): def test_basic_metric_nonstandard_http_method_success(self): self.client.open("/hello/756", method="NONSTANDARD") expected_duration_attributes = { - "http.method": "UNKNOWN", + "http.method": "_OTHER", "http.host": "localhost", "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "localhost", "net.host.port": 80, "http.status_code": 405, + "net.host.name": "localhost", } expected_requests_count_attributes = { - "http.method": "UNKNOWN", + "http.method": "_OTHER", "http.host": "localhost", "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "localhost", + "net.host.name": "localhost", + "net.host.port": 80, } self._assert_basic_metric( expected_duration_attributes, @@ -410,6 +417,7 @@ def test_basic_metric_nonstandard_http_method_allowed_success(self): "http.server_name": "localhost", "net.host.port": 80, "http.status_code": 405, + "net.host.name": "localhost", } expected_requests_count_attributes = { "http.method": "NONSTANDARD", @@ -417,6 +425,8 @@ def test_basic_metric_nonstandard_http_method_allowed_success(self): "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "localhost", + "net.host.name": "localhost", + "net.host.port": 80, } self._assert_basic_metric( expected_duration_attributes, From e0e19c3ce50dc36c2ac0dfab241728ce03aa6123 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 11 Apr 2024 15:51:15 -0700 Subject: [PATCH 12/42] falcon --- .../tests/test_falcon.py | 12 +++--------- .../opentelemetry/instrumentation/wsgi/__init__.py | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 773b060de5..0d4511375c 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -135,15 +135,9 @@ def _test_method(self, method): # In falcon>3, NET_PEER_IP is not set to anything by default to # https://github.com/falconry/falcon/blob/5233d0abed977d9dab78ebadf305f5abe2eef07c/falcon/testing/helpers.py#L1168-L1172 # noqa if SpanAttributes.NET_PEER_IP in span.attributes: - if package_version.parse(_falcon_version) < 3: - self.assertEqual( - span.attributes[SpanAttributes.NET_PEER_IP], "127.0.0.1" - ) - else: - self.assertEqual( - span.attributes[SpanAttributes.NET_PEER_IP], "/" - ) - + self.assertEqual( + span.attributes[SpanAttributes.NET_PEER_IP], "127.0.0.1" + ) self.memory_exporter.clear() def test_404(self): diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index bb60fdcbe4..152ac59845 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -358,7 +358,7 @@ def collect_request_attributes( remote_addr = environ.get("REMOTE_ADDR") if remote_addr: - _set_http_peer_ip(result, target, sem_conv_opt_in_mode) + _set_http_peer_ip(result, remote_addr, sem_conv_opt_in_mode) peer_port = environ.get("REMOTE_PORT") if peer_port: From 08806acb53bdcfa1932e316e7a2027a4c39df3d9 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Mon, 15 Apr 2024 11:48:53 -0700 Subject: [PATCH 13/42] wsgi tests --- .../tests/test_requests_integration.py | 1 - .../instrumentation/wsgi/__init__.py | 8 +- .../tests/test_wsgi_middleware.py | 256 ++++++++++++++++-- 3 files changed, 238 insertions(+), 27 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index 81b8995be5..81f32a0514 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -524,7 +524,6 @@ def test_requests_exception_new_semconv(self, *_, **__): self.perform_request(url_with_port) span = self.assert_span() - print(span.attributes) self.assertEqual( span.attributes, { diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 152ac59845..11c7232166 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -214,6 +214,8 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he from opentelemetry import context, trace from opentelemetry.instrumentation._semconv import ( + _OpenTelemetrySemanticConventionStability, + _OpenTelemetryStabilitySignalType, _METRIC_ATTRIBUTES_SERVER_DURATION_NAME, _OpenTelemetryStabilityMode, _SPAN_ATTRIBUTES_ERROR_TYPE, @@ -530,8 +532,12 @@ def __init__( response_hook=None, tracer_provider=None, meter_provider=None, - sem_conv_opt_in_mode: _OpenTelemetryStabilityMode = _OpenTelemetryStabilityMode.DEFAULT, ): + # initialize semantic conventions opt-in if needed + _OpenTelemetrySemanticConventionStability._initialize() + sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode( + _OpenTelemetryStabilitySignalType.HTTP, + ) self.wsgi = wsgi self.tracer = trace.get_tracer( __name__, diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index 5563e21df9..0d2e265e94 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -11,7 +11,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - import sys import unittest import wsgiref.util as wsgiref_util @@ -25,6 +24,9 @@ _server_active_requests_count_attrs_old, _server_duration_attrs_new, _server_duration_attrs_old, + _OTEL_SEMCONV_STABILITY_OPT_IN_KEY, + _OpenTelemetrySemanticConventionStability, + _OpenTelemetryStabilityMode, ) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, @@ -133,21 +135,54 @@ def wsgi_with_repeat_custom_response_headers(environ, start_response): return [b"*"] -_expected_metric_names = [ +_expected_metric_names_old = [ "http.server.active_requests", "http.server.duration", ] +_expected_metric_names_new = [ + "http.server.active_requests", + "http.server.request.duration", +] _recommended_metrics_attrs_old = { "http.server.active_requests": _server_active_requests_count_attrs_old, "http.server.duration": _server_duration_attrs_old, } _recommended_metrics_attrs_new = { "http.server.active_requests": _server_active_requests_count_attrs_new, - "http.server.duration": _server_duration_attrs_new, + "http.server.request.duration": _server_duration_attrs_new, +} +_server_active_requests_count_attrs_both = _server_active_requests_count_attrs_old +_server_active_requests_count_attrs_both.extend(_server_active_requests_count_attrs_new) +_recommended_metrics_attrs_both = { + "http.server.active_requests": _server_active_requests_count_attrs_both, + "http.server.duration": _server_duration_attrs_old, + "http.server.request.duration": _server_duration_attrs_new, } class TestWsgiApplication(WsgiTestBase): + def setUp(self): + super().setUp() + + test_name = "" + if hasattr(self, "_testMethodName"): + test_name = self._testMethodName + sem_conv_mode = "default" + if "new_semconv" in test_name: + sem_conv_mode = "http" + elif "both_semconv" in test_name: + sem_conv_mode = "http/dup" + self.env_patch = mock.patch.dict( + "os.environ", + { + _OTEL_SEMCONV_STABILITY_OPT_IN_KEY: sem_conv_mode, + }, + ) + + _OpenTelemetrySemanticConventionStability._initialized = False + + self.env_patch.start() + def validate_response( self, response, @@ -156,6 +191,8 @@ def validate_response( http_method="GET", span_attributes=None, response_headers=None, + old_sem_conv=True, + new_sem_conv=False, ): while True: try: @@ -181,7 +218,8 @@ def validate_response( self.assertEqual(len(span_list), 1) self.assertEqual(span_list[0].name, span_name) self.assertEqual(span_list[0].kind, trace_api.SpanKind.SERVER) - expected_attributes = { + expected_attributes = {} + expected_attributes_old = { SpanAttributes.HTTP_SERVER_NAME: "127.0.0.1", SpanAttributes.HTTP_SCHEME: "http", SpanAttributes.NET_HOST_PORT: 80, @@ -189,11 +227,26 @@ def validate_response( SpanAttributes.HTTP_FLAVOR: "1.0", SpanAttributes.HTTP_URL: "http://127.0.0.1/", SpanAttributes.HTTP_STATUS_CODE: 200, - SpanAttributes.NET_HOST_NAME: "127.0.0.1", + SpanAttributes.NET_HOST_NAME: "127.0.0.1" + } + expected_attributes_new = { + SpanAttributes.URL_SCHEME: "http", + SpanAttributes.SERVER_PORT: 80, + SpanAttributes.SERVER_ADDRESS: "127.0.0.1", + SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.0", + SpanAttributes.HTTP_RESPONSE_STATUS_CODE: 200, } + if old_sem_conv: + expected_attributes.update(expected_attributes_old) + if new_sem_conv: + expected_attributes.update(expected_attributes_new) + expected_attributes.update(span_attributes or {}) if http_method is not None: - expected_attributes[SpanAttributes.HTTP_METHOD] = http_method + if old_sem_conv: + expected_attributes[SpanAttributes.HTTP_METHOD] = http_method + if new_sem_conv: + expected_attributes[SpanAttributes.HTTP_REQUEST_METHOD] = http_method self.assertEqual(span_list[0].attributes, expected_attributes) def test_basic_wsgi_call(self): @@ -201,6 +254,16 @@ def test_basic_wsgi_call(self): response = app(self.environ, self.start_response) self.validate_response(response) + def test_basic_wsgi_call_new_semconv(self): + app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi) + response = app(self.environ, self.start_response) + self.validate_response(response, old_sem_conv=False, new_sem_conv=True) + + def test_basic_wsgi_call_both_semconv(self): + app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi) + response = app(self.environ, self.start_response) + self.validate_response(response, old_sem_conv=True, new_sem_conv=True) + def test_hooks(self): hook_headers = ( "hook_attr", @@ -294,7 +357,7 @@ def test_wsgi_metrics(self): for scope_metric in resource_metric.scope_metrics: self.assertTrue(len(scope_metric.metrics) != 0) for metric in scope_metric.metrics: - self.assertIn(metric.name, _expected_metric_names) + self.assertIn(metric.name, _expected_metric_names_old) data_points = list(metric.data.data_points) self.assertEqual(len(data_points), 1) for point in data_points: @@ -309,6 +372,69 @@ def test_wsgi_metrics(self): ) self.assertTrue(number_data_point_seen and histogram_data_point_seen) + def test_wsgi_metrics_new_semconv(self): + app = otel_wsgi.OpenTelemetryMiddleware(error_wsgi_unhandled) + self.assertRaises(ValueError, app, self.environ, self.start_response) + self.assertRaises(ValueError, app, self.environ, self.start_response) + self.assertRaises(ValueError, app, self.environ, self.start_response) + metrics_list = self.memory_metrics_reader.get_metrics_data() + number_data_point_seen = False + histogram_data_point_seen = False + + self.assertTrue(len(metrics_list.resource_metrics) != 0) + for resource_metric in metrics_list.resource_metrics: + self.assertTrue(len(resource_metric.scope_metrics) != 0) + for scope_metric in resource_metric.scope_metrics: + self.assertTrue(len(scope_metric.metrics) != 0) + for metric in scope_metric.metrics: + self.assertIn(metric.name, _expected_metric_names_new) + data_points = list(metric.data.data_points) + self.assertEqual(len(data_points), 1) + for point in data_points: + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 3) + histogram_data_point_seen = True + if isinstance(point, NumberDataPoint): + number_data_point_seen = True + for attr in point.attributes: + self.assertIn( + attr, _recommended_metrics_attrs_new[metric.name] + ) + self.assertTrue(number_data_point_seen and histogram_data_point_seen) + + def test_wsgi_metrics_both_semconv(self): + app = otel_wsgi.OpenTelemetryMiddleware(error_wsgi_unhandled) + self.assertRaises(ValueError, app, self.environ, self.start_response) + metrics_list = self.memory_metrics_reader.get_metrics_data() + number_data_point_seen = False + histogram_data_point_seen = False + + self.assertTrue(len(metrics_list.resource_metrics) != 0) + for resource_metric in metrics_list.resource_metrics: + self.assertTrue(len(resource_metric.scope_metrics) != 0) + for scope_metric in resource_metric.scope_metrics: + self.assertTrue(len(scope_metric.metrics) != 0) + for metric in scope_metric.metrics: + if metric.unit == "ms": + self.assertEqual(metric.name, "http.server.duration") + elif metric.unit == "s": + self.assertEqual(metric.name, "http.server.request.duration") + else: + self.assertEqual(metric.name, "http.server.active_requests") + data_points = list(metric.data.data_points) + self.assertEqual(len(data_points), 1) + for point in data_points: + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 1) + histogram_data_point_seen = True + if isinstance(point, NumberDataPoint): + number_data_point_seen = True + for attr in point.attributes: + self.assertIn( + attr, _recommended_metrics_attrs_both[metric.name] + ) + self.assertTrue(number_data_point_seen and histogram_data_point_seen) + def test_nonstandard_http_method(self): self.environ["REQUEST_METHOD"] = "NONSTANDARD" app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi) @@ -364,31 +490,76 @@ def test_request_attributes(self): }, ) - def validate_url(self, expected_url, raw=False, has_host=True): + def test_request_attributes_new_semconv(self): + self.environ["QUERY_STRING"] = "foo=bar" + self.environ["REQUEST_URI"] = "http://127.0.0.1/?foo=bar" + + attrs = otel_wsgi.collect_request_attributes( + self.environ, + _OpenTelemetryStabilityMode.HTTP, + ) + self.assertDictEqual( + attrs, + { + SpanAttributes.HTTP_REQUEST_METHOD: "GET", + SpanAttributes.SERVER_ADDRESS: "127.0.0.1", + SpanAttributes.SERVER_PORT: 80, + SpanAttributes.URL_SCHEME: "http", + SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.0", + SpanAttributes.URL_PATH: "/", + SpanAttributes.URL_QUERY: "foo=bar", + }, + ) + + def validate_url(self, expected_url, raw=False, has_host=True, old_semconv=True, new_semconv=False): parts = urlsplit(expected_url) - expected = { + expected_old = { SpanAttributes.HTTP_SCHEME: parts.scheme, SpanAttributes.NET_HOST_PORT: parts.port or (80 if parts.scheme == "http" else 443), SpanAttributes.HTTP_SERVER_NAME: parts.hostname, # Not true in the general case, but for all tests. } - if raw: - expected[SpanAttributes.HTTP_TARGET] = expected_url.split( - parts.netloc, 1 - )[1] - else: - expected[SpanAttributes.HTTP_URL] = expected_url - if has_host: - expected[SpanAttributes.HTTP_HOST] = parts.hostname + expected_new = { + SpanAttributes.URL_SCHEME: parts.scheme, + SpanAttributes.SERVER_PORT: parts.port + or (80 if parts.scheme == "http" else 443), + SpanAttributes.SERVER_ADDRESS: parts.hostname, + SpanAttributes.URL_PATH: parts.path, + SpanAttributes.URL_QUERY: parts.query, + } + if old_semconv: + if raw: + expected_old[SpanAttributes.HTTP_TARGET] = expected_url.split( + parts.netloc, 1 + )[1] + else: + expected_old[SpanAttributes.HTTP_URL] = expected_url + if has_host: + expected_old[SpanAttributes.HTTP_HOST] = parts.hostname + if new_semconv: + if raw: + expected_new[SpanAttributes.URL_PATH] = expected_url.split( + parts.path, 1 + )[1] + if parts.query: + expected_new[SpanAttributes.URL_QUERY] = expected_url.split( + parts.query, 1 + )[1] + else: + expected_new[SpanAttributes.HTTP_URL] = expected_url + if has_host: + expected_new[SpanAttributes.SERVER_ADDRESS] = parts.hostname attrs = otel_wsgi.collect_request_attributes(self.environ) self.assertGreaterEqual( - attrs.items(), expected.items(), expected_url + " expected." + attrs.items(), expected_old.items(), expected_url + " expected." ) def test_request_attributes_with_partial_raw_uri(self): - self.environ["RAW_URI"] = "/#top" - self.validate_url("http://127.0.0.1/#top", raw=True) + self.environ["RAW_URI"] = "/?foo=bar/#top" + self.validate_url("http://127.0.0.1/?foo=bar/#top", raw=True) + self.validate_url("http://127.0.0.1/?foo=bar/#top", raw=True, old_semconv=False, new_semconv=True) + self.validate_url("http://127.0.0.1/?foo=bar/#top", raw=True, old_semconv=True, new_semconv=True) def test_request_attributes_with_partial_raw_uri_and_nonstandard_port( self, @@ -397,18 +568,26 @@ def test_request_attributes_with_partial_raw_uri_and_nonstandard_port( del self.environ["HTTP_HOST"] self.environ["SERVER_PORT"] = "8080" self.validate_url("http://127.0.0.1:8080/?", raw=True, has_host=False) + self.validate_url("http://127.0.0.1:8080/?", raw=True, has_host=False, old_semconv=False, new_semconv=True) + self.validate_url("http://127.0.0.1:8080/?", raw=True, has_host=False, old_semconv=True, new_semconv=True) def test_https_uri_port(self): del self.environ["HTTP_HOST"] self.environ["SERVER_PORT"] = "443" self.environ["wsgi.url_scheme"] = "https" self.validate_url("https://127.0.0.1/", has_host=False) + self.validate_url("https://127.0.0.1/", has_host=False, old_semconv=False, new_semconv=True) + self.validate_url("https://127.0.0.1/", has_host=False, old_semconv=True, new_semconv=True) self.environ["SERVER_PORT"] = "8080" self.validate_url("https://127.0.0.1:8080/", has_host=False) + self.validate_url("https://127.0.0.1:8080/", has_host=False, old_semconv=False, new_semconv=True) + self.validate_url("https://127.0.0.1:8080/", has_host=False, old_semconv=True, new_semconv=True) self.environ["SERVER_PORT"] = "80" self.validate_url("https://127.0.0.1:80/", has_host=False) + self.validate_url("https://127.0.0.1:80/", has_host=False, old_semconv=False, new_semconv=True) + self.validate_url("https://127.0.0.1:80/", has_host=False, old_semconv=True, new_semconv=True) def test_http_uri_port(self): del self.environ["HTTP_HOST"] @@ -459,29 +638,56 @@ def test_request_attributes_with_full_request_uri(self): self.environ["REQUEST_METHOD"] = "CONNECT" self.environ[ "REQUEST_URI" - ] = "127.0.0.1:8080" # Might happen in a CONNECT request - expected = { + ] = "127.0.0.1:8080/?foo=bar" # Might happen in a CONNECT request + expected_old = { SpanAttributes.HTTP_HOST: "127.0.0.1:8080", - SpanAttributes.HTTP_TARGET: "127.0.0.1:8080", + SpanAttributes.HTTP_TARGET: "127.0.0.1:8080/?foo=bar", + } + expected_new = { + SpanAttributes.URL_PATH: "127.0.0.1:8080/", + SpanAttributes.URL_QUERY: "foo=bar", } self.assertGreaterEqual( otel_wsgi.collect_request_attributes(self.environ).items(), - expected.items(), + expected_old.items(), + ) + self.assertGreaterEqual( + otel_wsgi.collect_request_attributes( + self.environ, + _OpenTelemetryStabilityMode.HTTP, + ).items(), + expected_new.items(), ) def test_http_user_agent_attribute(self): self.environ["HTTP_USER_AGENT"] = "test-useragent" expected = {SpanAttributes.HTTP_USER_AGENT: "test-useragent"} + expected_new = {SpanAttributes.USER_AGENT_ORIGINAL: "test-useragent"} self.assertGreaterEqual( otel_wsgi.collect_request_attributes(self.environ).items(), expected.items(), ) + self.assertGreaterEqual( + otel_wsgi.collect_request_attributes( + self.environ, + _OpenTelemetryStabilityMode.HTTP, + ).items(), + expected_new.items(), + ) def test_response_attributes(self): otel_wsgi.add_response_attributes(self.span, "404 Not Found", {}) + otel_wsgi.add_response_attributes( + self.span, + "404 Not Found", + {}, + sem_conv_opt_in_mode=_OpenTelemetryStabilityMode.HTTP, + ) expected = (mock.call(SpanAttributes.HTTP_STATUS_CODE, 404),) - self.assertEqual(self.span.set_attribute.call_count, len(expected)) + expected_new = (mock.call(SpanAttributes.HTTP_RESPONSE_STATUS_CODE, 404),) + self.assertEqual(self.span.set_attribute.call_count, 2) self.span.set_attribute.assert_has_calls(expected, any_order=True) + self.span.set_attribute.assert_has_calls(expected_new, any_order=True) def test_credential_removal(self): self.environ["HTTP_HOST"] = "username:password@mock" From 3eef43783886f4bd961be35d864e3bbcc2fd855c Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Mon, 15 Apr 2024 14:44:54 -0700 Subject: [PATCH 14/42] Update CHANGELOG.md --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55f624e4c5..567045b657 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased -- `opentelemetry-sdk-extension-aws` Register AWS resource detectors under the - `opentelemetry_resource_detector` entry point - ([#2382](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2382)) ### Breaking changes @@ -17,8 +14,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- `opentelemetry-instrumentation-flask`, `opentelemetry-instrumentation-wsgi` Implement new semantic convention opt-in with stable http semantic conventions - ([#2002](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2002)) +- `opentelemetry-sdk-extension-aws` Register AWS resource detectors under the + `opentelemetry_resource_detector` entry point + ([#2382](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2382)) +- `opentelemetry-instrumentation-wsgi` Implement new semantic convention opt-in with stable http semantic conventions + ([#2425](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2425)) - `opentelemetry-instrumentation-threading` Initial release for threading ([#2253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2253)) From 60337bc1c38aeb86435e0b86c542c6a29f22e10d Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Wed, 17 Apr 2024 10:26:53 -0700 Subject: [PATCH 15/42] name --- .../instrumentation/requests/__init__.py | 8 ++--- .../instrumentation/wsgi/__init__.py | 16 +++++----- .../tests/test_wsgi_middleware.py | 10 +++---- .../opentelemetry/instrumentation/_semconv.py | 30 +++++++++---------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index 0415c80082..ee2d8b23d1 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -68,7 +68,7 @@ _filter_semconv_duration_attrs, _get_schema_url, _OpenTelemetrySemanticConventionStability, - _OpenTelemetryStabilityMode, + _HTTPStabilityMode, _OpenTelemetryStabilitySignalType, _report_new, _report_old, @@ -119,7 +119,7 @@ def _instrument( request_hook: _RequestHookT = None, response_hook: _ResponseHookT = None, excluded_urls: ExcludeList = None, - sem_conv_opt_in_mode: _OpenTelemetryStabilityMode = _OpenTelemetryStabilityMode.DEFAULT, + sem_conv_opt_in_mode: _HTTPStabilityMode = _HTTPStabilityMode.DEFAULT, ): """Enables tracing of all requests calls that go through :code:`requests.session.Session.request` (this includes @@ -290,7 +290,7 @@ def get_or_create_headers(): metric_labels, _client_duration_attrs_old, _client_duration_attrs_new, - _OpenTelemetryStabilityMode.DEFAULT + _HTTPStabilityMode.DEFAULT ) duration_histogram_old.record( max(round(elapsed_time * 1000), 0), @@ -301,7 +301,7 @@ def get_or_create_headers(): metric_labels, _client_duration_attrs_old, _client_duration_attrs_new, - _OpenTelemetryStabilityMode.HTTP + _HTTPStabilityMode.HTTP ) duration_histogram_new.record( elapsed_time, attributes=duration_attrs_new diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 11c7232166..021aa60871 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -217,7 +217,7 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he _OpenTelemetrySemanticConventionStability, _OpenTelemetryStabilitySignalType, _METRIC_ATTRIBUTES_SERVER_DURATION_NAME, - _OpenTelemetryStabilityMode, + _HTTPStabilityMode, _SPAN_ATTRIBUTES_ERROR_TYPE, _get_schema_url, _filter_semconv_active_request_count_attr, @@ -305,7 +305,7 @@ def setifnotnone(dic, key, value): def collect_request_attributes( environ, - sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, + sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT, ): """Collects HTTP request attributes from the PEP3333-conforming WSGI environ and returns a dictionary to be used as span creation attributes. @@ -446,7 +446,7 @@ def _parse_status_code(resp_status): return None -def _parse_active_request_count_attrs(req_attrs, sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT): +def _parse_active_request_count_attrs(req_attrs, sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT): return _filter_semconv_active_request_count_attr( req_attrs, _server_active_requests_count_attrs_old, @@ -455,7 +455,7 @@ def _parse_active_request_count_attrs(req_attrs, sem_conv_opt_in_mode = _OpenTel ) -def _parse_duration_attrs(req_attrs, sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT): +def _parse_duration_attrs(req_attrs, sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT): return _filter_semconv_duration_attrs( req_attrs, _server_duration_attrs_old, @@ -469,7 +469,7 @@ def add_response_attributes( start_response_status, response_headers, duration_attrs = None, - sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, + sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT, ): # pylint: disable=unused-argument """Adds HTTP response attributes to span using the arguments passed to a PEP3333-conforming start_response callable. @@ -648,7 +648,7 @@ def __call__(self, environ, start_response): if span.is_recording(): if _report_new(self._sem_conv_opt_in_mode): span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, type(ex).__qualname__ ) - span.set_status(Status(StatusCode.ERROR, str(ex))) + span.set_status(Status(StatusCode.ERROR, str(ex))) span.end() if token is not None: context.detach(token) @@ -656,10 +656,10 @@ def __call__(self, environ, start_response): finally: duration_s = default_timer() - start if self.duration_histogram_old: - duration_attrs_old = _parse_duration_attrs(req_attrs, _OpenTelemetryStabilityMode.DEFAULT) + duration_attrs_old = _parse_duration_attrs(req_attrs, _HTTPStabilityMode.DEFAULT) self.duration_histogram_old.record(max(round(duration_s * 1000), 0), duration_attrs_old) if self.duration_histogram_new: - duration_attrs_new = _parse_duration_attrs(req_attrs, _OpenTelemetryStabilityMode.HTTP) + duration_attrs_new = _parse_duration_attrs(req_attrs, _HTTPStabilityMode.HTTP) self.duration_histogram_new.record(max(round(duration_s * 1000), 0), duration_attrs_new) self.active_requests_counter.add(-1, active_requests_count_attrs) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index 0d2e265e94..73051fa70a 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -26,7 +26,7 @@ _server_duration_attrs_old, _OTEL_SEMCONV_STABILITY_OPT_IN_KEY, _OpenTelemetrySemanticConventionStability, - _OpenTelemetryStabilityMode, + _HTTPStabilityMode, ) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, @@ -496,7 +496,7 @@ def test_request_attributes_new_semconv(self): attrs = otel_wsgi.collect_request_attributes( self.environ, - _OpenTelemetryStabilityMode.HTTP, + _HTTPStabilityMode.HTTP, ) self.assertDictEqual( attrs, @@ -654,7 +654,7 @@ def test_request_attributes_with_full_request_uri(self): self.assertGreaterEqual( otel_wsgi.collect_request_attributes( self.environ, - _OpenTelemetryStabilityMode.HTTP, + _HTTPStabilityMode.HTTP, ).items(), expected_new.items(), ) @@ -670,7 +670,7 @@ def test_http_user_agent_attribute(self): self.assertGreaterEqual( otel_wsgi.collect_request_attributes( self.environ, - _OpenTelemetryStabilityMode.HTTP, + _HTTPStabilityMode.HTTP, ).items(), expected_new.items(), ) @@ -681,7 +681,7 @@ def test_response_attributes(self): self.span, "404 Not Found", {}, - sem_conv_opt_in_mode=_OpenTelemetryStabilityMode.HTTP, + sem_conv_opt_in_mode=_HTTPStabilityMode.HTTP, ) expected = (mock.call(SpanAttributes.HTTP_STATUS_CODE, 404),) expected_new = (mock.call(SpanAttributes.HTTP_RESPONSE_STATUS_CODE, 404),) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index 09f4a1c90b..618d8e225e 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -91,7 +91,7 @@ class _OpenTelemetryStabilitySignalType: HTTP = "http" -class _OpenTelemetryStabilityMode(Enum): +class _HTTPStabilityMode(Enum): # http - emit the new, stable HTTP and networking conventions ONLY HTTP = "http" # http/dup - emit both the old and the stable HTTP and networking conventions @@ -101,11 +101,11 @@ class _OpenTelemetryStabilityMode(Enum): def _report_new(mode): - return mode.name != _OpenTelemetryStabilityMode.DEFAULT.name + return mode.name != _HTTPStabilityMode.DEFAULT.name def _report_old(mode): - return mode.name != _OpenTelemetryStabilityMode.HTTP.name + return mode.name != _HTTPStabilityMode.HTTP.name class _OpenTelemetrySemanticConventionStability: @@ -123,17 +123,17 @@ def _initialize(cls): opt_in_list = [] if opt_in: opt_in_list = [s.strip() for s in opt_in.split(",")] - http_opt_in = _OpenTelemetryStabilityMode.DEFAULT + http_opt_in = _HTTPStabilityMode.DEFAULT if opt_in_list: # Process http opt-in # http/dup takes priority over http if ( - _OpenTelemetryStabilityMode.HTTP_DUP.value + _HTTPStabilityMode.HTTP_DUP.value in opt_in_list ): - http_opt_in = _OpenTelemetryStabilityMode.HTTP_DUP - elif _OpenTelemetryStabilityMode.HTTP.value in opt_in_list: - http_opt_in = _OpenTelemetryStabilityMode.HTTP + http_opt_in = _HTTPStabilityMode.HTTP_DUP + elif _HTTPStabilityMode.HTTP.value in opt_in_list: + http_opt_in = _HTTPStabilityMode.HTTP _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING[ _OpenTelemetryStabilitySignalType.HTTP ] = http_opt_in @@ -144,9 +144,9 @@ def _initialize(cls): def _get_opentelemetry_stability_opt_in_mode( cls, signal_type: _OpenTelemetryStabilitySignalType, - ) -> _OpenTelemetryStabilityMode: + ) -> _HTTPStabilityMode: return _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING.get( - signal_type, _OpenTelemetryStabilityMode.DEFAULT + signal_type, _HTTPStabilityMode.DEFAULT ) @@ -154,13 +154,13 @@ def _filter_semconv_duration_attrs( attrs, old_attrs, new_attrs, - sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, + sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT, ): filtered_attrs = {} # duration is two different metrics depending on sem_conv_opt_in_mode, so no DUP attributes allowed_attributes = ( new_attrs - if sem_conv_opt_in_mode == _OpenTelemetryStabilityMode.HTTP + if sem_conv_opt_in_mode == _HTTPStabilityMode.HTTP else old_attrs ) for key, val in attrs.items(): @@ -173,7 +173,7 @@ def _filter_semconv_active_request_count_attr( attrs, old_attrs, new_attrs, - sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, + sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT, ): filtered_attrs = {} if _report_old(sem_conv_opt_in_mode): @@ -364,7 +364,7 @@ def _set_status(span, metrics_attributes, status_code_str, status_code, sem_conv # Get schema version based off of opt-in mode -def _get_schema_url(mode: _OpenTelemetryStabilityMode) -> str: - if mode is _OpenTelemetryStabilityMode.DEFAULT: +def _get_schema_url(mode: _HTTPStabilityMode) -> str: + if mode is _HTTPStabilityMode.DEFAULT: return "https://opentelemetry.io/schemas/1.11.0" return SpanAttributes.SCHEMA_URL From 69206cab9307eb28f7c0130fac3f8e7a34236757 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 18 Apr 2024 10:39:32 -0700 Subject: [PATCH 16/42] fixes --- .../opentelemetry/instrumentation/wsgi/__init__.py | 7 ++++--- .../tests/test_wsgi_middleware.py | 11 ++++------- .../src/opentelemetry/instrumentation/_semconv.py | 11 +++++++---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 021aa60871..aa4a939479 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -645,8 +645,9 @@ def __call__(self, environ, start_response): iterable = self.wsgi(environ, start_response) return _end_span_after_iterating(iterable, span, token) except Exception as ex: - if span.is_recording(): - if _report_new(self._sem_conv_opt_in_mode): + if _report_new(self._sem_conv_opt_in_mode): + req_attrs[_SPAN_ATTRIBUTES_ERROR_TYPE] = type(ex).__qualname__ + if span.is_recording(): span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, type(ex).__qualname__ ) span.set_status(Status(StatusCode.ERROR, str(ex))) span.end() @@ -660,7 +661,7 @@ def __call__(self, environ, start_response): self.duration_histogram_old.record(max(round(duration_s * 1000), 0), duration_attrs_old) if self.duration_histogram_new: duration_attrs_new = _parse_duration_attrs(req_attrs, _HTTPStabilityMode.HTTP) - self.duration_histogram_new.record(max(round(duration_s * 1000), 0), duration_attrs_new) + self.duration_histogram_new.record(max(duration_s, 0), duration_attrs_new) self.active_requests_counter.add(-1, active_requests_count_attrs) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index 73051fa70a..b143f2b75a 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -230,7 +230,6 @@ def validate_response( SpanAttributes.NET_HOST_NAME: "127.0.0.1" } expected_attributes_new = { - SpanAttributes.URL_SCHEME: "http", SpanAttributes.SERVER_PORT: 80, SpanAttributes.SERVER_ADDRESS: "127.0.0.1", SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.0", @@ -504,7 +503,6 @@ def test_request_attributes_new_semconv(self): SpanAttributes.HTTP_REQUEST_METHOD: "GET", SpanAttributes.SERVER_ADDRESS: "127.0.0.1", SpanAttributes.SERVER_PORT: 80, - SpanAttributes.URL_SCHEME: "http", SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.0", SpanAttributes.URL_PATH: "/", SpanAttributes.URL_QUERY: "foo=bar", @@ -520,7 +518,6 @@ def validate_url(self, expected_url, raw=False, has_host=True, old_semconv=True, SpanAttributes.HTTP_SERVER_NAME: parts.hostname, # Not true in the general case, but for all tests. } expected_new = { - SpanAttributes.URL_SCHEME: parts.scheme, SpanAttributes.SERVER_PORT: parts.port or (80 if parts.scheme == "http" else 443), SpanAttributes.SERVER_ADDRESS: parts.hostname, @@ -638,14 +635,14 @@ def test_request_attributes_with_full_request_uri(self): self.environ["REQUEST_METHOD"] = "CONNECT" self.environ[ "REQUEST_URI" - ] = "127.0.0.1:8080/?foo=bar" # Might happen in a CONNECT request + ] = "http://docs.python.org:80/3/library/urllib.parse.html?highlight=params#url-parsing" # Might happen in a CONNECT request expected_old = { SpanAttributes.HTTP_HOST: "127.0.0.1:8080", - SpanAttributes.HTTP_TARGET: "127.0.0.1:8080/?foo=bar", + SpanAttributes.HTTP_TARGET: "http://docs.python.org:80/3/library/urllib.parse.html?highlight=params#url-parsing", } expected_new = { - SpanAttributes.URL_PATH: "127.0.0.1:8080/", - SpanAttributes.URL_QUERY: "foo=bar", + SpanAttributes.URL_PATH: "/3/library/urllib.parse.html", + SpanAttributes.URL_QUERY: "highlight=params", } self.assertGreaterEqual( otel_wsgi.collect_request_attributes(self.environ).items(), diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index 618d8e225e..1b6c12194a 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -46,7 +46,8 @@ SpanAttributes.NETWORK_PROTOCOL_VERSION, SpanAttributes.SERVER_ADDRESS, SpanAttributes.SERVER_PORT, - SpanAttributes.URL_SCHEME, + # TODO: Support opt-in for scheme in new semconv + # SpanAttributes.URL_SCHEME, ] _server_duration_attrs_old = [ @@ -66,7 +67,8 @@ SpanAttributes.HTTP_RESPONSE_STATUS_CODE, SpanAttributes.HTTP_ROUTE, SpanAttributes.NETWORK_PROTOCOL_VERSION, - SpanAttributes.URL_SCHEME, + # TODO: Support opt-in for scheme in new semconv + # SpanAttributes.URL_SCHEME, ] _server_active_requests_count_attrs_old = [ @@ -237,8 +239,9 @@ def _set_http_url(result, url, sem_conv_opt_in_mode): def _set_http_scheme(result, scheme, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_SCHEME, scheme) - if _report_new(sem_conv_opt_in_mode): - set_string_attribute(result, SpanAttributes.URL_SCHEME, scheme) + # TODO: Support opt-in for scheme in new semconv + # if _report_new(sem_conv_opt_in_mode): + # set_string_attribute(result, SpanAttributes.URL_SCHEME, scheme) def _set_http_host(result, host, sem_conv_opt_in_mode): From 1359c68d636c382e09702091afe1f9d017075c91 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 18 Apr 2024 11:04:46 -0700 Subject: [PATCH 17/42] lint --- .../tests/test_falcon.py | 2 +- .../instrumentation/requests/__init__.py | 4 +- .../tests/test_requests_integration.py | 2 - .../instrumentation/wsgi/__init__.py | 77 +++++++--- .../tests/test_wsgi_middleware.py | 131 ++++++++++++++---- .../opentelemetry/instrumentation/_semconv.py | 61 +++++--- 6 files changed, 200 insertions(+), 77 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 0d4511375c..1ddecc3f21 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -29,7 +29,7 @@ ) from opentelemetry.instrumentation._semconv import ( _server_active_requests_count_attrs_old, - _server_duration_attrs_old + _server_duration_attrs_old, ) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index ee2d8b23d1..8987c38764 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -290,7 +290,7 @@ def get_or_create_headers(): metric_labels, _client_duration_attrs_old, _client_duration_attrs_new, - _HTTPStabilityMode.DEFAULT + _HTTPStabilityMode.DEFAULT, ) duration_histogram_old.record( max(round(elapsed_time * 1000), 0), @@ -301,7 +301,7 @@ def get_or_create_headers(): metric_labels, _client_duration_attrs_old, _client_duration_attrs_new, - _HTTPStabilityMode.HTTP + _HTTPStabilityMode.HTTP, ) duration_histogram_new.record( elapsed_time, attributes=duration_attrs_new diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index 81f32a0514..7dbcc0af05 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -729,7 +729,6 @@ def test_basic_metric_new_semconv(self): SpanAttributes.SERVER_PORT: 8000, SpanAttributes.HTTP_REQUEST_METHOD: "GET", SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.1", - SpanAttributes.URL_SCHEME: "http", } for ( resource_metrics @@ -766,7 +765,6 @@ def test_basic_metric_both_semconv(self): SpanAttributes.SERVER_PORT: 8000, SpanAttributes.HTTP_REQUEST_METHOD: "GET", SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.1", - SpanAttributes.URL_SCHEME: "http", } for ( diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index aa4a939479..b8b9796a29 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -304,9 +304,9 @@ def setifnotnone(dic, key, value): def collect_request_attributes( - environ, - sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT, - ): + environ, + sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT, +): """Collects HTTP request attributes from the PEP3333-conforming WSGI environ and returns a dictionary to be used as span creation attributes. """ @@ -314,9 +314,7 @@ def collect_request_attributes( _set_http_method( result, environ.get("REQUEST_METHOD", ""), - sanitize_method( - environ.get("REQUEST_METHOD", "") - ), + sanitize_method(environ.get("REQUEST_METHOD", "")), sem_conv_opt_in_mode, ) # old semconv v1.12.0 @@ -344,7 +342,6 @@ def collect_request_attributes( sem_conv_opt_in_mode, ) - target = environ.get("RAW_URI") if target is None: # Note: `"" or None is None` target = environ.get("REQUEST_URI") @@ -361,14 +358,16 @@ def collect_request_attributes( remote_addr = environ.get("REMOTE_ADDR") if remote_addr: _set_http_peer_ip(result, remote_addr, sem_conv_opt_in_mode) - + peer_port = environ.get("REMOTE_PORT") if peer_port: _set_http_peer_port_server(result, peer_port, sem_conv_opt_in_mode) remote_host = environ.get("REMOTE_HOST") if remote_host and remote_host != remote_addr: - _set_http_net_peer_name_server(result, remote_host, sem_conv_opt_in_mode) + _set_http_net_peer_name_server( + result, remote_host, sem_conv_opt_in_mode + ) user_agent = environ.get("HTTP_USER_AGENT") if user_agent is not None and len(user_agent) > 0: @@ -446,7 +445,9 @@ def _parse_status_code(resp_status): return None -def _parse_active_request_count_attrs(req_attrs, sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT): +def _parse_active_request_count_attrs( + req_attrs, sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT +): return _filter_semconv_active_request_count_attr( req_attrs, _server_active_requests_count_attrs_old, @@ -455,7 +456,9 @@ def _parse_active_request_count_attrs(req_attrs, sem_conv_opt_in_mode = _HTTPSta ) -def _parse_duration_attrs(req_attrs, sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT): +def _parse_duration_attrs( + req_attrs, sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT +): return _filter_semconv_duration_attrs( req_attrs, _server_duration_attrs_old, @@ -468,8 +471,8 @@ def add_response_attributes( span, start_response_status, response_headers, - duration_attrs = None, - sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT, + duration_attrs=None, + sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT, ): # pylint: disable=unused-argument """Adds HTTP response attributes to span using the arguments passed to a PEP3333-conforming start_response callable. @@ -485,8 +488,14 @@ def add_response_attributes( status_code = -1 if duration_attrs is None: duration_attrs = {} - _set_status(span, duration_attrs, status_code_str, status_code, sem_conv_opt_in_mode) - + _set_status( + span, + duration_attrs, + status_code_str, + status_code, + sem_conv_opt_in_mode, + ) + def get_default_span_name(environ): """ @@ -578,11 +587,21 @@ def __init__( @staticmethod def _create_start_response( - span, start_response, response_hook, duration_attrs, sem_conv_opt_in_mode, + span, + start_response, + response_hook, + duration_attrs, + sem_conv_opt_in_mode, ): @functools.wraps(start_response) def _start_response(status, response_headers, *args, **kwargs): - add_response_attributes(span, status, response_headers, duration_attrs, sem_conv_opt_in_mode) + add_response_attributes( + span, + status, + response_headers, + duration_attrs, + sem_conv_opt_in_mode, + ) if span.is_recording() and span.kind == trace.SpanKind.SERVER: custom_attributes = collect_custom_response_headers_attributes( response_headers @@ -603,7 +622,9 @@ def __call__(self, environ, start_response): environ: A WSGI environment. start_response: The WSGI start_response callable. """ - req_attrs = collect_request_attributes(environ, self._sem_conv_opt_in_mode) + req_attrs = collect_request_attributes( + environ, self._sem_conv_opt_in_mode + ) active_requests_count_attrs = _parse_active_request_count_attrs( req_attrs, self._sem_conv_opt_in_mode, @@ -648,7 +669,9 @@ def __call__(self, environ, start_response): if _report_new(self._sem_conv_opt_in_mode): req_attrs[_SPAN_ATTRIBUTES_ERROR_TYPE] = type(ex).__qualname__ if span.is_recording(): - span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, type(ex).__qualname__ ) + span.set_attribute( + _SPAN_ATTRIBUTES_ERROR_TYPE, type(ex).__qualname__ + ) span.set_status(Status(StatusCode.ERROR, str(ex))) span.end() if token is not None: @@ -657,11 +680,19 @@ def __call__(self, environ, start_response): finally: duration_s = default_timer() - start if self.duration_histogram_old: - duration_attrs_old = _parse_duration_attrs(req_attrs, _HTTPStabilityMode.DEFAULT) - self.duration_histogram_old.record(max(round(duration_s * 1000), 0), duration_attrs_old) + duration_attrs_old = _parse_duration_attrs( + req_attrs, _HTTPStabilityMode.DEFAULT + ) + self.duration_histogram_old.record( + max(round(duration_s * 1000), 0), duration_attrs_old + ) if self.duration_histogram_new: - duration_attrs_new = _parse_duration_attrs(req_attrs, _HTTPStabilityMode.HTTP) - self.duration_histogram_new.record(max(duration_s, 0), duration_attrs_new) + duration_attrs_new = _parse_duration_attrs( + req_attrs, _HTTPStabilityMode.HTTP + ) + self.duration_histogram_new.record( + max(duration_s, 0), duration_attrs_new + ) self.active_requests_counter.add(-1, active_requests_count_attrs) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index b143f2b75a..f232199dbd 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -151,8 +151,12 @@ def wsgi_with_repeat_custom_response_headers(environ, start_response): "http.server.active_requests": _server_active_requests_count_attrs_new, "http.server.request.duration": _server_duration_attrs_new, } -_server_active_requests_count_attrs_both = _server_active_requests_count_attrs_old -_server_active_requests_count_attrs_both.extend(_server_active_requests_count_attrs_new) +_server_active_requests_count_attrs_both = ( + _server_active_requests_count_attrs_old +) +_server_active_requests_count_attrs_both.extend( + _server_active_requests_count_attrs_new +) _recommended_metrics_attrs_both = { "http.server.active_requests": _server_active_requests_count_attrs_both, "http.server.duration": _server_duration_attrs_old, @@ -227,7 +231,7 @@ def validate_response( SpanAttributes.HTTP_FLAVOR: "1.0", SpanAttributes.HTTP_URL: "http://127.0.0.1/", SpanAttributes.HTTP_STATUS_CODE: 200, - SpanAttributes.NET_HOST_NAME: "127.0.0.1" + SpanAttributes.NET_HOST_NAME: "127.0.0.1", } expected_attributes_new = { SpanAttributes.SERVER_PORT: 80, @@ -239,13 +243,15 @@ def validate_response( expected_attributes.update(expected_attributes_old) if new_sem_conv: expected_attributes.update(expected_attributes_new) - + expected_attributes.update(span_attributes or {}) if http_method is not None: if old_sem_conv: expected_attributes[SpanAttributes.HTTP_METHOD] = http_method if new_sem_conv: - expected_attributes[SpanAttributes.HTTP_REQUEST_METHOD] = http_method + expected_attributes[ + SpanAttributes.HTTP_REQUEST_METHOD + ] = http_method self.assertEqual(span_list[0].attributes, expected_attributes) def test_basic_wsgi_call(self): @@ -367,7 +373,8 @@ def test_wsgi_metrics(self): number_data_point_seen = True for attr in point.attributes: self.assertIn( - attr, _recommended_metrics_attrs_old[metric.name] + attr, + _recommended_metrics_attrs_old[metric.name], ) self.assertTrue(number_data_point_seen and histogram_data_point_seen) @@ -397,7 +404,8 @@ def test_wsgi_metrics_new_semconv(self): number_data_point_seen = True for attr in point.attributes: self.assertIn( - attr, _recommended_metrics_attrs_new[metric.name] + attr, + _recommended_metrics_attrs_new[metric.name], ) self.assertTrue(number_data_point_seen and histogram_data_point_seen) @@ -417,9 +425,13 @@ def test_wsgi_metrics_both_semconv(self): if metric.unit == "ms": self.assertEqual(metric.name, "http.server.duration") elif metric.unit == "s": - self.assertEqual(metric.name, "http.server.request.duration") + self.assertEqual( + metric.name, "http.server.request.duration" + ) else: - self.assertEqual(metric.name, "http.server.active_requests") + self.assertEqual( + metric.name, "http.server.active_requests" + ) data_points = list(metric.data.data_points) self.assertEqual(len(data_points), 1) for point in data_points: @@ -430,7 +442,8 @@ def test_wsgi_metrics_both_semconv(self): number_data_point_seen = True for attr in point.attributes: self.assertIn( - attr, _recommended_metrics_attrs_both[metric.name] + attr, + _recommended_metrics_attrs_both[metric.name], ) self.assertTrue(number_data_point_seen and histogram_data_point_seen) @@ -509,7 +522,14 @@ def test_request_attributes_new_semconv(self): }, ) - def validate_url(self, expected_url, raw=False, has_host=True, old_semconv=True, new_semconv=False): + def validate_url( + self, + expected_url, + raw=False, + has_host=True, + old_semconv=True, + new_semconv=False, + ): parts = urlsplit(expected_url) expected_old = { SpanAttributes.HTTP_SCHEME: parts.scheme, @@ -539,9 +559,9 @@ def validate_url(self, expected_url, raw=False, has_host=True, old_semconv=True, parts.path, 1 )[1] if parts.query: - expected_new[SpanAttributes.URL_QUERY] = expected_url.split( - parts.query, 1 - )[1] + expected_new[ + SpanAttributes.URL_QUERY + ] = expected_url.split(parts.query, 1)[1] else: expected_new[SpanAttributes.HTTP_URL] = expected_url if has_host: @@ -555,8 +575,18 @@ def validate_url(self, expected_url, raw=False, has_host=True, old_semconv=True, def test_request_attributes_with_partial_raw_uri(self): self.environ["RAW_URI"] = "/?foo=bar/#top" self.validate_url("http://127.0.0.1/?foo=bar/#top", raw=True) - self.validate_url("http://127.0.0.1/?foo=bar/#top", raw=True, old_semconv=False, new_semconv=True) - self.validate_url("http://127.0.0.1/?foo=bar/#top", raw=True, old_semconv=True, new_semconv=True) + self.validate_url( + "http://127.0.0.1/?foo=bar/#top", + raw=True, + old_semconv=False, + new_semconv=True, + ) + self.validate_url( + "http://127.0.0.1/?foo=bar/#top", + raw=True, + old_semconv=True, + new_semconv=True, + ) def test_request_attributes_with_partial_raw_uri_and_nonstandard_port( self, @@ -565,26 +595,68 @@ def test_request_attributes_with_partial_raw_uri_and_nonstandard_port( del self.environ["HTTP_HOST"] self.environ["SERVER_PORT"] = "8080" self.validate_url("http://127.0.0.1:8080/?", raw=True, has_host=False) - self.validate_url("http://127.0.0.1:8080/?", raw=True, has_host=False, old_semconv=False, new_semconv=True) - self.validate_url("http://127.0.0.1:8080/?", raw=True, has_host=False, old_semconv=True, new_semconv=True) + self.validate_url( + "http://127.0.0.1:8080/?", + raw=True, + has_host=False, + old_semconv=False, + new_semconv=True, + ) + self.validate_url( + "http://127.0.0.1:8080/?", + raw=True, + has_host=False, + old_semconv=True, + new_semconv=True, + ) def test_https_uri_port(self): del self.environ["HTTP_HOST"] self.environ["SERVER_PORT"] = "443" self.environ["wsgi.url_scheme"] = "https" self.validate_url("https://127.0.0.1/", has_host=False) - self.validate_url("https://127.0.0.1/", has_host=False, old_semconv=False, new_semconv=True) - self.validate_url("https://127.0.0.1/", has_host=False, old_semconv=True, new_semconv=True) + self.validate_url( + "https://127.0.0.1/", + has_host=False, + old_semconv=False, + new_semconv=True, + ) + self.validate_url( + "https://127.0.0.1/", + has_host=False, + old_semconv=True, + new_semconv=True, + ) self.environ["SERVER_PORT"] = "8080" self.validate_url("https://127.0.0.1:8080/", has_host=False) - self.validate_url("https://127.0.0.1:8080/", has_host=False, old_semconv=False, new_semconv=True) - self.validate_url("https://127.0.0.1:8080/", has_host=False, old_semconv=True, new_semconv=True) + self.validate_url( + "https://127.0.0.1:8080/", + has_host=False, + old_semconv=False, + new_semconv=True, + ) + self.validate_url( + "https://127.0.0.1:8080/", + has_host=False, + old_semconv=True, + new_semconv=True, + ) self.environ["SERVER_PORT"] = "80" self.validate_url("https://127.0.0.1:80/", has_host=False) - self.validate_url("https://127.0.0.1:80/", has_host=False, old_semconv=False, new_semconv=True) - self.validate_url("https://127.0.0.1:80/", has_host=False, old_semconv=True, new_semconv=True) + self.validate_url( + "https://127.0.0.1:80/", + has_host=False, + old_semconv=False, + new_semconv=True, + ) + self.validate_url( + "https://127.0.0.1:80/", + has_host=False, + old_semconv=True, + new_semconv=True, + ) def test_http_uri_port(self): del self.environ["HTTP_HOST"] @@ -627,8 +699,11 @@ def test_request_attributes_with_faux_scheme_relative_raw_uri(self): def test_request_attributes_pathless(self): self.environ["RAW_URI"] = "" expected = {SpanAttributes.HTTP_TARGET: ""} - self.assertIsNone(otel_wsgi.collect_request_attributes(self.environ) -.get(SpanAttributes.HTTP_TARGET)) + self.assertIsNone( + otel_wsgi.collect_request_attributes(self.environ).get( + SpanAttributes.HTTP_TARGET + ) + ) def test_request_attributes_with_full_request_uri(self): self.environ["HTTP_HOST"] = "127.0.0.1:8080" @@ -681,7 +756,9 @@ def test_response_attributes(self): sem_conv_opt_in_mode=_HTTPStabilityMode.HTTP, ) expected = (mock.call(SpanAttributes.HTTP_STATUS_CODE, 404),) - expected_new = (mock.call(SpanAttributes.HTTP_RESPONSE_STATUS_CODE, 404),) + expected_new = ( + mock.call(SpanAttributes.HTTP_RESPONSE_STATUS_CODE, 404), + ) self.assertEqual(self.span.set_attribute.call_count, 2) self.span.set_attribute.assert_has_calls(expected, any_order=True) self.span.set_attribute.assert_has_calls(expected_new, any_order=True) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index 1b6c12194a..a4a7128327 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -129,10 +129,7 @@ def _initialize(cls): if opt_in_list: # Process http opt-in # http/dup takes priority over http - if ( - _HTTPStabilityMode.HTTP_DUP.value - in opt_in_list - ): + if _HTTPStabilityMode.HTTP_DUP.value in opt_in_list: http_opt_in = _HTTPStabilityMode.HTTP_DUP elif _HTTPStabilityMode.HTTP.value in opt_in_list: http_opt_in = _HTTPStabilityMode.HTTP @@ -156,7 +153,7 @@ def _filter_semconv_duration_attrs( attrs, old_attrs, new_attrs, - sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT, + sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT, ): filtered_attrs = {} # duration is two different metrics depending on sem_conv_opt_in_mode, so no DUP attributes @@ -175,7 +172,7 @@ def _filter_semconv_active_request_count_attr( attrs, old_attrs, new_attrs, - sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT, + sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT, ): filtered_attrs = {} if _report_old(sem_conv_opt_in_mode): @@ -250,8 +247,10 @@ def _set_http_host(result, host, sem_conv_opt_in_mode): if _report_new(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.SERVER_ADDRESS, host) + # Client + def _set_http_net_peer_name_client(result, peer_name, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.NET_PEER_NAME, peer_name) @@ -274,8 +273,10 @@ def _set_http_network_protocol_version(result, version, sem_conv_opt_in_mode): result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version ) + # Server + def _set_http_net_host(result, host, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.NET_HOST_NAME, host) @@ -295,13 +296,9 @@ def _set_http_target(result, target, path, query, sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_TARGET, target) if _report_new(sem_conv_opt_in_mode): if path: - set_string_attribute( - result, SpanAttributes.URL_PATH, path - ) + set_string_attribute(result, SpanAttributes.URL_PATH, path) if query: - set_string_attribute( - result, SpanAttributes.URL_QUERY, query - ) + set_string_attribute(result, SpanAttributes.URL_QUERY, query) def _set_http_peer_ip(result, ip, sem_conv_opt_in_mode): @@ -316,13 +313,17 @@ def _set_http_peer_port_server(result, port, sem_conv_opt_in_mode): set_int_attribute(result, SpanAttributes.NET_PEER_PORT, port) if _report_new(sem_conv_opt_in_mode): set_int_attribute(result, SpanAttributes.CLIENT_PORT, port) - + def _set_http_user_agent(result, user_agent, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): - set_string_attribute(result, SpanAttributes.HTTP_USER_AGENT, user_agent) + set_string_attribute( + result, SpanAttributes.HTTP_USER_AGENT, user_agent + ) if _report_new(sem_conv_opt_in_mode): - set_string_attribute(result, SpanAttributes.USER_AGENT_ORIGINAL, user_agent) + set_string_attribute( + result, SpanAttributes.USER_AGENT_ORIGINAL, user_agent + ) def _set_http_net_peer_name_server(result, name, sem_conv_opt_in_mode): @@ -336,11 +337,19 @@ def _set_http_flavor_version(result, version, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_FLAVOR, version) if _report_new(sem_conv_opt_in_mode): - set_string_attribute(result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version) + set_string_attribute( + result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version + ) -def _set_status(span, metrics_attributes, status_code_str, status_code, sem_conv_opt_in_mode): - if (status_code < 0): +def _set_status( + span, + metrics_attributes, + status_code_str, + status_code, + sem_conv_opt_in_mode, +): + if status_code < 0: if _report_new(sem_conv_opt_in_mode): span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, status_code_str) metrics_attributes[_SPAN_ATTRIBUTES_ERROR_TYPE] = status_code_str @@ -358,11 +367,19 @@ def _set_status(span, metrics_attributes, status_code_str, status_code, sem_conv span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code) metrics_attributes[SpanAttributes.HTTP_STATUS_CODE] = status_code if _report_new(sem_conv_opt_in_mode): - span.set_attribute(SpanAttributes.HTTP_RESPONSE_STATUS_CODE, status_code) - metrics_attributes[SpanAttributes.HTTP_RESPONSE_STATUS_CODE] = status_code + span.set_attribute( + SpanAttributes.HTTP_RESPONSE_STATUS_CODE, status_code + ) + metrics_attributes[ + SpanAttributes.HTTP_RESPONSE_STATUS_CODE + ] = status_code if status == StatusCode.ERROR: - span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, status_code_str) - metrics_attributes[_SPAN_ATTRIBUTES_ERROR_TYPE] = status_code_str + span.set_attribute( + _SPAN_ATTRIBUTES_ERROR_TYPE, status_code_str + ) + metrics_attributes[ + _SPAN_ATTRIBUTES_ERROR_TYPE + ] = status_code_str span.set_status(Status(status)) From f3348a2152922c20ae4af56d56a8d99478a72fb9 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 18 Apr 2024 11:30:58 -0700 Subject: [PATCH 18/42] lint --- .../tests/test_falcon.py | 8 ++++---- .../tests/test_programmatic.py | 2 +- .../instrumentation/requests/__init__.py | 2 +- .../tests/test_requests_integration.py | 6 +++--- .../instrumentation/wsgi/__init__.py | 16 +++++++--------- .../tests/test_wsgi_middleware.py | 8 ++++---- .../opentelemetry/instrumentation/_semconv.py | 10 ++++------ 7 files changed, 24 insertions(+), 28 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 1ddecc3f21..bf7f1d4f49 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -21,16 +21,16 @@ from packaging import version as package_version from opentelemetry import trace +from opentelemetry.instrumentation._semconv import ( + _server_active_requests_count_attrs_old, + _server_duration_attrs_old, +) from opentelemetry.instrumentation.falcon import FalconInstrumentor from opentelemetry.instrumentation.propagators import ( TraceResponsePropagator, get_global_response_propagator, set_global_response_propagator, ) -from opentelemetry.instrumentation._semconv import ( - _server_active_requests_count_attrs_old, - _server_duration_attrs_old, -) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, NumberDataPoint, diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py index cb6b849d88..82ca88460c 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py +++ b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py @@ -19,8 +19,8 @@ from opentelemetry import trace from opentelemetry.instrumentation._semconv import ( - _server_duration_attrs_old, _server_active_requests_count_attrs_old, + _server_duration_attrs_old, ) from opentelemetry.instrumentation.flask import FlaskInstrumentor from opentelemetry.instrumentation.propagators import ( diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index 8987c38764..2052fe47cd 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -67,8 +67,8 @@ _client_duration_attrs_old, _filter_semconv_duration_attrs, _get_schema_url, - _OpenTelemetrySemanticConventionStability, _HTTPStabilityMode, + _OpenTelemetrySemanticConventionStability, _OpenTelemetryStabilitySignalType, _report_new, _report_old, diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index 7dbcc0af05..edc8da301d 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -23,7 +23,7 @@ import opentelemetry.instrumentation.requests from opentelemetry import trace from opentelemetry.instrumentation._semconv import ( - _OTEL_SEMCONV_STABILITY_OPT_IN_KEY, + OTEL_SEMCONV_STABILITY_OPT_IN, _SPAN_ATTRIBUTES_ERROR_TYPE, _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS, _SPAN_ATTRIBUTES_NETWORK_PEER_PORT, @@ -88,7 +88,7 @@ def setUp(self): "os.environ", { "OTEL_PYTHON_REQUESTS_EXCLUDED_URLS": "http://localhost/env_excluded_arg/123,env_excluded_noarg", - _OTEL_SEMCONV_STABILITY_OPT_IN_KEY: sem_conv_mode, + OTEL_SEMCONV_STABILITY_OPT_IN: sem_conv_mode, }, ) @@ -670,7 +670,7 @@ def setUp(self): self.env_patch = mock.patch.dict( "os.environ", { - _OTEL_SEMCONV_STABILITY_OPT_IN_KEY: sem_conv_mode, + OTEL_SEMCONV_STABILITY_OPT_IN: sem_conv_mode, }, ) self.env_patch.start() diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index b8b9796a29..a57ea38e9b 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -214,16 +214,16 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he from opentelemetry import context, trace from opentelemetry.instrumentation._semconv import ( - _OpenTelemetrySemanticConventionStability, - _OpenTelemetryStabilitySignalType, _METRIC_ATTRIBUTES_SERVER_DURATION_NAME, - _HTTPStabilityMode, _SPAN_ATTRIBUTES_ERROR_TYPE, - _get_schema_url, _filter_semconv_active_request_count_attr, _filter_semconv_duration_attrs, - _report_old, + _get_schema_url, + _HTTPStabilityMode, + _OpenTelemetrySemanticConventionStability, + _OpenTelemetryStabilitySignalType, _report_new, + _report_old, _server_active_requests_count_attrs_new, _server_active_requests_count_attrs_old, _server_duration_attrs_new, @@ -240,9 +240,7 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he _set_http_user_agent, _set_status, ) -from opentelemetry.instrumentation.utils import ( - _start_internal_or_server_span, -) +from opentelemetry.instrumentation.utils import _start_internal_or_server_span from opentelemetry.instrumentation.wsgi.version import __version__ from opentelemetry.metrics import get_meter from opentelemetry.propagators.textmap import Getter @@ -250,11 +248,11 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.trace.status import Status, StatusCode from opentelemetry.util.http import ( - _parse_url_query, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE, SanitizeValue, + _parse_url_query, get_custom_headers, normalise_request_header_name, normalise_response_header_name, diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index f232199dbd..ef35a7edf7 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -20,13 +20,13 @@ import opentelemetry.instrumentation.wsgi as otel_wsgi from opentelemetry import trace as trace_api from opentelemetry.instrumentation._semconv import ( + OTEL_SEMCONV_STABILITY_OPT_IN, + _HTTPStabilityMode, + _OpenTelemetrySemanticConventionStability, _server_active_requests_count_attrs_new, _server_active_requests_count_attrs_old, _server_duration_attrs_new, _server_duration_attrs_old, - _OTEL_SEMCONV_STABILITY_OPT_IN_KEY, - _OpenTelemetrySemanticConventionStability, - _HTTPStabilityMode, ) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, @@ -179,7 +179,7 @@ def setUp(self): self.env_patch = mock.patch.dict( "os.environ", { - _OTEL_SEMCONV_STABILITY_OPT_IN_KEY: sem_conv_mode, + OTEL_SEMCONV_STABILITY_OPT_IN: sem_conv_mode, }, ) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index a4a7128327..31c2486acc 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -16,11 +16,9 @@ import threading from enum import Enum -from opentelemetry.instrumentation.utils import ( - http_status_to_status_code, -) -from opentelemetry.trace.status import Status, StatusCode +from opentelemetry.instrumentation.utils import http_status_to_status_code from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.trace.status import Status, StatusCode # TODO: will come through semconv package once updated _SPAN_ATTRIBUTES_ERROR_TYPE = "error.type" @@ -86,7 +84,7 @@ SpanAttributes.URL_SCHEME, ] -_OTEL_SEMCONV_STABILITY_OPT_IN_KEY = "OTEL_SEMCONV_STABILITY_OPT_IN" +OTEL_SEMCONV_STABILITY_OPT_IN = "OTEL_SEMCONV_STABILITY_OPT_IN" class _OpenTelemetryStabilitySignalType: @@ -121,7 +119,7 @@ def _initialize(cls): if not _OpenTelemetrySemanticConventionStability._initialized: # Users can pass in comma delimited string for opt-in options # Only values for http stability are supported for now - opt_in = os.environ.get(_OTEL_SEMCONV_STABILITY_OPT_IN_KEY, "") + opt_in = os.environ.get(OTEL_SEMCONV_STABILITY_OPT_IN, "") opt_in_list = [] if opt_in: opt_in_list = [s.strip() for s in opt_in.split(",")] From e37fa8f0d5366f64b2d8f43e18cfd5daca520fde Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 18 Apr 2024 13:34:29 -0700 Subject: [PATCH 19/42] Update test_requests_integration.py --- .../tests/test_requests_integration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index edc8da301d..d85d70e20e 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -23,10 +23,10 @@ import opentelemetry.instrumentation.requests from opentelemetry import trace from opentelemetry.instrumentation._semconv import ( - OTEL_SEMCONV_STABILITY_OPT_IN, _SPAN_ATTRIBUTES_ERROR_TYPE, _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS, _SPAN_ATTRIBUTES_NETWORK_PEER_PORT, + OTEL_SEMCONV_STABILITY_OPT_IN, _OpenTelemetrySemanticConventionStability, ) from opentelemetry.instrumentation.requests import RequestsInstrumentor From 03252ffba8303eb4f5db725189b533f032aab65f Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Tue, 9 Apr 2024 16:12:00 -0700 Subject: [PATCH 20/42] wsgi, requests --- CHANGELOG.md | 5 + .../instrumentation/requests/__init__.py | 12 +-- .../instrumentation/wsgi/__init__.py | 83 ++++++++++++---- .../opentelemetry/instrumentation/_semconv.py | 96 ++++++++++++++++--- .../src/opentelemetry/util/http/__init__.py | 9 +- 5 files changed, 162 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 146b15a5f0..bfdd5ac570 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-grpc` AioClientInterceptor should propagate with a Metadata object ([#2363](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2363)) +### Added + +- `opentelemetry-instrumentation-flask`, `opentelemetry-instrumentation-wsgi` Implement new semantic convention opt-in with stable http semantic conventions + ([#2002](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2002)) + ## Version 1.24.0/0.45b0 (2024-03-28) ### Added diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index d0150d57b7..cd8ebfe7be 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -70,11 +70,11 @@ _OpenTelemetryStabilitySignalType, _report_new, _report_old, - _set_http_hostname, + _set_http_host, _set_http_method, _set_http_net_peer_name, _set_http_network_protocol_version, - _set_http_port, + _set_http_peer_port_client, _set_http_scheme, _set_http_status_code, _set_http_url, @@ -174,14 +174,14 @@ def get_or_create_headers(): metric_labels, parsed_url.scheme, sem_conv_opt_in_mode ) if parsed_url.hostname: - _set_http_hostname( + _set_http_host( metric_labels, parsed_url.hostname, sem_conv_opt_in_mode ) _set_http_net_peer_name( metric_labels, parsed_url.hostname, sem_conv_opt_in_mode ) if _report_new(sem_conv_opt_in_mode): - _set_http_hostname( + _set_http_host( span_attributes, parsed_url.hostname, sem_conv_opt_in_mode, @@ -191,11 +191,11 @@ def get_or_create_headers(): _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS ] = parsed_url.hostname if parsed_url.port: - _set_http_port( + _set_http_peer_port_client( metric_labels, parsed_url.port, sem_conv_opt_in_mode ) if _report_new(sem_conv_opt_in_mode): - _set_http_port( + _set_http_peer_port_client( span_attributes, parsed_url.port, sem_conv_opt_in_mode ) # Use semconv library when available diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 50d4f03dff..77a9984528 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -213,6 +213,19 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he from timeit import default_timer from opentelemetry import context, trace +from opentelemetry.instrumentation._semconv import ( + _OpenTelemetryStabilityMode, + _report_old, + _set_http_flavor_version, + _set_http_method, + _set_http_net_host, + _set_http_net_host_port, + _set_http_peer_ip, + _set_http_peer_port_client_server, + _set_http_scheme, + _set_http_target, + _set_http_user_agent, +) from opentelemetry.instrumentation.utils import ( _start_internal_or_server_span, http_status_to_status_code, @@ -296,53 +309,81 @@ def setifnotnone(dic, key, value): dic[key] = value -def collect_request_attributes(environ): +def collect_request_attributes( + environ, + sem_conv_opt_in_mode: _OpenTelemetryStabilityMode = _OpenTelemetryStabilityMode.DEFAULT + ): """Collects HTTP request attributes from the PEP3333-conforming WSGI environ and returns a dictionary to be used as span creation attributes. """ - - result = { - SpanAttributes.HTTP_METHOD: sanitize_method( - environ.get("REQUEST_METHOD") + result = {} + _set_http_method( + result, + environ.get("REQUEST_METHOD", ""), + sanitize_method( + environ.get("REQUEST_METHOD", "") ), - SpanAttributes.HTTP_SERVER_NAME: environ.get("SERVER_NAME"), - SpanAttributes.HTTP_SCHEME: environ.get("wsgi.url_scheme"), - } + sem_conv_opt_in_mode, + ) + # old semconv v1.12.0 + server_name = environ.get("SERVER_NAME") + if _report_old(): + result[SpanAttributes.HTTP_SERVER_NAME] = server_name + + _set_http_scheme( + result, + environ.get("wsgi.url_scheme"), + sem_conv_opt_in_mode, + ) + host = environ.get("HTTP_HOST") host_port = environ.get("SERVER_PORT") - if host_port is not None and not host_port == "": - result.update({SpanAttributes.NET_HOST_PORT: int(host_port)}) + if host: + _set_http_net_host(result, host, sem_conv_opt_in_mode) + # old semconv v1.12.0 + if _report_old(): + result[SpanAttributes.HTTP_HOST] = host + if host_port: + _set_http_net_host_port( + result, + int(host_port), + sem_conv_opt_in_mode, + ) + - setifnotnone(result, SpanAttributes.HTTP_HOST, environ.get("HTTP_HOST")) target = environ.get("RAW_URI") if target is None: # Note: `"" or None is None` target = environ.get("REQUEST_URI") if target is not None: - result[SpanAttributes.HTTP_TARGET] = target + _set_http_target(result, target, sem_conv_opt_in_mode) else: - result[SpanAttributes.HTTP_URL] = remove_url_credentials( - wsgiref_util.request_uri(environ) - ) + # old semconv v1.20.0 + if _report_old(): + result[SpanAttributes.HTTP_URL] = remove_url_credentials( + wsgiref_util.request_uri(environ) + ) remote_addr = environ.get("REMOTE_ADDR") if remote_addr: - result[SpanAttributes.NET_PEER_IP] = remote_addr + _set_http_peer_ip(result, target, sem_conv_opt_in_mode) + + peer_port = environ.get("REMOTE_PORT") + if peer_port: + _set_http_peer_port_client_server(result, peer_port, sem_conv_opt_in_mode) + remote_host = environ.get("REMOTE_HOST") if remote_host and remote_host != remote_addr: result[SpanAttributes.NET_PEER_NAME] = remote_host user_agent = environ.get("HTTP_USER_AGENT") if user_agent is not None and len(user_agent) > 0: - result[SpanAttributes.HTTP_USER_AGENT] = user_agent + _set_http_user_agent(result, user_agent, sem_conv_opt_in_mode) - setifnotnone( - result, SpanAttributes.NET_PEER_PORT, environ.get("REMOTE_PORT") - ) flavor = environ.get("SERVER_PROTOCOL", "") if flavor.upper().startswith(_HTTP_VERSION_PREFIX): flavor = flavor[len(_HTTP_VERSION_PREFIX) :] if flavor: - result[SpanAttributes.HTTP_FLAVOR] = flavor + _set_http_flavor_version(result, flavor, sem_conv_opt_in_mode) return result diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index fbfc92cf21..4ca818faea 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -17,6 +17,7 @@ from enum import Enum from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.util.http import _parse_url_query # TODO: will come through semconv package once updated _SPAN_ATTRIBUTES_ERROR_TYPE = "error.type" @@ -90,6 +91,15 @@ def _set_http_method(result, original, normalized, sem_conv_opt_in_mode): ) +def _set_http_status_code(result, code, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_int_attribute(result, SpanAttributes.HTTP_STATUS_CODE, code) + if _report_new(sem_conv_opt_in_mode): + set_int_attribute( + result, SpanAttributes.HTTP_RESPONSE_STATUS_CODE, code + ) + + def _set_http_url(result, url, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_URL, url) @@ -100,17 +110,17 @@ def _set_http_url(result, url, sem_conv_opt_in_mode): def _set_http_scheme(result, scheme, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_SCHEME, scheme) - # TODO: Support opt-in for scheme in new semconv - # if _report_new(sem_conv_opt_in_mode): - # set_string_attribute(result, SpanAttributes.URL_SCHEME, scheme) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.URL_SCHEME, scheme) -def _set_http_hostname(result, hostname, sem_conv_opt_in_mode): +def _set_http_host(result, host, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): - set_string_attribute(result, SpanAttributes.HTTP_HOST, hostname) + set_string_attribute(result, SpanAttributes.HTTP_HOST, host) if _report_new(sem_conv_opt_in_mode): - set_string_attribute(result, SpanAttributes.SERVER_ADDRESS, hostname) + set_string_attribute(result, SpanAttributes.SERVER_ADDRESS, host) +# Client def _set_http_net_peer_name(result, peer_name, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): @@ -119,29 +129,85 @@ def _set_http_net_peer_name(result, peer_name, sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.SERVER_ADDRESS, peer_name) -def _set_http_port(result, port, sem_conv_opt_in_mode): +def _set_http_peer_port_client(result, port, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_int_attribute(result, SpanAttributes.NET_PEER_PORT, port) if _report_new(sem_conv_opt_in_mode): set_int_attribute(result, SpanAttributes.SERVER_PORT, port) -def _set_http_status_code(result, code, sem_conv_opt_in_mode): +def _set_http_network_protocol_version(result, version, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): - set_int_attribute(result, SpanAttributes.HTTP_STATUS_CODE, code) + set_string_attribute(result, SpanAttributes.HTTP_FLAVOR, version) if _report_new(sem_conv_opt_in_mode): - set_int_attribute( - result, SpanAttributes.HTTP_RESPONSE_STATUS_CODE, code + set_string_attribute( + result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version ) +# Server -def _set_http_network_protocol_version(result, version, sem_conv_opt_in_mode): +def _set_http_net_host(result, host, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.NET_HOST_NAME, host) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.SERVER_ADDRESS, host) + + +def _set_http_net_host_port(result, port, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_int_attribute(result, SpanAttributes.NET_HOST_PORT, port) + if _report_new(sem_conv_opt_in_mode): + set_int_attribute(result, SpanAttributes.SERVER_PORT, port) + + +def _set_http_target(result, target, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.HTTP_TARGET, target) + if _report_new(sem_conv_opt_in_mode): + path, query = _parse_url_query(target) + if path: + set_string_attribute( + result, SpanAttributes.URL_PATH, path + ) + if query: + set_string_attribute( + result, SpanAttributes.URL_QUERY, query + ) + + +def _set_http_peer_ip(result, ip, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.NET_PEER_IP, ip) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.CLIENT_ADDRESS, ip) + + +def _set_http_peer_port_client_server(result, port, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_int_attribute(result, SpanAttributes.NET_PEER_PORT, port) + if _report_new(sem_conv_opt_in_mode): + set_int_attribute(result, SpanAttributes.CLIENT_PORT, port) + + +def _set_http_user_agent(result, user_agent, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.HTTP_USER_AGENT, user_agent) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.USER_AGENT_ORIGINAL, user_agent) + + +def _set_http_net_peer_name(result, name, sem_conv_opt_in_mode): + if _report_old(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.NET_PEER_NAME, name) + if _report_new(sem_conv_opt_in_mode): + set_string_attribute(result, SpanAttributes.CLIENT_ADDRESS, name) + + +def _set_http_flavor_version(result, version, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_FLAVOR, version) if _report_new(sem_conv_opt_in_mode): - set_string_attribute( - result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version - ) + set_string_attribute(result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version) _OTEL_SEMCONV_STABILITY_OPT_IN_KEY = "OTEL_SEMCONV_STABILITY_OPT_IN" diff --git a/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py b/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py index 523f9400b1..1f7ce98937 100644 --- a/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py +++ b/util/opentelemetry-util-http/src/opentelemetry/util/http/__init__.py @@ -218,7 +218,7 @@ def sanitize_method(method: Optional[str]) -> Optional[str]: ] ): return method - return "UNKNOWN" + return "_OTHER" def get_custom_headers(env_var: str) -> list[str]: @@ -245,3 +245,10 @@ def _parse_duration_attrs(req_attrs): for key in _duration_attrs.intersection(req_attrs.keys()) } return duration_attrs + + +def _parse_url_query(url: str): + parsed_url = urlparse(url) + path = parsed_url.path + query_params = parsed_url.query + return path, query_params From fe50c2e6fb86967bc97f1d23513dfe12b42cef13 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Wed, 10 Apr 2024 12:08:51 -0700 Subject: [PATCH 21/42] wsgi metrics --- .../instrumentation/requests/__init__.py | 18 +- .../instrumentation/wsgi/__init__.py | 82 ++++--- .../opentelemetry/instrumentation/_semconv.py | 200 +++++++++++------- 3 files changed, 198 insertions(+), 102 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index cd8ebfe7be..96d7a5f928 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -63,7 +63,9 @@ _SPAN_ATTRIBUTES_ERROR_TYPE, _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS, _SPAN_ATTRIBUTES_NETWORK_PEER_PORT, - _filter_duration_attrs, + _client_duration_attrs_new, + _client_duration_attrs_old, + _filter_semconv_duration_attrs, _get_schema_url, _OpenTelemetrySemanticConventionStability, _OpenTelemetryStabilityMode, @@ -284,16 +286,22 @@ def get_or_create_headers(): ).__qualname__ if duration_histogram_old is not None: - duration_attrs_old = _filter_duration_attrs( - metric_labels, _OpenTelemetryStabilityMode.DEFAULT + duration_attrs_old = _filter_semconv_duration_attrs( + metric_labels, + _client_duration_attrs_old, + _client_duration_attrs_new, + _OpenTelemetryStabilityMode.DEFAULT ) duration_histogram_old.record( max(round(elapsed_time * 1000), 0), attributes=duration_attrs_old, ) if duration_histogram_new is not None: - duration_attrs_new = _filter_duration_attrs( - metric_labels, _OpenTelemetryStabilityMode.HTTP + duration_attrs_new = _filter_semconv_duration_attrs( + metric_labels, + _client_duration_attrs_old, + _client_duration_attrs_new, + _OpenTelemetryStabilityMode.HTTP ) duration_histogram_new.record( elapsed_time, attributes=duration_attrs_new diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 77a9984528..386e157e11 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -214,8 +214,18 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he from opentelemetry import context, trace from opentelemetry.instrumentation._semconv import ( + _METRIC_ATTRIBUTES_SERVER_DURATION_NAME, _OpenTelemetryStabilityMode, + _SPAN_ATTRIBUTES_ERROR_TYPE, + _get_schema_url, + _filter_semconv_active_request_count_attr, + _filter_semconv_duration_attrs, _report_old, + _report_new, + _server_active_requests_count_attrs_new, + _server_active_requests_count_attrs_old, + _server_duration_attrs_new, + _server_duration_attrs_old, _set_http_flavor_version, _set_http_method, _set_http_net_host, @@ -311,7 +321,7 @@ def setifnotnone(dic, key, value): def collect_request_attributes( environ, - sem_conv_opt_in_mode: _OpenTelemetryStabilityMode = _OpenTelemetryStabilityMode.DEFAULT + sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, ): """Collects HTTP request attributes from the PEP3333-conforming WSGI environ and returns a dictionary to be used as span creation attributes. @@ -327,7 +337,7 @@ def collect_request_attributes( ) # old semconv v1.12.0 server_name = environ.get("SERVER_NAME") - if _report_old(): + if _report_old(sem_conv_opt_in_mode): result[SpanAttributes.HTTP_SERVER_NAME] = server_name _set_http_scheme( @@ -341,7 +351,7 @@ def collect_request_attributes( if host: _set_http_net_host(result, host, sem_conv_opt_in_mode) # old semconv v1.12.0 - if _report_old(): + if _report_old(sem_conv_opt_in_mode): result[SpanAttributes.HTTP_HOST] = host if host_port: _set_http_net_host_port( @@ -358,7 +368,7 @@ def collect_request_attributes( _set_http_target(result, target, sem_conv_opt_in_mode) else: # old semconv v1.20.0 - if _report_old(): + if _report_old(sem_conv_opt_in_mode): result[SpanAttributes.HTTP_URL] = remove_url_credentials( wsgiref_util.request_uri(environ) ) @@ -451,24 +461,30 @@ def _parse_status_code(resp_status): return None -def _parse_active_request_count_attrs(req_attrs): - active_requests_count_attrs = {} - for attr_key in _active_requests_count_attrs: - if req_attrs.get(attr_key) is not None: - active_requests_count_attrs[attr_key] = req_attrs[attr_key] - return active_requests_count_attrs +def _parse_active_request_count_attrs(req_attrs, sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT): + return _filter_semconv_active_request_count_attr( + req_attrs, + _server_active_requests_count_attrs_old, + _server_active_requests_count_attrs_new, + sem_conv_opt_in_mode, + ) -def _parse_duration_attrs(req_attrs): - duration_attrs = {} - for attr_key in _duration_attrs: - if req_attrs.get(attr_key) is not None: - duration_attrs[attr_key] = req_attrs[attr_key] - return duration_attrs +def _parse_duration_attrs(req_attrs, sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT): + return _filter_semconv_duration_attrs( + req_attrs, + _server_duration_attrs_old, + _server_duration_attrs_new, + sem_conv_opt_in_mode, + ) def add_response_attributes( - span, start_response_status, response_headers + span, + start_response_status, + response_headers, + sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, + duration_attrs = None, ): # pylint: disable=unused-argument """Adds HTTP response attributes to span using the arguments passed to a PEP3333-conforming start_response callable. @@ -505,6 +521,8 @@ def get_default_span_name(environ): The span name. """ method = sanitize_method(environ.get("REQUEST_METHOD", "").strip()) + if method == "_OTHER": + return "HTTP" path = environ.get("PATH_INFO", "").strip() if method and path: return f"{method} {path}" @@ -535,29 +553,41 @@ def __init__( response_hook=None, tracer_provider=None, meter_provider=None, + sem_conv_opt_in_mode: _OpenTelemetryStabilityMode = _OpenTelemetryStabilityMode.DEFAULT, ): self.wsgi = wsgi self.tracer = trace.get_tracer( __name__, __version__, tracer_provider, - schema_url="https://opentelemetry.io/schemas/1.11.0", + schema_url=_get_schema_url(sem_conv_opt_in_mode), ) self.meter = get_meter( __name__, __version__, meter_provider, - schema_url="https://opentelemetry.io/schemas/1.11.0", - ) - self.duration_histogram = self.meter.create_histogram( - name=MetricInstruments.HTTP_SERVER_DURATION, - unit="ms", - description="Duration of HTTP client requests.", + schema_url=_get_schema_url(sem_conv_opt_in_mode), ) + self.duration_histogram_old = None + if _report_old(sem_conv_opt_in_mode): + self.duration_histogram_old = self.meter.create_histogram( + name=MetricInstruments.HTTP_SERVER_DURATION, + unit="ms", + description="measures the duration of the inbound HTTP request", + ) + self.duration_histogram_new = None + if _report_new(sem_conv_opt_in_mode): + self.duration_histogram_new = self.meter.create_histogram( + name=_METRIC_ATTRIBUTES_SERVER_DURATION_NAME, + unit="s", + description="measures the duration of the inbound HTTP request", + ) + # We don't need a separate active request counter for old/new semantic conventions + # because the new attributes are a subset of the old attributes self.active_requests_counter = self.meter.create_up_down_counter( name=MetricInstruments.HTTP_SERVER_ACTIVE_REQUESTS, - unit="requests", - description="measures the number of concurrent HTTP requests that are currently in-flight", + unit="{request}", + description="Number of active HTTP server requests.", ) self.request_hook = request_hook self.response_hook = response_hook diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index 4ca818faea..0441319be1 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -24,6 +24,7 @@ _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS = "network.peer.address" _SPAN_ATTRIBUTES_NETWORK_PEER_PORT = "network.peer.port" _METRIC_ATTRIBUTES_CLIENT_DURATION_NAME = "http.client.request.duration" +_METRIC_ATTRIBUTES_SERVER_DURATION_NAME = "http.server.request.duration" _client_duration_attrs_old = [ SpanAttributes.HTTP_STATUS_CODE, @@ -42,17 +43,122 @@ SpanAttributes.NETWORK_PROTOCOL_VERSION, SpanAttributes.SERVER_ADDRESS, SpanAttributes.SERVER_PORT, - # TODO: Support opt-in for scheme in new semconv - # SpanAttributes.URL_SCHEME, + SpanAttributes.URL_SCHEME, ] +_server_duration_attrs_old = [ + SpanAttributes.HTTP_METHOD, + SpanAttributes.HTTP_HOST, + SpanAttributes.HTTP_SCHEME, + SpanAttributes.HTTP_STATUS_CODE, + SpanAttributes.HTTP_FLAVOR, + SpanAttributes.HTTP_SERVER_NAME, + SpanAttributes.NET_HOST_NAME, + SpanAttributes.NET_HOST_PORT, +] + +_server_duration_attrs_new = [ + _SPAN_ATTRIBUTES_ERROR_TYPE, + SpanAttributes.HTTP_REQUEST_METHOD, + SpanAttributes.HTTP_RESPONSE_STATUS_CODE, + SpanAttributes.HTTP_ROUTE, + SpanAttributes.NETWORK_PROTOCOL_VERSION, + SpanAttributes.URL_SCHEME, +] + +_server_active_requests_count_attrs_old = [ + SpanAttributes.HTTP_METHOD, + SpanAttributes.HTTP_HOST, + SpanAttributes.HTTP_SCHEME, + SpanAttributes.HTTP_FLAVOR, + SpanAttributes.HTTP_SERVER_NAME, + SpanAttributes.NET_HOST_NAME, + SpanAttributes.NET_HOST_PORT, +] + +_server_active_requests_count_attrs_new = [ + SpanAttributes.HTTP_REQUEST_METHOD, + SpanAttributes.URL_SCHEME, +] + +_OTEL_SEMCONV_STABILITY_OPT_IN_KEY = "OTEL_SEMCONV_STABILITY_OPT_IN" + + +class _OpenTelemetryStabilitySignalType: + HTTP = "http" + + +class _OpenTelemetryStabilityMode(Enum): + # http - emit the new, stable HTTP and networking conventions ONLY + HTTP = "http" + # http/dup - emit both the old and the stable HTTP and networking conventions + HTTP_DUP = "http/dup" + # default - continue emitting old experimental HTTP and networking conventions + DEFAULT = "default" + + +def _report_new(mode): + return mode.name != _OpenTelemetryStabilityMode.DEFAULT.name -def _filter_duration_attrs(attrs, sem_conv_opt_in_mode): + +def _report_old(mode): + return mode.name != _OpenTelemetryStabilityMode.HTTP.name + + +class _OpenTelemetrySemanticConventionStability: + _initialized = False + _lock = threading.Lock() + _OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING = {} + + @classmethod + def _initialize(cls): + with _OpenTelemetrySemanticConventionStability._lock: + if not _OpenTelemetrySemanticConventionStability._initialized: + # Users can pass in comma delimited string for opt-in options + # Only values for http stability are supported for now + opt_in = os.environ.get(_OTEL_SEMCONV_STABILITY_OPT_IN_KEY, "") + opt_in_list = [] + if opt_in: + opt_in_list = [s.strip() for s in opt_in.split(",")] + http_opt_in = _OpenTelemetryStabilityMode.DEFAULT + if opt_in_list: + # Process http opt-in + # http/dup takes priority over http + if ( + _OpenTelemetryStabilityMode.HTTP_DUP.value + in opt_in_list + ): + http_opt_in = _OpenTelemetryStabilityMode.HTTP_DUP + elif _OpenTelemetryStabilityMode.HTTP.value in opt_in_list: + http_opt_in = _OpenTelemetryStabilityMode.HTTP + _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING[ + _OpenTelemetryStabilitySignalType.HTTP + ] = http_opt_in + _OpenTelemetrySemanticConventionStability._initialized = True + + @classmethod + # Get OpenTelemetry opt-in mode based off of signal type (http, messaging, etc.) + def _get_opentelemetry_stability_opt_in_mode( + cls, + signal_type: _OpenTelemetryStabilitySignalType, + ) -> _OpenTelemetryStabilityMode: + return _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING.get( + signal_type, _OpenTelemetryStabilityMode.DEFAULT + ) + + +def _filter_semconv_duration_attrs( + attrs, + old_attrs, + new_attrs, + sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, +): filtered_attrs = {} + # duration is two different metrics depending on sem_conv_opt_in_mode, so no DUP attributes allowed_attributes = ( - _client_duration_attrs_new + new_attrs if sem_conv_opt_in_mode == _OpenTelemetryStabilityMode.HTTP - else _client_duration_attrs_old + else old_attrs ) for key, val in attrs.items(): if key in allowed_attributes: @@ -60,6 +166,24 @@ def _filter_duration_attrs(attrs, sem_conv_opt_in_mode): return filtered_attrs +def _filter_semconv_active_request_count_attr( + attrs, + old_attrs, + new_attrs, + sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, +): + filtered_attrs = {} + if _report_old(sem_conv_opt_in_mode): + for key, val in attrs.items(): + if key in old_attrs: + filtered_attrs[key] = val + if _report_new(sem_conv_opt_in_mode): + for key, val in attrs.items(): + if key in new_attrs: + filtered_attrs[key] = val + return filtered_attrs + + def set_string_attribute(result, key, value): if value: result[key] = value @@ -210,72 +334,6 @@ def _set_http_flavor_version(result, version, sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version) -_OTEL_SEMCONV_STABILITY_OPT_IN_KEY = "OTEL_SEMCONV_STABILITY_OPT_IN" - - -class _OpenTelemetryStabilitySignalType: - HTTP = "http" - - -class _OpenTelemetryStabilityMode(Enum): - # http - emit the new, stable HTTP and networking conventions ONLY - HTTP = "http" - # http/dup - emit both the old and the stable HTTP and networking conventions - HTTP_DUP = "http/dup" - # default - continue emitting old experimental HTTP and networking conventions - DEFAULT = "default" - - -def _report_new(mode): - return mode.name != _OpenTelemetryStabilityMode.DEFAULT.name - - -def _report_old(mode): - return mode.name != _OpenTelemetryStabilityMode.HTTP.name - - -class _OpenTelemetrySemanticConventionStability: - _initialized = False - _lock = threading.Lock() - _OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING = {} - - @classmethod - def _initialize(cls): - with _OpenTelemetrySemanticConventionStability._lock: - if not _OpenTelemetrySemanticConventionStability._initialized: - # Users can pass in comma delimited string for opt-in options - # Only values for http stability are supported for now - opt_in = os.environ.get(_OTEL_SEMCONV_STABILITY_OPT_IN_KEY, "") - opt_in_list = [] - if opt_in: - opt_in_list = [s.strip() for s in opt_in.split(",")] - http_opt_in = _OpenTelemetryStabilityMode.DEFAULT - if opt_in_list: - # Process http opt-in - # http/dup takes priority over http - if ( - _OpenTelemetryStabilityMode.HTTP_DUP.value - in opt_in_list - ): - http_opt_in = _OpenTelemetryStabilityMode.HTTP_DUP - elif _OpenTelemetryStabilityMode.HTTP.value in opt_in_list: - http_opt_in = _OpenTelemetryStabilityMode.HTTP - _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING[ - _OpenTelemetryStabilitySignalType.HTTP - ] = http_opt_in - _OpenTelemetrySemanticConventionStability._initialized = True - - @classmethod - # Get OpenTelemetry opt-in mode based off of signal type (http, messaging, etc.) - def _get_opentelemetry_stability_opt_in_mode( - cls, - signal_type: _OpenTelemetryStabilitySignalType, - ) -> _OpenTelemetryStabilityMode: - return _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING.get( - signal_type, _OpenTelemetryStabilityMode.DEFAULT - ) - - # Get schema version based off of opt-in mode def _get_schema_url(mode: _OpenTelemetryStabilityMode) -> str: if mode is _OpenTelemetryStabilityMode.DEFAULT: From de0e426fc530d6e128652870900353963212e9bc Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Wed, 10 Apr 2024 16:24:40 -0700 Subject: [PATCH 22/42] wsgi resp --- .../instrumentation/requests/__init__.py | 4 +- .../instrumentation/wsgi/__init__.py | 64 ++++++++++--------- .../opentelemetry/instrumentation/_semconv.py | 37 ++++++++++- 3 files changed, 70 insertions(+), 35 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index 96d7a5f928..54b718f678 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -74,7 +74,7 @@ _report_old, _set_http_host, _set_http_method, - _set_http_net_peer_name, + _set_http_net_peer_name_client, _set_http_network_protocol_version, _set_http_peer_port_client, _set_http_scheme, @@ -179,7 +179,7 @@ def get_or_create_headers(): _set_http_host( metric_labels, parsed_url.hostname, sem_conv_opt_in_mode ) - _set_http_net_peer_name( + _set_http_net_peer_name_client( metric_labels, parsed_url.hostname, sem_conv_opt_in_mode ) if _report_new(sem_conv_opt_in_mode): diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 386e157e11..5e55fb40d1 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -230,15 +230,16 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he _set_http_method, _set_http_net_host, _set_http_net_host_port, + _set_http_net_peer_name_server, _set_http_peer_ip, - _set_http_peer_port_client_server, + _set_http_peer_port_server, _set_http_scheme, _set_http_target, _set_http_user_agent, + _set_status, ) from opentelemetry.instrumentation.utils import ( _start_internal_or_server_span, - http_status_to_status_code, ) from opentelemetry.instrumentation.wsgi.version import __version__ from opentelemetry.metrics import get_meter @@ -379,11 +380,11 @@ def collect_request_attributes( peer_port = environ.get("REMOTE_PORT") if peer_port: - _set_http_peer_port_client_server(result, peer_port, sem_conv_opt_in_mode) + _set_http_peer_port_server(result, peer_port, sem_conv_opt_in_mode) remote_host = environ.get("REMOTE_HOST") if remote_host and remote_host != remote_addr: - result[SpanAttributes.NET_PEER_NAME] = remote_host + _set_http_net_peer_name_server(result, remote_host, sem_conv_opt_in_mode) user_agent = environ.get("HTTP_USER_AGENT") if user_agent is not None and len(user_agent) > 0: @@ -491,23 +492,17 @@ def add_response_attributes( """ if not span.is_recording(): return - status_code, _ = start_response_status.split(" ", 1) + status_code_str, _ = start_response_status.split(" ", 1) + status_code = 0 try: - status_code = int(status_code) + status_code = int(status_code_str) 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)) - ) - + status_code = -1 + if duration_attrs is None: + duration_attrs = {} + _set_status(span, duration_attrs, status_code_str, status_code, sem_conv_opt_in_mode) + def get_default_span_name(environ): """ @@ -591,17 +586,15 @@ def __init__( ) self.request_hook = request_hook self.response_hook = response_hook + self._sem_conv_opt_in_mode = sem_conv_opt_in_mode @staticmethod def _create_start_response( - span, start_response, response_hook, duration_attrs + span, start_response, response_hook, duration_attrs, sem_conv_opt_in_mode, ): @functools.wraps(start_response) def _start_response(status, response_headers, *args, **kwargs): - add_response_attributes(span, status, response_headers) - status_code = _parse_status_code(status) - if status_code is not None: - duration_attrs[SpanAttributes.HTTP_STATUS_CODE] = status_code + add_response_attributes(span, status, response_headers, duration_attrs, sem_conv_opt_in_mode) if span.is_recording() and span.kind == trace.SpanKind.SERVER: custom_attributes = collect_custom_response_headers_attributes( response_headers @@ -622,11 +615,11 @@ def __call__(self, environ, start_response): environ: A WSGI environment. start_response: The WSGI start_response callable. """ - req_attrs = collect_request_attributes(environ) + req_attrs = collect_request_attributes(environ, self._sem_conv_opt_in_mode) active_requests_count_attrs = _parse_active_request_count_attrs( - req_attrs + req_attrs, + self._sem_conv_opt_in_mode, ) - duration_attrs = _parse_duration_attrs(req_attrs) span, token = _start_internal_or_server_span( tracer=self.tracer, @@ -655,20 +648,31 @@ def __call__(self, environ, start_response): try: with trace.use_span(span): start_response = self._create_start_response( - span, start_response, response_hook, duration_attrs + span, + start_response, + response_hook, + req_attrs, + self._sem_conv_opt_in_mode, ) iterable = self.wsgi(environ, start_response) return _end_span_after_iterating(iterable, span, token) except Exception as ex: if span.is_recording(): - span.set_status(Status(StatusCode.ERROR, str(ex))) + if _report_new(self._sem_conv_opt_in_mode): + span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, type(ex).__qualname__ ) + span.set_status(Status(StatusCode.ERROR, str(ex))) span.end() if token is not None: context.detach(token) raise finally: - duration = max(round((default_timer() - start) * 1000), 0) - self.duration_histogram.record(duration, duration_attrs) + duration_s = default_timer() - start + if self.duration_histogram_old: + duration_attrs_old = _parse_duration_attrs(req_attrs, _OpenTelemetryStabilityMode.DEFAULT) + self.duration_histogram_old.record(max(round(duration_s * 1000), 0), duration_attrs_old) + if self.duration_histogram_new: + duration_attrs_new = _parse_duration_attrs(req_attrs, _OpenTelemetryStabilityMode.HTTP) + self.duration_histogram_new.record(max(round(duration_s * 1000), 0), duration_attrs_new) self.active_requests_counter.add(-1, active_requests_count_attrs) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index 0441319be1..c6f6ce25cf 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -16,6 +16,10 @@ import threading from enum import Enum +from opentelemetry.instrumentation.utils import ( + http_status_to_status_code, +) +from opentelemetry.trace.status import Status, StatusCode from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.util.http import _parse_url_query @@ -246,7 +250,7 @@ def _set_http_host(result, host, sem_conv_opt_in_mode): # Client -def _set_http_net_peer_name(result, peer_name, sem_conv_opt_in_mode): +def _set_http_net_peer_name_client(result, peer_name, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.NET_PEER_NAME, peer_name) if _report_new(sem_conv_opt_in_mode): @@ -306,7 +310,7 @@ def _set_http_peer_ip(result, ip, sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.CLIENT_ADDRESS, ip) -def _set_http_peer_port_client_server(result, port, sem_conv_opt_in_mode): +def _set_http_peer_port_server(result, port, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_int_attribute(result, SpanAttributes.NET_PEER_PORT, port) if _report_new(sem_conv_opt_in_mode): @@ -320,7 +324,7 @@ def _set_http_user_agent(result, user_agent, sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.USER_AGENT_ORIGINAL, user_agent) -def _set_http_net_peer_name(result, name, sem_conv_opt_in_mode): +def _set_http_net_peer_name_server(result, name, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.NET_PEER_NAME, name) if _report_new(sem_conv_opt_in_mode): @@ -334,6 +338,33 @@ def _set_http_flavor_version(result, version, sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version) +def _set_status(span, metrics_attributes, status_code_str, status_code, sem_conv_opt_in_mode): + if (status_code < 0): + if _report_new(sem_conv_opt_in_mode): + span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, status_code_str) + metrics_attributes[_SPAN_ATTRIBUTES_ERROR_TYPE] = status_code_str + + span.set_status( + Status( + StatusCode.ERROR, + "Non-integer HTTP status: " + status_code_str, + ) + ) + else: + status = http_status_to_status_code(status_code, server_span=True) + + if _report_old(sem_conv_opt_in_mode): + span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code) + metrics_attributes[SpanAttributes.HTTP_STATUS_CODE] = status_code + if _report_new(sem_conv_opt_in_mode): + span.set_attribute(SpanAttributes.HTTP_RESPONSE_STATUS_CODE, status_code) + metrics_attributes[SpanAttributes.HTTP_RESPONSE_STATUS_CODE] = status_code + if status == StatusCode.ERROR: + span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, status_code_str) + metrics_attributes[_SPAN_ATTRIBUTES_ERROR_TYPE] = status_code_str + span.set_status(Status(status)) + + # Get schema version based off of opt-in mode def _get_schema_url(mode: _OpenTelemetryStabilityMode) -> str: if mode is _OpenTelemetryStabilityMode.DEFAULT: From 0d57a3e64cbd9b80ec92fdcbfe9a8b8424995f6d Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 11 Apr 2024 13:03:54 -0700 Subject: [PATCH 23/42] fix wsgi tests --- .../tests/test_falcon.py | 10 ++--- .../instrumentation/wsgi/__init__.py | 39 +++++-------------- .../tests/test_wsgi_middleware.py | 28 ++++++++----- 3 files changed, 33 insertions(+), 44 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 2245dbfd80..74cd77c8c4 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -27,9 +27,9 @@ get_global_response_propagator, set_global_response_propagator, ) -from opentelemetry.instrumentation.wsgi import ( - _active_requests_count_attrs, - _duration_attrs, +from opentelemetry.instrumentation._semconv import ( + _server_active_requests_count_attrs_old, + _server_duration_attrs_old ) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, @@ -53,8 +53,8 @@ "http.server.duration", ] _recommended_attrs = { - "http.server.active_requests": _active_requests_count_attrs, - "http.server.duration": _duration_attrs, + "http.server.active_requests": _server_active_requests_count_attrs_old, + "http.server.duration": _server_duration_attrs_old, } diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 5e55fb40d1..7e789871ea 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -263,26 +263,6 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he _CARRIER_KEY_PREFIX = "HTTP_" _CARRIER_KEY_PREFIX_LEN = len(_CARRIER_KEY_PREFIX) -# List of recommended attributes -_duration_attrs = [ - SpanAttributes.HTTP_METHOD, - SpanAttributes.HTTP_HOST, - SpanAttributes.HTTP_SCHEME, - SpanAttributes.HTTP_STATUS_CODE, - SpanAttributes.HTTP_FLAVOR, - SpanAttributes.HTTP_SERVER_NAME, - SpanAttributes.NET_HOST_NAME, - SpanAttributes.NET_HOST_PORT, -] - -_active_requests_count_attrs = [ - SpanAttributes.HTTP_METHOD, - SpanAttributes.HTTP_HOST, - SpanAttributes.HTTP_SCHEME, - SpanAttributes.HTTP_FLAVOR, - SpanAttributes.HTTP_SERVER_NAME, -] - class WSGIGetter(Getter[dict]): def get( @@ -354,18 +334,18 @@ def collect_request_attributes( # old semconv v1.12.0 if _report_old(sem_conv_opt_in_mode): result[SpanAttributes.HTTP_HOST] = host - if host_port: - _set_http_net_host_port( - result, - int(host_port), - sem_conv_opt_in_mode, - ) + if host_port: + _set_http_net_host_port( + result, + int(host_port), + sem_conv_opt_in_mode, + ) target = environ.get("RAW_URI") if target is None: # Note: `"" or None is None` target = environ.get("REQUEST_URI") - if target is not None: + if target: _set_http_target(result, target, sem_conv_opt_in_mode) else: # old semconv v1.20.0 @@ -483,9 +463,8 @@ def _parse_duration_attrs(req_attrs, sem_conv_opt_in_mode = _OpenTelemetryStabil def add_response_attributes( span, start_response_status, - response_headers, - sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, duration_attrs = None, + sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, ): # pylint: disable=unused-argument """Adds HTTP response attributes to span using the arguments passed to a PEP3333-conforming start_response callable. @@ -594,7 +573,7 @@ def _create_start_response( ): @functools.wraps(start_response) def _start_response(status, response_headers, *args, **kwargs): - add_response_attributes(span, status, response_headers, duration_attrs, sem_conv_opt_in_mode) + add_response_attributes(span, status, duration_attrs, sem_conv_opt_in_mode) if span.is_recording() and span.kind == trace.SpanKind.SERVER: custom_attributes = collect_custom_response_headers_attributes( response_headers diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index f74dd67867..5563e21df9 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -20,6 +20,12 @@ import opentelemetry.instrumentation.wsgi as otel_wsgi from opentelemetry import trace as trace_api +from opentelemetry.instrumentation._semconv import ( + _server_active_requests_count_attrs_new, + _server_active_requests_count_attrs_old, + _server_duration_attrs_new, + _server_duration_attrs_old, +) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, NumberDataPoint, @@ -131,9 +137,13 @@ def wsgi_with_repeat_custom_response_headers(environ, start_response): "http.server.active_requests", "http.server.duration", ] -_recommended_attrs = { - "http.server.active_requests": otel_wsgi._active_requests_count_attrs, - "http.server.duration": otel_wsgi._duration_attrs, +_recommended_metrics_attrs_old = { + "http.server.active_requests": _server_active_requests_count_attrs_old, + "http.server.duration": _server_duration_attrs_old, +} +_recommended_metrics_attrs_new = { + "http.server.active_requests": _server_active_requests_count_attrs_new, + "http.server.duration": _server_duration_attrs_new, } @@ -179,6 +189,7 @@ def validate_response( SpanAttributes.HTTP_FLAVOR: "1.0", SpanAttributes.HTTP_URL: "http://127.0.0.1/", SpanAttributes.HTTP_STATUS_CODE: 200, + SpanAttributes.NET_HOST_NAME: "127.0.0.1", } expected_attributes.update(span_attributes or {}) if http_method is not None: @@ -294,7 +305,7 @@ def test_wsgi_metrics(self): number_data_point_seen = True for attr in point.attributes: self.assertIn( - attr, _recommended_attrs[metric.name] + attr, _recommended_metrics_attrs_old[metric.name] ) self.assertTrue(number_data_point_seen and histogram_data_point_seen) @@ -303,7 +314,7 @@ def test_nonstandard_http_method(self): app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi) response = app(self.environ, self.start_response) self.validate_response( - response, span_name="UNKNOWN /", http_method="UNKNOWN" + response, span_name="HTTP", http_method="_OTHER" ) @mock.patch.dict( @@ -349,6 +360,7 @@ def test_request_attributes(self): SpanAttributes.HTTP_SCHEME: "http", SpanAttributes.HTTP_SERVER_NAME: "127.0.0.1", SpanAttributes.HTTP_FLAVOR: "1.0", + SpanAttributes.NET_HOST_NAME: "127.0.0.1", }, ) @@ -439,10 +451,8 @@ def test_request_attributes_with_faux_scheme_relative_raw_uri(self): def test_request_attributes_pathless(self): self.environ["RAW_URI"] = "" expected = {SpanAttributes.HTTP_TARGET: ""} - self.assertGreaterEqual( - otel_wsgi.collect_request_attributes(self.environ).items(), - expected.items(), - ) + self.assertIsNone(otel_wsgi.collect_request_attributes(self.environ) +.get(SpanAttributes.HTTP_TARGET)) def test_request_attributes_with_full_request_uri(self): self.environ["HTTP_HOST"] = "127.0.0.1:8080" From d25c7d73a135db823387247ce2a056adb9181917 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 11 Apr 2024 13:43:24 -0700 Subject: [PATCH 24/42] util --- .../src/opentelemetry/instrumentation/wsgi/__init__.py | 4 +++- .../src/opentelemetry/instrumentation/_semconv.py | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 7e789871ea..8696d534ab 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -248,6 +248,7 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.trace.status import Status, StatusCode from opentelemetry.util.http import ( + _parse_url_query, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE, @@ -346,7 +347,8 @@ def collect_request_attributes( if target is None: # Note: `"" or None is None` target = environ.get("REQUEST_URI") if target: - _set_http_target(result, target, sem_conv_opt_in_mode) + path, query = _parse_url_query(target) + _set_http_target(result, target, path, query, sem_conv_opt_in_mode) else: # old semconv v1.20.0 if _report_old(sem_conv_opt_in_mode): diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index c6f6ce25cf..09f4a1c90b 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -21,7 +21,6 @@ ) from opentelemetry.trace.status import Status, StatusCode from opentelemetry.semconv.trace import SpanAttributes -from opentelemetry.util.http import _parse_url_query # TODO: will come through semconv package once updated _SPAN_ATTRIBUTES_ERROR_TYPE = "error.type" @@ -288,11 +287,10 @@ def _set_http_net_host_port(result, port, sem_conv_opt_in_mode): set_int_attribute(result, SpanAttributes.SERVER_PORT, port) -def _set_http_target(result, target, sem_conv_opt_in_mode): +def _set_http_target(result, target, path, query, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_TARGET, target) if _report_new(sem_conv_opt_in_mode): - path, query = _parse_url_query(target) if path: set_string_attribute( result, SpanAttributes.URL_PATH, path From 60ec5e503f385f4cdd2bf61877a56e9188aa9503 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 11 Apr 2024 14:12:59 -0700 Subject: [PATCH 25/42] fix tests --- .../tests/test_falcon.py | 11 +++++++---- .../tests/test_programmatic.py | 14 +++++++------- .../tests/test_requests_integration.py | 2 ++ .../opentelemetry/instrumentation/wsgi/__init__.py | 1 + 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 74cd77c8c4..06b27f95fb 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -125,7 +125,7 @@ def _test_method(self, method): SpanAttributes.NET_HOST_PORT: 80, SpanAttributes.HTTP_HOST: "falconframework.org", SpanAttributes.HTTP_TARGET: "/", - SpanAttributes.NET_PEER_PORT: "65133", + SpanAttributes.NET_PEER_PORT: 65133, SpanAttributes.HTTP_FLAVOR: "1.1", "falcon.resource": "HelloWorldResource", SpanAttributes.HTTP_STATUS_CODE: 201, @@ -156,7 +156,7 @@ def test_404(self): SpanAttributes.NET_HOST_PORT: 80, SpanAttributes.HTTP_HOST: "falconframework.org", SpanAttributes.HTTP_TARGET: "/", - SpanAttributes.NET_PEER_PORT: "65133", + SpanAttributes.NET_PEER_PORT: 65133, SpanAttributes.HTTP_FLAVOR: "1.1", SpanAttributes.HTTP_STATUS_CODE: 404, }, @@ -193,7 +193,7 @@ def test_500(self): SpanAttributes.NET_HOST_PORT: 80, SpanAttributes.HTTP_HOST: "falconframework.org", SpanAttributes.HTTP_TARGET: "/", - SpanAttributes.NET_PEER_PORT: "65133", + SpanAttributes.NET_PEER_PORT: 65133, SpanAttributes.HTTP_FLAVOR: "1.1", SpanAttributes.HTTP_STATUS_CODE: 500, }, @@ -226,7 +226,7 @@ def test_url_template(self): SpanAttributes.NET_HOST_PORT: 80, SpanAttributes.HTTP_HOST: "falconframework.org", SpanAttributes.HTTP_TARGET: "/", - SpanAttributes.NET_PEER_PORT: "65133", + SpanAttributes.NET_PEER_PORT: 65133, SpanAttributes.HTTP_FLAVOR: "1.1", "falcon.resource": "UserResource", SpanAttributes.HTTP_STATUS_CODE: 200, @@ -336,6 +336,7 @@ def test_falcon_metric_values(self): "http.flavor": "1.1", "http.server_name": "falconframework.org", "net.host.port": 80, + "net.host.name": "falconframework.org", "http.status_code": 404, } expected_requests_count_attributes = { @@ -344,6 +345,8 @@ def test_falcon_metric_values(self): "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "falconframework.org", + "net.host.name": "falconframework.org", + "net.host.port": 80, } start = default_timer() self.client().simulate_get("/hello/756") diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py index dec265907f..00a37d2339 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py +++ b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py @@ -18,17 +18,17 @@ from flask import Flask, request from opentelemetry import trace +from opentelemetry.instrumentation._semconv import ( + _server_duration_attrs_old, + _server_active_requests_count_attrs_old, +) from opentelemetry.instrumentation.flask import FlaskInstrumentor from opentelemetry.instrumentation.propagators import ( TraceResponsePropagator, get_global_response_propagator, set_global_response_propagator, ) -from opentelemetry.instrumentation.wsgi import ( - OpenTelemetryMiddleware, - _active_requests_count_attrs, - _duration_attrs, -) +from opentelemetry.instrumentation.wsgi import OpenTelemetryMiddleware from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, NumberDataPoint, @@ -69,8 +69,8 @@ def expected_attributes(override_attributes): "http.server.duration", ] _recommended_attrs = { - "http.server.active_requests": _active_requests_count_attrs, - "http.server.duration": _duration_attrs, + "http.server.active_requests": _server_active_requests_count_attrs_old, + "http.server.duration": _server_duration_attrs_old, } diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index 8817053068..81b8995be5 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -730,6 +730,7 @@ def test_basic_metric_new_semconv(self): SpanAttributes.SERVER_PORT: 8000, SpanAttributes.HTTP_REQUEST_METHOD: "GET", SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.1", + SpanAttributes.URL_SCHEME: "http", } for ( resource_metrics @@ -766,6 +767,7 @@ def test_basic_metric_both_semconv(self): SpanAttributes.SERVER_PORT: 8000, SpanAttributes.HTTP_REQUEST_METHOD: "GET", SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.1", + SpanAttributes.URL_SCHEME: "http", } for ( diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 8696d534ab..46f807b706 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -465,6 +465,7 @@ def _parse_duration_attrs(req_attrs, sem_conv_opt_in_mode = _OpenTelemetryStabil def add_response_attributes( span, start_response_status, + response_headers, duration_attrs = None, sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, ): # pylint: disable=unused-argument From 4b83027f8bc47bb59553f1f66857ad33416b9ab0 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 11 Apr 2024 14:26:15 -0700 Subject: [PATCH 26/42] fix wsgi --- .../src/opentelemetry/instrumentation/wsgi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 46f807b706..bb60fdcbe4 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -576,7 +576,7 @@ def _create_start_response( ): @functools.wraps(start_response) def _start_response(status, response_headers, *args, **kwargs): - add_response_attributes(span, status, duration_attrs, sem_conv_opt_in_mode) + add_response_attributes(span, status, response_headers, duration_attrs, sem_conv_opt_in_mode) if span.is_recording() and span.kind == trace.SpanKind.SERVER: custom_attributes = collect_custom_response_headers_attributes( response_headers From 176758017650eceb734349cd4de7c90e1a7964aa Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 11 Apr 2024 14:44:28 -0700 Subject: [PATCH 27/42] pyramid --- .../tests/test_automatic.py | 13 +++++++++---- .../tests/test_programmatic.py | 1 + .../instrumentation/requests/__init__.py | 5 ++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py index 2c3ec85e18..4715e0b461 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py +++ b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py @@ -18,6 +18,10 @@ from pyramid.config import Configurator from opentelemetry import trace +from opentelemetry.instrumentation._semconv import ( + _server_active_requests_count_attrs_old, + _server_duration_attrs_old, +) from opentelemetry.instrumentation.pyramid import PyramidInstrumentor from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, @@ -31,8 +35,6 @@ OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE, - _active_requests_count_attrs, - _duration_attrs, ) # pylint: disable=import-error @@ -43,8 +45,8 @@ "http.server.duration", ] _recommended_attrs = { - "http.server.active_requests": _active_requests_count_attrs, - "http.server.duration": _duration_attrs, + "http.server.active_requests": _server_active_requests_count_attrs_old, + "http.server.duration": _server_duration_attrs_old, } @@ -213,6 +215,7 @@ def test_basic_metric_success(self): "http.server_name": "localhost", "net.host.port": 80, "http.status_code": 200, + "net.host.name": "localhost", } expected_requests_count_attributes = { "http.method": "GET", @@ -220,6 +223,8 @@ def test_basic_metric_success(self): "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "localhost", + "net.host.name": "localhost", + "net.host.port": 80, } metrics_list = self.memory_metrics_reader.get_metrics_data() for metric in ( diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py index 478eab1937..c566c301d8 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py +++ b/instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py @@ -37,6 +37,7 @@ def expected_attributes(override_attributes): SpanAttributes.HTTP_SERVER_NAME: "localhost", SpanAttributes.HTTP_SCHEME: "http", SpanAttributes.NET_HOST_PORT: 80, + SpanAttributes.NET_HOST_NAME: "localhost", SpanAttributes.HTTP_HOST: "localhost", SpanAttributes.HTTP_TARGET: "/", SpanAttributes.HTTP_FLAVOR: "1.1", diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index 54b718f678..0415c80082 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -349,7 +349,10 @@ def get_default_span_name(method): Returns: span name """ - return sanitize_method(method.upper().strip()) + method = sanitize_method(method.upper().strip()) + if method == "_OTHER": + return "HTTP" + return method class RequestsInstrumentor(BaseInstrumentor): From b76af8076870a4b3fbbab8bbd58f1d9e80f522bb Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 11 Apr 2024 14:54:43 -0700 Subject: [PATCH 28/42] flacon --- .../tests/test_falcon.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 06b27f95fb..185bec0072 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -16,7 +16,7 @@ from unittest.mock import Mock, patch import pytest -from falcon import __version__ as _falcon_verison +from falcon import __version__ as _falcon_version from falcon import testing from packaging import version as package_version @@ -135,9 +135,15 @@ def _test_method(self, method): # In falcon>3, NET_PEER_IP is not set to anything by default to # https://github.com/falconry/falcon/blob/5233d0abed977d9dab78ebadf305f5abe2eef07c/falcon/testing/helpers.py#L1168-L1172 # noqa if SpanAttributes.NET_PEER_IP in span.attributes: - self.assertEqual( - span.attributes[SpanAttributes.NET_PEER_IP], "127.0.0.1" - ) + if _falcon_version < 3: + self.assertEqual( + span.attributes[SpanAttributes.NET_PEER_IP], "127.0.0.1" + ) + else: + self.assertEqual( + span.attributes[SpanAttributes.NET_PEER_IP], "/" + ) + self.memory_exporter.clear() def test_404(self): @@ -526,7 +532,7 @@ def test_custom_request_header_not_added_in_internal_span(self): self.assertNotIn(key, span.attributes) @pytest.mark.skipif( - condition=package_version.parse(_falcon_verison) + condition=package_version.parse(_falcon_version) < package_version.parse("2.0.0"), reason="falcon<2 does not implement custom response headers", ) @@ -561,7 +567,7 @@ def test_custom_response_header_added_in_server_span(self): self.assertNotIn(key, span.attributes) @pytest.mark.skipif( - condition=package_version.parse(_falcon_verison) + condition=package_version.parse(_falcon_version) < package_version.parse("2.0.0"), reason="falcon<2 does not implement custom response headers", ) From b28742a6049348f48df2ae88f17657ae5743fc1d Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 11 Apr 2024 15:21:07 -0700 Subject: [PATCH 29/42] fix tests --- .../tests/test_falcon.py | 2 +- .../tests/test_programmatic.py | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 185bec0072..773b060de5 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -135,7 +135,7 @@ def _test_method(self, method): # In falcon>3, NET_PEER_IP is not set to anything by default to # https://github.com/falconry/falcon/blob/5233d0abed977d9dab78ebadf305f5abe2eef07c/falcon/testing/helpers.py#L1168-L1172 # noqa if SpanAttributes.NET_PEER_IP in span.attributes: - if _falcon_version < 3: + if package_version.parse(_falcon_version) < 3: self.assertEqual( span.attributes[SpanAttributes.NET_PEER_IP], "127.0.0.1" ) diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py index 00a37d2339..cb6b849d88 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py +++ b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py @@ -54,6 +54,7 @@ def expected_attributes(override_attributes): SpanAttributes.HTTP_SERVER_NAME: "localhost", SpanAttributes.HTTP_SCHEME: "http", SpanAttributes.NET_HOST_PORT: 80, + SpanAttributes.NET_HOST_NAME: "localhost", SpanAttributes.HTTP_HOST: "localhost", SpanAttributes.HTTP_TARGET: "/", SpanAttributes.HTTP_FLAVOR: "1.1", @@ -358,6 +359,7 @@ def test_basic_metric_success(self): "http.server_name": "localhost", "net.host.port": 80, "http.status_code": 200, + "net.host.name": "localhost", } expected_requests_count_attributes = { "http.method": "GET", @@ -365,6 +367,8 @@ def test_basic_metric_success(self): "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "localhost", + "net.host.name": "localhost", + "net.host.port": 80, } self._assert_basic_metric( expected_duration_attributes, @@ -374,20 +378,23 @@ def test_basic_metric_success(self): def test_basic_metric_nonstandard_http_method_success(self): self.client.open("/hello/756", method="NONSTANDARD") expected_duration_attributes = { - "http.method": "UNKNOWN", + "http.method": "_OTHER", "http.host": "localhost", "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "localhost", "net.host.port": 80, "http.status_code": 405, + "net.host.name": "localhost", } expected_requests_count_attributes = { - "http.method": "UNKNOWN", + "http.method": "_OTHER", "http.host": "localhost", "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "localhost", + "net.host.name": "localhost", + "net.host.port": 80, } self._assert_basic_metric( expected_duration_attributes, @@ -410,6 +417,7 @@ def test_basic_metric_nonstandard_http_method_allowed_success(self): "http.server_name": "localhost", "net.host.port": 80, "http.status_code": 405, + "net.host.name": "localhost", } expected_requests_count_attributes = { "http.method": "NONSTANDARD", @@ -417,6 +425,8 @@ def test_basic_metric_nonstandard_http_method_allowed_success(self): "http.scheme": "http", "http.flavor": "1.1", "http.server_name": "localhost", + "net.host.name": "localhost", + "net.host.port": 80, } self._assert_basic_metric( expected_duration_attributes, From 930860043156c0d098743dfd1636954a0707dbf7 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 11 Apr 2024 15:51:15 -0700 Subject: [PATCH 30/42] falcon --- .../tests/test_falcon.py | 12 +++--------- .../opentelemetry/instrumentation/wsgi/__init__.py | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 773b060de5..0d4511375c 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -135,15 +135,9 @@ def _test_method(self, method): # In falcon>3, NET_PEER_IP is not set to anything by default to # https://github.com/falconry/falcon/blob/5233d0abed977d9dab78ebadf305f5abe2eef07c/falcon/testing/helpers.py#L1168-L1172 # noqa if SpanAttributes.NET_PEER_IP in span.attributes: - if package_version.parse(_falcon_version) < 3: - self.assertEqual( - span.attributes[SpanAttributes.NET_PEER_IP], "127.0.0.1" - ) - else: - self.assertEqual( - span.attributes[SpanAttributes.NET_PEER_IP], "/" - ) - + self.assertEqual( + span.attributes[SpanAttributes.NET_PEER_IP], "127.0.0.1" + ) self.memory_exporter.clear() def test_404(self): diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index bb60fdcbe4..152ac59845 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -358,7 +358,7 @@ def collect_request_attributes( remote_addr = environ.get("REMOTE_ADDR") if remote_addr: - _set_http_peer_ip(result, target, sem_conv_opt_in_mode) + _set_http_peer_ip(result, remote_addr, sem_conv_opt_in_mode) peer_port = environ.get("REMOTE_PORT") if peer_port: From f65ebb4775ab3b99e2aa3945accf8977d207b5b5 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Mon, 15 Apr 2024 11:48:53 -0700 Subject: [PATCH 31/42] wsgi tests --- .../tests/test_requests_integration.py | 1 - .../instrumentation/wsgi/__init__.py | 8 +- .../tests/test_wsgi_middleware.py | 256 ++++++++++++++++-- 3 files changed, 238 insertions(+), 27 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index 81b8995be5..81f32a0514 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -524,7 +524,6 @@ def test_requests_exception_new_semconv(self, *_, **__): self.perform_request(url_with_port) span = self.assert_span() - print(span.attributes) self.assertEqual( span.attributes, { diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 152ac59845..11c7232166 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -214,6 +214,8 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he from opentelemetry import context, trace from opentelemetry.instrumentation._semconv import ( + _OpenTelemetrySemanticConventionStability, + _OpenTelemetryStabilitySignalType, _METRIC_ATTRIBUTES_SERVER_DURATION_NAME, _OpenTelemetryStabilityMode, _SPAN_ATTRIBUTES_ERROR_TYPE, @@ -530,8 +532,12 @@ def __init__( response_hook=None, tracer_provider=None, meter_provider=None, - sem_conv_opt_in_mode: _OpenTelemetryStabilityMode = _OpenTelemetryStabilityMode.DEFAULT, ): + # initialize semantic conventions opt-in if needed + _OpenTelemetrySemanticConventionStability._initialize() + sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode( + _OpenTelemetryStabilitySignalType.HTTP, + ) self.wsgi = wsgi self.tracer = trace.get_tracer( __name__, diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index 5563e21df9..0d2e265e94 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -11,7 +11,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - import sys import unittest import wsgiref.util as wsgiref_util @@ -25,6 +24,9 @@ _server_active_requests_count_attrs_old, _server_duration_attrs_new, _server_duration_attrs_old, + _OTEL_SEMCONV_STABILITY_OPT_IN_KEY, + _OpenTelemetrySemanticConventionStability, + _OpenTelemetryStabilityMode, ) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, @@ -133,21 +135,54 @@ def wsgi_with_repeat_custom_response_headers(environ, start_response): return [b"*"] -_expected_metric_names = [ +_expected_metric_names_old = [ "http.server.active_requests", "http.server.duration", ] +_expected_metric_names_new = [ + "http.server.active_requests", + "http.server.request.duration", +] _recommended_metrics_attrs_old = { "http.server.active_requests": _server_active_requests_count_attrs_old, "http.server.duration": _server_duration_attrs_old, } _recommended_metrics_attrs_new = { "http.server.active_requests": _server_active_requests_count_attrs_new, - "http.server.duration": _server_duration_attrs_new, + "http.server.request.duration": _server_duration_attrs_new, +} +_server_active_requests_count_attrs_both = _server_active_requests_count_attrs_old +_server_active_requests_count_attrs_both.extend(_server_active_requests_count_attrs_new) +_recommended_metrics_attrs_both = { + "http.server.active_requests": _server_active_requests_count_attrs_both, + "http.server.duration": _server_duration_attrs_old, + "http.server.request.duration": _server_duration_attrs_new, } class TestWsgiApplication(WsgiTestBase): + def setUp(self): + super().setUp() + + test_name = "" + if hasattr(self, "_testMethodName"): + test_name = self._testMethodName + sem_conv_mode = "default" + if "new_semconv" in test_name: + sem_conv_mode = "http" + elif "both_semconv" in test_name: + sem_conv_mode = "http/dup" + self.env_patch = mock.patch.dict( + "os.environ", + { + _OTEL_SEMCONV_STABILITY_OPT_IN_KEY: sem_conv_mode, + }, + ) + + _OpenTelemetrySemanticConventionStability._initialized = False + + self.env_patch.start() + def validate_response( self, response, @@ -156,6 +191,8 @@ def validate_response( http_method="GET", span_attributes=None, response_headers=None, + old_sem_conv=True, + new_sem_conv=False, ): while True: try: @@ -181,7 +218,8 @@ def validate_response( self.assertEqual(len(span_list), 1) self.assertEqual(span_list[0].name, span_name) self.assertEqual(span_list[0].kind, trace_api.SpanKind.SERVER) - expected_attributes = { + expected_attributes = {} + expected_attributes_old = { SpanAttributes.HTTP_SERVER_NAME: "127.0.0.1", SpanAttributes.HTTP_SCHEME: "http", SpanAttributes.NET_HOST_PORT: 80, @@ -189,11 +227,26 @@ def validate_response( SpanAttributes.HTTP_FLAVOR: "1.0", SpanAttributes.HTTP_URL: "http://127.0.0.1/", SpanAttributes.HTTP_STATUS_CODE: 200, - SpanAttributes.NET_HOST_NAME: "127.0.0.1", + SpanAttributes.NET_HOST_NAME: "127.0.0.1" + } + expected_attributes_new = { + SpanAttributes.URL_SCHEME: "http", + SpanAttributes.SERVER_PORT: 80, + SpanAttributes.SERVER_ADDRESS: "127.0.0.1", + SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.0", + SpanAttributes.HTTP_RESPONSE_STATUS_CODE: 200, } + if old_sem_conv: + expected_attributes.update(expected_attributes_old) + if new_sem_conv: + expected_attributes.update(expected_attributes_new) + expected_attributes.update(span_attributes or {}) if http_method is not None: - expected_attributes[SpanAttributes.HTTP_METHOD] = http_method + if old_sem_conv: + expected_attributes[SpanAttributes.HTTP_METHOD] = http_method + if new_sem_conv: + expected_attributes[SpanAttributes.HTTP_REQUEST_METHOD] = http_method self.assertEqual(span_list[0].attributes, expected_attributes) def test_basic_wsgi_call(self): @@ -201,6 +254,16 @@ def test_basic_wsgi_call(self): response = app(self.environ, self.start_response) self.validate_response(response) + def test_basic_wsgi_call_new_semconv(self): + app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi) + response = app(self.environ, self.start_response) + self.validate_response(response, old_sem_conv=False, new_sem_conv=True) + + def test_basic_wsgi_call_both_semconv(self): + app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi) + response = app(self.environ, self.start_response) + self.validate_response(response, old_sem_conv=True, new_sem_conv=True) + def test_hooks(self): hook_headers = ( "hook_attr", @@ -294,7 +357,7 @@ def test_wsgi_metrics(self): for scope_metric in resource_metric.scope_metrics: self.assertTrue(len(scope_metric.metrics) != 0) for metric in scope_metric.metrics: - self.assertIn(metric.name, _expected_metric_names) + self.assertIn(metric.name, _expected_metric_names_old) data_points = list(metric.data.data_points) self.assertEqual(len(data_points), 1) for point in data_points: @@ -309,6 +372,69 @@ def test_wsgi_metrics(self): ) self.assertTrue(number_data_point_seen and histogram_data_point_seen) + def test_wsgi_metrics_new_semconv(self): + app = otel_wsgi.OpenTelemetryMiddleware(error_wsgi_unhandled) + self.assertRaises(ValueError, app, self.environ, self.start_response) + self.assertRaises(ValueError, app, self.environ, self.start_response) + self.assertRaises(ValueError, app, self.environ, self.start_response) + metrics_list = self.memory_metrics_reader.get_metrics_data() + number_data_point_seen = False + histogram_data_point_seen = False + + self.assertTrue(len(metrics_list.resource_metrics) != 0) + for resource_metric in metrics_list.resource_metrics: + self.assertTrue(len(resource_metric.scope_metrics) != 0) + for scope_metric in resource_metric.scope_metrics: + self.assertTrue(len(scope_metric.metrics) != 0) + for metric in scope_metric.metrics: + self.assertIn(metric.name, _expected_metric_names_new) + data_points = list(metric.data.data_points) + self.assertEqual(len(data_points), 1) + for point in data_points: + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 3) + histogram_data_point_seen = True + if isinstance(point, NumberDataPoint): + number_data_point_seen = True + for attr in point.attributes: + self.assertIn( + attr, _recommended_metrics_attrs_new[metric.name] + ) + self.assertTrue(number_data_point_seen and histogram_data_point_seen) + + def test_wsgi_metrics_both_semconv(self): + app = otel_wsgi.OpenTelemetryMiddleware(error_wsgi_unhandled) + self.assertRaises(ValueError, app, self.environ, self.start_response) + metrics_list = self.memory_metrics_reader.get_metrics_data() + number_data_point_seen = False + histogram_data_point_seen = False + + self.assertTrue(len(metrics_list.resource_metrics) != 0) + for resource_metric in metrics_list.resource_metrics: + self.assertTrue(len(resource_metric.scope_metrics) != 0) + for scope_metric in resource_metric.scope_metrics: + self.assertTrue(len(scope_metric.metrics) != 0) + for metric in scope_metric.metrics: + if metric.unit == "ms": + self.assertEqual(metric.name, "http.server.duration") + elif metric.unit == "s": + self.assertEqual(metric.name, "http.server.request.duration") + else: + self.assertEqual(metric.name, "http.server.active_requests") + data_points = list(metric.data.data_points) + self.assertEqual(len(data_points), 1) + for point in data_points: + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 1) + histogram_data_point_seen = True + if isinstance(point, NumberDataPoint): + number_data_point_seen = True + for attr in point.attributes: + self.assertIn( + attr, _recommended_metrics_attrs_both[metric.name] + ) + self.assertTrue(number_data_point_seen and histogram_data_point_seen) + def test_nonstandard_http_method(self): self.environ["REQUEST_METHOD"] = "NONSTANDARD" app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi) @@ -364,31 +490,76 @@ def test_request_attributes(self): }, ) - def validate_url(self, expected_url, raw=False, has_host=True): + def test_request_attributes_new_semconv(self): + self.environ["QUERY_STRING"] = "foo=bar" + self.environ["REQUEST_URI"] = "http://127.0.0.1/?foo=bar" + + attrs = otel_wsgi.collect_request_attributes( + self.environ, + _OpenTelemetryStabilityMode.HTTP, + ) + self.assertDictEqual( + attrs, + { + SpanAttributes.HTTP_REQUEST_METHOD: "GET", + SpanAttributes.SERVER_ADDRESS: "127.0.0.1", + SpanAttributes.SERVER_PORT: 80, + SpanAttributes.URL_SCHEME: "http", + SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.0", + SpanAttributes.URL_PATH: "/", + SpanAttributes.URL_QUERY: "foo=bar", + }, + ) + + def validate_url(self, expected_url, raw=False, has_host=True, old_semconv=True, new_semconv=False): parts = urlsplit(expected_url) - expected = { + expected_old = { SpanAttributes.HTTP_SCHEME: parts.scheme, SpanAttributes.NET_HOST_PORT: parts.port or (80 if parts.scheme == "http" else 443), SpanAttributes.HTTP_SERVER_NAME: parts.hostname, # Not true in the general case, but for all tests. } - if raw: - expected[SpanAttributes.HTTP_TARGET] = expected_url.split( - parts.netloc, 1 - )[1] - else: - expected[SpanAttributes.HTTP_URL] = expected_url - if has_host: - expected[SpanAttributes.HTTP_HOST] = parts.hostname + expected_new = { + SpanAttributes.URL_SCHEME: parts.scheme, + SpanAttributes.SERVER_PORT: parts.port + or (80 if parts.scheme == "http" else 443), + SpanAttributes.SERVER_ADDRESS: parts.hostname, + SpanAttributes.URL_PATH: parts.path, + SpanAttributes.URL_QUERY: parts.query, + } + if old_semconv: + if raw: + expected_old[SpanAttributes.HTTP_TARGET] = expected_url.split( + parts.netloc, 1 + )[1] + else: + expected_old[SpanAttributes.HTTP_URL] = expected_url + if has_host: + expected_old[SpanAttributes.HTTP_HOST] = parts.hostname + if new_semconv: + if raw: + expected_new[SpanAttributes.URL_PATH] = expected_url.split( + parts.path, 1 + )[1] + if parts.query: + expected_new[SpanAttributes.URL_QUERY] = expected_url.split( + parts.query, 1 + )[1] + else: + expected_new[SpanAttributes.HTTP_URL] = expected_url + if has_host: + expected_new[SpanAttributes.SERVER_ADDRESS] = parts.hostname attrs = otel_wsgi.collect_request_attributes(self.environ) self.assertGreaterEqual( - attrs.items(), expected.items(), expected_url + " expected." + attrs.items(), expected_old.items(), expected_url + " expected." ) def test_request_attributes_with_partial_raw_uri(self): - self.environ["RAW_URI"] = "/#top" - self.validate_url("http://127.0.0.1/#top", raw=True) + self.environ["RAW_URI"] = "/?foo=bar/#top" + self.validate_url("http://127.0.0.1/?foo=bar/#top", raw=True) + self.validate_url("http://127.0.0.1/?foo=bar/#top", raw=True, old_semconv=False, new_semconv=True) + self.validate_url("http://127.0.0.1/?foo=bar/#top", raw=True, old_semconv=True, new_semconv=True) def test_request_attributes_with_partial_raw_uri_and_nonstandard_port( self, @@ -397,18 +568,26 @@ def test_request_attributes_with_partial_raw_uri_and_nonstandard_port( del self.environ["HTTP_HOST"] self.environ["SERVER_PORT"] = "8080" self.validate_url("http://127.0.0.1:8080/?", raw=True, has_host=False) + self.validate_url("http://127.0.0.1:8080/?", raw=True, has_host=False, old_semconv=False, new_semconv=True) + self.validate_url("http://127.0.0.1:8080/?", raw=True, has_host=False, old_semconv=True, new_semconv=True) def test_https_uri_port(self): del self.environ["HTTP_HOST"] self.environ["SERVER_PORT"] = "443" self.environ["wsgi.url_scheme"] = "https" self.validate_url("https://127.0.0.1/", has_host=False) + self.validate_url("https://127.0.0.1/", has_host=False, old_semconv=False, new_semconv=True) + self.validate_url("https://127.0.0.1/", has_host=False, old_semconv=True, new_semconv=True) self.environ["SERVER_PORT"] = "8080" self.validate_url("https://127.0.0.1:8080/", has_host=False) + self.validate_url("https://127.0.0.1:8080/", has_host=False, old_semconv=False, new_semconv=True) + self.validate_url("https://127.0.0.1:8080/", has_host=False, old_semconv=True, new_semconv=True) self.environ["SERVER_PORT"] = "80" self.validate_url("https://127.0.0.1:80/", has_host=False) + self.validate_url("https://127.0.0.1:80/", has_host=False, old_semconv=False, new_semconv=True) + self.validate_url("https://127.0.0.1:80/", has_host=False, old_semconv=True, new_semconv=True) def test_http_uri_port(self): del self.environ["HTTP_HOST"] @@ -459,29 +638,56 @@ def test_request_attributes_with_full_request_uri(self): self.environ["REQUEST_METHOD"] = "CONNECT" self.environ[ "REQUEST_URI" - ] = "127.0.0.1:8080" # Might happen in a CONNECT request - expected = { + ] = "127.0.0.1:8080/?foo=bar" # Might happen in a CONNECT request + expected_old = { SpanAttributes.HTTP_HOST: "127.0.0.1:8080", - SpanAttributes.HTTP_TARGET: "127.0.0.1:8080", + SpanAttributes.HTTP_TARGET: "127.0.0.1:8080/?foo=bar", + } + expected_new = { + SpanAttributes.URL_PATH: "127.0.0.1:8080/", + SpanAttributes.URL_QUERY: "foo=bar", } self.assertGreaterEqual( otel_wsgi.collect_request_attributes(self.environ).items(), - expected.items(), + expected_old.items(), + ) + self.assertGreaterEqual( + otel_wsgi.collect_request_attributes( + self.environ, + _OpenTelemetryStabilityMode.HTTP, + ).items(), + expected_new.items(), ) def test_http_user_agent_attribute(self): self.environ["HTTP_USER_AGENT"] = "test-useragent" expected = {SpanAttributes.HTTP_USER_AGENT: "test-useragent"} + expected_new = {SpanAttributes.USER_AGENT_ORIGINAL: "test-useragent"} self.assertGreaterEqual( otel_wsgi.collect_request_attributes(self.environ).items(), expected.items(), ) + self.assertGreaterEqual( + otel_wsgi.collect_request_attributes( + self.environ, + _OpenTelemetryStabilityMode.HTTP, + ).items(), + expected_new.items(), + ) def test_response_attributes(self): otel_wsgi.add_response_attributes(self.span, "404 Not Found", {}) + otel_wsgi.add_response_attributes( + self.span, + "404 Not Found", + {}, + sem_conv_opt_in_mode=_OpenTelemetryStabilityMode.HTTP, + ) expected = (mock.call(SpanAttributes.HTTP_STATUS_CODE, 404),) - self.assertEqual(self.span.set_attribute.call_count, len(expected)) + expected_new = (mock.call(SpanAttributes.HTTP_RESPONSE_STATUS_CODE, 404),) + self.assertEqual(self.span.set_attribute.call_count, 2) self.span.set_attribute.assert_has_calls(expected, any_order=True) + self.span.set_attribute.assert_has_calls(expected_new, any_order=True) def test_credential_removal(self): self.environ["HTTP_HOST"] = "username:password@mock" From fa0934560be4bd118edb7e68e816151affc2991d Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Mon, 15 Apr 2024 14:44:54 -0700 Subject: [PATCH 32/42] Update CHANGELOG.md --- CHANGELOG.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfdd5ac570..739001f447 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## Unreleased -- `opentelemetry-sdk-extension-aws` Register AWS resource detectors under the - `opentelemetry_resource_detector` entry point - ([#2382](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2382)) ### Breaking changes @@ -31,8 +28,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- `opentelemetry-instrumentation-flask`, `opentelemetry-instrumentation-wsgi` Implement new semantic convention opt-in with stable http semantic conventions - ([#2002](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2002)) +- `opentelemetry-sdk-extension-aws` Register AWS resource detectors under the `opentelemetry_resource_detector` entry point + ([#2382](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2382)) +- `opentelemetry-instrumentation-wsgi` Implement new semantic convention opt-in with stable http semantic conventions + ([#2425](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2425)) +- `opentelemetry-instrumentation-threading` Initial release for threading + ([#2253](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2253)) ## Version 1.24.0/0.45b0 (2024-03-28) From 35d24e824ae768a947ea18634ec1bc768f192deb Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Wed, 17 Apr 2024 10:26:53 -0700 Subject: [PATCH 33/42] name --- .../instrumentation/requests/__init__.py | 8 ++--- .../instrumentation/wsgi/__init__.py | 16 +++++----- .../tests/test_wsgi_middleware.py | 10 +++---- .../opentelemetry/instrumentation/_semconv.py | 30 +++++++++---------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index 0415c80082..ee2d8b23d1 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -68,7 +68,7 @@ _filter_semconv_duration_attrs, _get_schema_url, _OpenTelemetrySemanticConventionStability, - _OpenTelemetryStabilityMode, + _HTTPStabilityMode, _OpenTelemetryStabilitySignalType, _report_new, _report_old, @@ -119,7 +119,7 @@ def _instrument( request_hook: _RequestHookT = None, response_hook: _ResponseHookT = None, excluded_urls: ExcludeList = None, - sem_conv_opt_in_mode: _OpenTelemetryStabilityMode = _OpenTelemetryStabilityMode.DEFAULT, + sem_conv_opt_in_mode: _HTTPStabilityMode = _HTTPStabilityMode.DEFAULT, ): """Enables tracing of all requests calls that go through :code:`requests.session.Session.request` (this includes @@ -290,7 +290,7 @@ def get_or_create_headers(): metric_labels, _client_duration_attrs_old, _client_duration_attrs_new, - _OpenTelemetryStabilityMode.DEFAULT + _HTTPStabilityMode.DEFAULT ) duration_histogram_old.record( max(round(elapsed_time * 1000), 0), @@ -301,7 +301,7 @@ def get_or_create_headers(): metric_labels, _client_duration_attrs_old, _client_duration_attrs_new, - _OpenTelemetryStabilityMode.HTTP + _HTTPStabilityMode.HTTP ) duration_histogram_new.record( elapsed_time, attributes=duration_attrs_new diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 11c7232166..021aa60871 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -217,7 +217,7 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he _OpenTelemetrySemanticConventionStability, _OpenTelemetryStabilitySignalType, _METRIC_ATTRIBUTES_SERVER_DURATION_NAME, - _OpenTelemetryStabilityMode, + _HTTPStabilityMode, _SPAN_ATTRIBUTES_ERROR_TYPE, _get_schema_url, _filter_semconv_active_request_count_attr, @@ -305,7 +305,7 @@ def setifnotnone(dic, key, value): def collect_request_attributes( environ, - sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, + sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT, ): """Collects HTTP request attributes from the PEP3333-conforming WSGI environ and returns a dictionary to be used as span creation attributes. @@ -446,7 +446,7 @@ def _parse_status_code(resp_status): return None -def _parse_active_request_count_attrs(req_attrs, sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT): +def _parse_active_request_count_attrs(req_attrs, sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT): return _filter_semconv_active_request_count_attr( req_attrs, _server_active_requests_count_attrs_old, @@ -455,7 +455,7 @@ def _parse_active_request_count_attrs(req_attrs, sem_conv_opt_in_mode = _OpenTel ) -def _parse_duration_attrs(req_attrs, sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT): +def _parse_duration_attrs(req_attrs, sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT): return _filter_semconv_duration_attrs( req_attrs, _server_duration_attrs_old, @@ -469,7 +469,7 @@ def add_response_attributes( start_response_status, response_headers, duration_attrs = None, - sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, + sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT, ): # pylint: disable=unused-argument """Adds HTTP response attributes to span using the arguments passed to a PEP3333-conforming start_response callable. @@ -648,7 +648,7 @@ def __call__(self, environ, start_response): if span.is_recording(): if _report_new(self._sem_conv_opt_in_mode): span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, type(ex).__qualname__ ) - span.set_status(Status(StatusCode.ERROR, str(ex))) + span.set_status(Status(StatusCode.ERROR, str(ex))) span.end() if token is not None: context.detach(token) @@ -656,10 +656,10 @@ def __call__(self, environ, start_response): finally: duration_s = default_timer() - start if self.duration_histogram_old: - duration_attrs_old = _parse_duration_attrs(req_attrs, _OpenTelemetryStabilityMode.DEFAULT) + duration_attrs_old = _parse_duration_attrs(req_attrs, _HTTPStabilityMode.DEFAULT) self.duration_histogram_old.record(max(round(duration_s * 1000), 0), duration_attrs_old) if self.duration_histogram_new: - duration_attrs_new = _parse_duration_attrs(req_attrs, _OpenTelemetryStabilityMode.HTTP) + duration_attrs_new = _parse_duration_attrs(req_attrs, _HTTPStabilityMode.HTTP) self.duration_histogram_new.record(max(round(duration_s * 1000), 0), duration_attrs_new) self.active_requests_counter.add(-1, active_requests_count_attrs) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index 0d2e265e94..73051fa70a 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -26,7 +26,7 @@ _server_duration_attrs_old, _OTEL_SEMCONV_STABILITY_OPT_IN_KEY, _OpenTelemetrySemanticConventionStability, - _OpenTelemetryStabilityMode, + _HTTPStabilityMode, ) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, @@ -496,7 +496,7 @@ def test_request_attributes_new_semconv(self): attrs = otel_wsgi.collect_request_attributes( self.environ, - _OpenTelemetryStabilityMode.HTTP, + _HTTPStabilityMode.HTTP, ) self.assertDictEqual( attrs, @@ -654,7 +654,7 @@ def test_request_attributes_with_full_request_uri(self): self.assertGreaterEqual( otel_wsgi.collect_request_attributes( self.environ, - _OpenTelemetryStabilityMode.HTTP, + _HTTPStabilityMode.HTTP, ).items(), expected_new.items(), ) @@ -670,7 +670,7 @@ def test_http_user_agent_attribute(self): self.assertGreaterEqual( otel_wsgi.collect_request_attributes( self.environ, - _OpenTelemetryStabilityMode.HTTP, + _HTTPStabilityMode.HTTP, ).items(), expected_new.items(), ) @@ -681,7 +681,7 @@ def test_response_attributes(self): self.span, "404 Not Found", {}, - sem_conv_opt_in_mode=_OpenTelemetryStabilityMode.HTTP, + sem_conv_opt_in_mode=_HTTPStabilityMode.HTTP, ) expected = (mock.call(SpanAttributes.HTTP_STATUS_CODE, 404),) expected_new = (mock.call(SpanAttributes.HTTP_RESPONSE_STATUS_CODE, 404),) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index 09f4a1c90b..618d8e225e 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -91,7 +91,7 @@ class _OpenTelemetryStabilitySignalType: HTTP = "http" -class _OpenTelemetryStabilityMode(Enum): +class _HTTPStabilityMode(Enum): # http - emit the new, stable HTTP and networking conventions ONLY HTTP = "http" # http/dup - emit both the old and the stable HTTP and networking conventions @@ -101,11 +101,11 @@ class _OpenTelemetryStabilityMode(Enum): def _report_new(mode): - return mode.name != _OpenTelemetryStabilityMode.DEFAULT.name + return mode.name != _HTTPStabilityMode.DEFAULT.name def _report_old(mode): - return mode.name != _OpenTelemetryStabilityMode.HTTP.name + return mode.name != _HTTPStabilityMode.HTTP.name class _OpenTelemetrySemanticConventionStability: @@ -123,17 +123,17 @@ def _initialize(cls): opt_in_list = [] if opt_in: opt_in_list = [s.strip() for s in opt_in.split(",")] - http_opt_in = _OpenTelemetryStabilityMode.DEFAULT + http_opt_in = _HTTPStabilityMode.DEFAULT if opt_in_list: # Process http opt-in # http/dup takes priority over http if ( - _OpenTelemetryStabilityMode.HTTP_DUP.value + _HTTPStabilityMode.HTTP_DUP.value in opt_in_list ): - http_opt_in = _OpenTelemetryStabilityMode.HTTP_DUP - elif _OpenTelemetryStabilityMode.HTTP.value in opt_in_list: - http_opt_in = _OpenTelemetryStabilityMode.HTTP + http_opt_in = _HTTPStabilityMode.HTTP_DUP + elif _HTTPStabilityMode.HTTP.value in opt_in_list: + http_opt_in = _HTTPStabilityMode.HTTP _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING[ _OpenTelemetryStabilitySignalType.HTTP ] = http_opt_in @@ -144,9 +144,9 @@ def _initialize(cls): def _get_opentelemetry_stability_opt_in_mode( cls, signal_type: _OpenTelemetryStabilitySignalType, - ) -> _OpenTelemetryStabilityMode: + ) -> _HTTPStabilityMode: return _OpenTelemetrySemanticConventionStability._OTEL_SEMCONV_STABILITY_SIGNAL_MAPPING.get( - signal_type, _OpenTelemetryStabilityMode.DEFAULT + signal_type, _HTTPStabilityMode.DEFAULT ) @@ -154,13 +154,13 @@ def _filter_semconv_duration_attrs( attrs, old_attrs, new_attrs, - sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, + sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT, ): filtered_attrs = {} # duration is two different metrics depending on sem_conv_opt_in_mode, so no DUP attributes allowed_attributes = ( new_attrs - if sem_conv_opt_in_mode == _OpenTelemetryStabilityMode.HTTP + if sem_conv_opt_in_mode == _HTTPStabilityMode.HTTP else old_attrs ) for key, val in attrs.items(): @@ -173,7 +173,7 @@ def _filter_semconv_active_request_count_attr( attrs, old_attrs, new_attrs, - sem_conv_opt_in_mode = _OpenTelemetryStabilityMode.DEFAULT, + sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT, ): filtered_attrs = {} if _report_old(sem_conv_opt_in_mode): @@ -364,7 +364,7 @@ def _set_status(span, metrics_attributes, status_code_str, status_code, sem_conv # Get schema version based off of opt-in mode -def _get_schema_url(mode: _OpenTelemetryStabilityMode) -> str: - if mode is _OpenTelemetryStabilityMode.DEFAULT: +def _get_schema_url(mode: _HTTPStabilityMode) -> str: + if mode is _HTTPStabilityMode.DEFAULT: return "https://opentelemetry.io/schemas/1.11.0" return SpanAttributes.SCHEMA_URL From 99be783ddfcc9b2fcbceb88f51eef3cee7a3eca0 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 18 Apr 2024 10:39:32 -0700 Subject: [PATCH 34/42] fixes --- .../opentelemetry/instrumentation/wsgi/__init__.py | 7 ++++--- .../tests/test_wsgi_middleware.py | 11 ++++------- .../src/opentelemetry/instrumentation/_semconv.py | 11 +++++++---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 021aa60871..aa4a939479 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -645,8 +645,9 @@ def __call__(self, environ, start_response): iterable = self.wsgi(environ, start_response) return _end_span_after_iterating(iterable, span, token) except Exception as ex: - if span.is_recording(): - if _report_new(self._sem_conv_opt_in_mode): + if _report_new(self._sem_conv_opt_in_mode): + req_attrs[_SPAN_ATTRIBUTES_ERROR_TYPE] = type(ex).__qualname__ + if span.is_recording(): span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, type(ex).__qualname__ ) span.set_status(Status(StatusCode.ERROR, str(ex))) span.end() @@ -660,7 +661,7 @@ def __call__(self, environ, start_response): self.duration_histogram_old.record(max(round(duration_s * 1000), 0), duration_attrs_old) if self.duration_histogram_new: duration_attrs_new = _parse_duration_attrs(req_attrs, _HTTPStabilityMode.HTTP) - self.duration_histogram_new.record(max(round(duration_s * 1000), 0), duration_attrs_new) + self.duration_histogram_new.record(max(duration_s, 0), duration_attrs_new) self.active_requests_counter.add(-1, active_requests_count_attrs) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index 73051fa70a..b143f2b75a 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -230,7 +230,6 @@ def validate_response( SpanAttributes.NET_HOST_NAME: "127.0.0.1" } expected_attributes_new = { - SpanAttributes.URL_SCHEME: "http", SpanAttributes.SERVER_PORT: 80, SpanAttributes.SERVER_ADDRESS: "127.0.0.1", SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.0", @@ -504,7 +503,6 @@ def test_request_attributes_new_semconv(self): SpanAttributes.HTTP_REQUEST_METHOD: "GET", SpanAttributes.SERVER_ADDRESS: "127.0.0.1", SpanAttributes.SERVER_PORT: 80, - SpanAttributes.URL_SCHEME: "http", SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.0", SpanAttributes.URL_PATH: "/", SpanAttributes.URL_QUERY: "foo=bar", @@ -520,7 +518,6 @@ def validate_url(self, expected_url, raw=False, has_host=True, old_semconv=True, SpanAttributes.HTTP_SERVER_NAME: parts.hostname, # Not true in the general case, but for all tests. } expected_new = { - SpanAttributes.URL_SCHEME: parts.scheme, SpanAttributes.SERVER_PORT: parts.port or (80 if parts.scheme == "http" else 443), SpanAttributes.SERVER_ADDRESS: parts.hostname, @@ -638,14 +635,14 @@ def test_request_attributes_with_full_request_uri(self): self.environ["REQUEST_METHOD"] = "CONNECT" self.environ[ "REQUEST_URI" - ] = "127.0.0.1:8080/?foo=bar" # Might happen in a CONNECT request + ] = "http://docs.python.org:80/3/library/urllib.parse.html?highlight=params#url-parsing" # Might happen in a CONNECT request expected_old = { SpanAttributes.HTTP_HOST: "127.0.0.1:8080", - SpanAttributes.HTTP_TARGET: "127.0.0.1:8080/?foo=bar", + SpanAttributes.HTTP_TARGET: "http://docs.python.org:80/3/library/urllib.parse.html?highlight=params#url-parsing", } expected_new = { - SpanAttributes.URL_PATH: "127.0.0.1:8080/", - SpanAttributes.URL_QUERY: "foo=bar", + SpanAttributes.URL_PATH: "/3/library/urllib.parse.html", + SpanAttributes.URL_QUERY: "highlight=params", } self.assertGreaterEqual( otel_wsgi.collect_request_attributes(self.environ).items(), diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index 618d8e225e..1b6c12194a 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -46,7 +46,8 @@ SpanAttributes.NETWORK_PROTOCOL_VERSION, SpanAttributes.SERVER_ADDRESS, SpanAttributes.SERVER_PORT, - SpanAttributes.URL_SCHEME, + # TODO: Support opt-in for scheme in new semconv + # SpanAttributes.URL_SCHEME, ] _server_duration_attrs_old = [ @@ -66,7 +67,8 @@ SpanAttributes.HTTP_RESPONSE_STATUS_CODE, SpanAttributes.HTTP_ROUTE, SpanAttributes.NETWORK_PROTOCOL_VERSION, - SpanAttributes.URL_SCHEME, + # TODO: Support opt-in for scheme in new semconv + # SpanAttributes.URL_SCHEME, ] _server_active_requests_count_attrs_old = [ @@ -237,8 +239,9 @@ def _set_http_url(result, url, sem_conv_opt_in_mode): def _set_http_scheme(result, scheme, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_SCHEME, scheme) - if _report_new(sem_conv_opt_in_mode): - set_string_attribute(result, SpanAttributes.URL_SCHEME, scheme) + # TODO: Support opt-in for scheme in new semconv + # if _report_new(sem_conv_opt_in_mode): + # set_string_attribute(result, SpanAttributes.URL_SCHEME, scheme) def _set_http_host(result, host, sem_conv_opt_in_mode): From 6dfa6318d7a724c60ec30416b6a3ffec8ff095e6 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 18 Apr 2024 11:04:46 -0700 Subject: [PATCH 35/42] lint --- .../tests/test_falcon.py | 2 +- .../instrumentation/requests/__init__.py | 4 +- .../tests/test_requests_integration.py | 2 - .../instrumentation/wsgi/__init__.py | 77 +++++++--- .../tests/test_wsgi_middleware.py | 131 ++++++++++++++---- .../opentelemetry/instrumentation/_semconv.py | 61 +++++--- 6 files changed, 200 insertions(+), 77 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 0d4511375c..1ddecc3f21 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -29,7 +29,7 @@ ) from opentelemetry.instrumentation._semconv import ( _server_active_requests_count_attrs_old, - _server_duration_attrs_old + _server_duration_attrs_old, ) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index ee2d8b23d1..8987c38764 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -290,7 +290,7 @@ def get_or_create_headers(): metric_labels, _client_duration_attrs_old, _client_duration_attrs_new, - _HTTPStabilityMode.DEFAULT + _HTTPStabilityMode.DEFAULT, ) duration_histogram_old.record( max(round(elapsed_time * 1000), 0), @@ -301,7 +301,7 @@ def get_or_create_headers(): metric_labels, _client_duration_attrs_old, _client_duration_attrs_new, - _HTTPStabilityMode.HTTP + _HTTPStabilityMode.HTTP, ) duration_histogram_new.record( elapsed_time, attributes=duration_attrs_new diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index 81f32a0514..7dbcc0af05 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -729,7 +729,6 @@ def test_basic_metric_new_semconv(self): SpanAttributes.SERVER_PORT: 8000, SpanAttributes.HTTP_REQUEST_METHOD: "GET", SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.1", - SpanAttributes.URL_SCHEME: "http", } for ( resource_metrics @@ -766,7 +765,6 @@ def test_basic_metric_both_semconv(self): SpanAttributes.SERVER_PORT: 8000, SpanAttributes.HTTP_REQUEST_METHOD: "GET", SpanAttributes.NETWORK_PROTOCOL_VERSION: "1.1", - SpanAttributes.URL_SCHEME: "http", } for ( diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index aa4a939479..b8b9796a29 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -304,9 +304,9 @@ def setifnotnone(dic, key, value): def collect_request_attributes( - environ, - sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT, - ): + environ, + sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT, +): """Collects HTTP request attributes from the PEP3333-conforming WSGI environ and returns a dictionary to be used as span creation attributes. """ @@ -314,9 +314,7 @@ def collect_request_attributes( _set_http_method( result, environ.get("REQUEST_METHOD", ""), - sanitize_method( - environ.get("REQUEST_METHOD", "") - ), + sanitize_method(environ.get("REQUEST_METHOD", "")), sem_conv_opt_in_mode, ) # old semconv v1.12.0 @@ -344,7 +342,6 @@ def collect_request_attributes( sem_conv_opt_in_mode, ) - target = environ.get("RAW_URI") if target is None: # Note: `"" or None is None` target = environ.get("REQUEST_URI") @@ -361,14 +358,16 @@ def collect_request_attributes( remote_addr = environ.get("REMOTE_ADDR") if remote_addr: _set_http_peer_ip(result, remote_addr, sem_conv_opt_in_mode) - + peer_port = environ.get("REMOTE_PORT") if peer_port: _set_http_peer_port_server(result, peer_port, sem_conv_opt_in_mode) remote_host = environ.get("REMOTE_HOST") if remote_host and remote_host != remote_addr: - _set_http_net_peer_name_server(result, remote_host, sem_conv_opt_in_mode) + _set_http_net_peer_name_server( + result, remote_host, sem_conv_opt_in_mode + ) user_agent = environ.get("HTTP_USER_AGENT") if user_agent is not None and len(user_agent) > 0: @@ -446,7 +445,9 @@ def _parse_status_code(resp_status): return None -def _parse_active_request_count_attrs(req_attrs, sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT): +def _parse_active_request_count_attrs( + req_attrs, sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT +): return _filter_semconv_active_request_count_attr( req_attrs, _server_active_requests_count_attrs_old, @@ -455,7 +456,9 @@ def _parse_active_request_count_attrs(req_attrs, sem_conv_opt_in_mode = _HTTPSta ) -def _parse_duration_attrs(req_attrs, sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT): +def _parse_duration_attrs( + req_attrs, sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT +): return _filter_semconv_duration_attrs( req_attrs, _server_duration_attrs_old, @@ -468,8 +471,8 @@ def add_response_attributes( span, start_response_status, response_headers, - duration_attrs = None, - sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT, + duration_attrs=None, + sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT, ): # pylint: disable=unused-argument """Adds HTTP response attributes to span using the arguments passed to a PEP3333-conforming start_response callable. @@ -485,8 +488,14 @@ def add_response_attributes( status_code = -1 if duration_attrs is None: duration_attrs = {} - _set_status(span, duration_attrs, status_code_str, status_code, sem_conv_opt_in_mode) - + _set_status( + span, + duration_attrs, + status_code_str, + status_code, + sem_conv_opt_in_mode, + ) + def get_default_span_name(environ): """ @@ -578,11 +587,21 @@ def __init__( @staticmethod def _create_start_response( - span, start_response, response_hook, duration_attrs, sem_conv_opt_in_mode, + span, + start_response, + response_hook, + duration_attrs, + sem_conv_opt_in_mode, ): @functools.wraps(start_response) def _start_response(status, response_headers, *args, **kwargs): - add_response_attributes(span, status, response_headers, duration_attrs, sem_conv_opt_in_mode) + add_response_attributes( + span, + status, + response_headers, + duration_attrs, + sem_conv_opt_in_mode, + ) if span.is_recording() and span.kind == trace.SpanKind.SERVER: custom_attributes = collect_custom_response_headers_attributes( response_headers @@ -603,7 +622,9 @@ def __call__(self, environ, start_response): environ: A WSGI environment. start_response: The WSGI start_response callable. """ - req_attrs = collect_request_attributes(environ, self._sem_conv_opt_in_mode) + req_attrs = collect_request_attributes( + environ, self._sem_conv_opt_in_mode + ) active_requests_count_attrs = _parse_active_request_count_attrs( req_attrs, self._sem_conv_opt_in_mode, @@ -648,7 +669,9 @@ def __call__(self, environ, start_response): if _report_new(self._sem_conv_opt_in_mode): req_attrs[_SPAN_ATTRIBUTES_ERROR_TYPE] = type(ex).__qualname__ if span.is_recording(): - span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, type(ex).__qualname__ ) + span.set_attribute( + _SPAN_ATTRIBUTES_ERROR_TYPE, type(ex).__qualname__ + ) span.set_status(Status(StatusCode.ERROR, str(ex))) span.end() if token is not None: @@ -657,11 +680,19 @@ def __call__(self, environ, start_response): finally: duration_s = default_timer() - start if self.duration_histogram_old: - duration_attrs_old = _parse_duration_attrs(req_attrs, _HTTPStabilityMode.DEFAULT) - self.duration_histogram_old.record(max(round(duration_s * 1000), 0), duration_attrs_old) + duration_attrs_old = _parse_duration_attrs( + req_attrs, _HTTPStabilityMode.DEFAULT + ) + self.duration_histogram_old.record( + max(round(duration_s * 1000), 0), duration_attrs_old + ) if self.duration_histogram_new: - duration_attrs_new = _parse_duration_attrs(req_attrs, _HTTPStabilityMode.HTTP) - self.duration_histogram_new.record(max(duration_s, 0), duration_attrs_new) + duration_attrs_new = _parse_duration_attrs( + req_attrs, _HTTPStabilityMode.HTTP + ) + self.duration_histogram_new.record( + max(duration_s, 0), duration_attrs_new + ) self.active_requests_counter.add(-1, active_requests_count_attrs) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index b143f2b75a..f232199dbd 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -151,8 +151,12 @@ def wsgi_with_repeat_custom_response_headers(environ, start_response): "http.server.active_requests": _server_active_requests_count_attrs_new, "http.server.request.duration": _server_duration_attrs_new, } -_server_active_requests_count_attrs_both = _server_active_requests_count_attrs_old -_server_active_requests_count_attrs_both.extend(_server_active_requests_count_attrs_new) +_server_active_requests_count_attrs_both = ( + _server_active_requests_count_attrs_old +) +_server_active_requests_count_attrs_both.extend( + _server_active_requests_count_attrs_new +) _recommended_metrics_attrs_both = { "http.server.active_requests": _server_active_requests_count_attrs_both, "http.server.duration": _server_duration_attrs_old, @@ -227,7 +231,7 @@ def validate_response( SpanAttributes.HTTP_FLAVOR: "1.0", SpanAttributes.HTTP_URL: "http://127.0.0.1/", SpanAttributes.HTTP_STATUS_CODE: 200, - SpanAttributes.NET_HOST_NAME: "127.0.0.1" + SpanAttributes.NET_HOST_NAME: "127.0.0.1", } expected_attributes_new = { SpanAttributes.SERVER_PORT: 80, @@ -239,13 +243,15 @@ def validate_response( expected_attributes.update(expected_attributes_old) if new_sem_conv: expected_attributes.update(expected_attributes_new) - + expected_attributes.update(span_attributes or {}) if http_method is not None: if old_sem_conv: expected_attributes[SpanAttributes.HTTP_METHOD] = http_method if new_sem_conv: - expected_attributes[SpanAttributes.HTTP_REQUEST_METHOD] = http_method + expected_attributes[ + SpanAttributes.HTTP_REQUEST_METHOD + ] = http_method self.assertEqual(span_list[0].attributes, expected_attributes) def test_basic_wsgi_call(self): @@ -367,7 +373,8 @@ def test_wsgi_metrics(self): number_data_point_seen = True for attr in point.attributes: self.assertIn( - attr, _recommended_metrics_attrs_old[metric.name] + attr, + _recommended_metrics_attrs_old[metric.name], ) self.assertTrue(number_data_point_seen and histogram_data_point_seen) @@ -397,7 +404,8 @@ def test_wsgi_metrics_new_semconv(self): number_data_point_seen = True for attr in point.attributes: self.assertIn( - attr, _recommended_metrics_attrs_new[metric.name] + attr, + _recommended_metrics_attrs_new[metric.name], ) self.assertTrue(number_data_point_seen and histogram_data_point_seen) @@ -417,9 +425,13 @@ def test_wsgi_metrics_both_semconv(self): if metric.unit == "ms": self.assertEqual(metric.name, "http.server.duration") elif metric.unit == "s": - self.assertEqual(metric.name, "http.server.request.duration") + self.assertEqual( + metric.name, "http.server.request.duration" + ) else: - self.assertEqual(metric.name, "http.server.active_requests") + self.assertEqual( + metric.name, "http.server.active_requests" + ) data_points = list(metric.data.data_points) self.assertEqual(len(data_points), 1) for point in data_points: @@ -430,7 +442,8 @@ def test_wsgi_metrics_both_semconv(self): number_data_point_seen = True for attr in point.attributes: self.assertIn( - attr, _recommended_metrics_attrs_both[metric.name] + attr, + _recommended_metrics_attrs_both[metric.name], ) self.assertTrue(number_data_point_seen and histogram_data_point_seen) @@ -509,7 +522,14 @@ def test_request_attributes_new_semconv(self): }, ) - def validate_url(self, expected_url, raw=False, has_host=True, old_semconv=True, new_semconv=False): + def validate_url( + self, + expected_url, + raw=False, + has_host=True, + old_semconv=True, + new_semconv=False, + ): parts = urlsplit(expected_url) expected_old = { SpanAttributes.HTTP_SCHEME: parts.scheme, @@ -539,9 +559,9 @@ def validate_url(self, expected_url, raw=False, has_host=True, old_semconv=True, parts.path, 1 )[1] if parts.query: - expected_new[SpanAttributes.URL_QUERY] = expected_url.split( - parts.query, 1 - )[1] + expected_new[ + SpanAttributes.URL_QUERY + ] = expected_url.split(parts.query, 1)[1] else: expected_new[SpanAttributes.HTTP_URL] = expected_url if has_host: @@ -555,8 +575,18 @@ def validate_url(self, expected_url, raw=False, has_host=True, old_semconv=True, def test_request_attributes_with_partial_raw_uri(self): self.environ["RAW_URI"] = "/?foo=bar/#top" self.validate_url("http://127.0.0.1/?foo=bar/#top", raw=True) - self.validate_url("http://127.0.0.1/?foo=bar/#top", raw=True, old_semconv=False, new_semconv=True) - self.validate_url("http://127.0.0.1/?foo=bar/#top", raw=True, old_semconv=True, new_semconv=True) + self.validate_url( + "http://127.0.0.1/?foo=bar/#top", + raw=True, + old_semconv=False, + new_semconv=True, + ) + self.validate_url( + "http://127.0.0.1/?foo=bar/#top", + raw=True, + old_semconv=True, + new_semconv=True, + ) def test_request_attributes_with_partial_raw_uri_and_nonstandard_port( self, @@ -565,26 +595,68 @@ def test_request_attributes_with_partial_raw_uri_and_nonstandard_port( del self.environ["HTTP_HOST"] self.environ["SERVER_PORT"] = "8080" self.validate_url("http://127.0.0.1:8080/?", raw=True, has_host=False) - self.validate_url("http://127.0.0.1:8080/?", raw=True, has_host=False, old_semconv=False, new_semconv=True) - self.validate_url("http://127.0.0.1:8080/?", raw=True, has_host=False, old_semconv=True, new_semconv=True) + self.validate_url( + "http://127.0.0.1:8080/?", + raw=True, + has_host=False, + old_semconv=False, + new_semconv=True, + ) + self.validate_url( + "http://127.0.0.1:8080/?", + raw=True, + has_host=False, + old_semconv=True, + new_semconv=True, + ) def test_https_uri_port(self): del self.environ["HTTP_HOST"] self.environ["SERVER_PORT"] = "443" self.environ["wsgi.url_scheme"] = "https" self.validate_url("https://127.0.0.1/", has_host=False) - self.validate_url("https://127.0.0.1/", has_host=False, old_semconv=False, new_semconv=True) - self.validate_url("https://127.0.0.1/", has_host=False, old_semconv=True, new_semconv=True) + self.validate_url( + "https://127.0.0.1/", + has_host=False, + old_semconv=False, + new_semconv=True, + ) + self.validate_url( + "https://127.0.0.1/", + has_host=False, + old_semconv=True, + new_semconv=True, + ) self.environ["SERVER_PORT"] = "8080" self.validate_url("https://127.0.0.1:8080/", has_host=False) - self.validate_url("https://127.0.0.1:8080/", has_host=False, old_semconv=False, new_semconv=True) - self.validate_url("https://127.0.0.1:8080/", has_host=False, old_semconv=True, new_semconv=True) + self.validate_url( + "https://127.0.0.1:8080/", + has_host=False, + old_semconv=False, + new_semconv=True, + ) + self.validate_url( + "https://127.0.0.1:8080/", + has_host=False, + old_semconv=True, + new_semconv=True, + ) self.environ["SERVER_PORT"] = "80" self.validate_url("https://127.0.0.1:80/", has_host=False) - self.validate_url("https://127.0.0.1:80/", has_host=False, old_semconv=False, new_semconv=True) - self.validate_url("https://127.0.0.1:80/", has_host=False, old_semconv=True, new_semconv=True) + self.validate_url( + "https://127.0.0.1:80/", + has_host=False, + old_semconv=False, + new_semconv=True, + ) + self.validate_url( + "https://127.0.0.1:80/", + has_host=False, + old_semconv=True, + new_semconv=True, + ) def test_http_uri_port(self): del self.environ["HTTP_HOST"] @@ -627,8 +699,11 @@ def test_request_attributes_with_faux_scheme_relative_raw_uri(self): def test_request_attributes_pathless(self): self.environ["RAW_URI"] = "" expected = {SpanAttributes.HTTP_TARGET: ""} - self.assertIsNone(otel_wsgi.collect_request_attributes(self.environ) -.get(SpanAttributes.HTTP_TARGET)) + self.assertIsNone( + otel_wsgi.collect_request_attributes(self.environ).get( + SpanAttributes.HTTP_TARGET + ) + ) def test_request_attributes_with_full_request_uri(self): self.environ["HTTP_HOST"] = "127.0.0.1:8080" @@ -681,7 +756,9 @@ def test_response_attributes(self): sem_conv_opt_in_mode=_HTTPStabilityMode.HTTP, ) expected = (mock.call(SpanAttributes.HTTP_STATUS_CODE, 404),) - expected_new = (mock.call(SpanAttributes.HTTP_RESPONSE_STATUS_CODE, 404),) + expected_new = ( + mock.call(SpanAttributes.HTTP_RESPONSE_STATUS_CODE, 404), + ) self.assertEqual(self.span.set_attribute.call_count, 2) self.span.set_attribute.assert_has_calls(expected, any_order=True) self.span.set_attribute.assert_has_calls(expected_new, any_order=True) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index 1b6c12194a..a4a7128327 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -129,10 +129,7 @@ def _initialize(cls): if opt_in_list: # Process http opt-in # http/dup takes priority over http - if ( - _HTTPStabilityMode.HTTP_DUP.value - in opt_in_list - ): + if _HTTPStabilityMode.HTTP_DUP.value in opt_in_list: http_opt_in = _HTTPStabilityMode.HTTP_DUP elif _HTTPStabilityMode.HTTP.value in opt_in_list: http_opt_in = _HTTPStabilityMode.HTTP @@ -156,7 +153,7 @@ def _filter_semconv_duration_attrs( attrs, old_attrs, new_attrs, - sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT, + sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT, ): filtered_attrs = {} # duration is two different metrics depending on sem_conv_opt_in_mode, so no DUP attributes @@ -175,7 +172,7 @@ def _filter_semconv_active_request_count_attr( attrs, old_attrs, new_attrs, - sem_conv_opt_in_mode = _HTTPStabilityMode.DEFAULT, + sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT, ): filtered_attrs = {} if _report_old(sem_conv_opt_in_mode): @@ -250,8 +247,10 @@ def _set_http_host(result, host, sem_conv_opt_in_mode): if _report_new(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.SERVER_ADDRESS, host) + # Client + def _set_http_net_peer_name_client(result, peer_name, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.NET_PEER_NAME, peer_name) @@ -274,8 +273,10 @@ def _set_http_network_protocol_version(result, version, sem_conv_opt_in_mode): result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version ) + # Server + def _set_http_net_host(result, host, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.NET_HOST_NAME, host) @@ -295,13 +296,9 @@ def _set_http_target(result, target, path, query, sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_TARGET, target) if _report_new(sem_conv_opt_in_mode): if path: - set_string_attribute( - result, SpanAttributes.URL_PATH, path - ) + set_string_attribute(result, SpanAttributes.URL_PATH, path) if query: - set_string_attribute( - result, SpanAttributes.URL_QUERY, query - ) + set_string_attribute(result, SpanAttributes.URL_QUERY, query) def _set_http_peer_ip(result, ip, sem_conv_opt_in_mode): @@ -316,13 +313,17 @@ def _set_http_peer_port_server(result, port, sem_conv_opt_in_mode): set_int_attribute(result, SpanAttributes.NET_PEER_PORT, port) if _report_new(sem_conv_opt_in_mode): set_int_attribute(result, SpanAttributes.CLIENT_PORT, port) - + def _set_http_user_agent(result, user_agent, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): - set_string_attribute(result, SpanAttributes.HTTP_USER_AGENT, user_agent) + set_string_attribute( + result, SpanAttributes.HTTP_USER_AGENT, user_agent + ) if _report_new(sem_conv_opt_in_mode): - set_string_attribute(result, SpanAttributes.USER_AGENT_ORIGINAL, user_agent) + set_string_attribute( + result, SpanAttributes.USER_AGENT_ORIGINAL, user_agent + ) def _set_http_net_peer_name_server(result, name, sem_conv_opt_in_mode): @@ -336,11 +337,19 @@ def _set_http_flavor_version(result, version, sem_conv_opt_in_mode): if _report_old(sem_conv_opt_in_mode): set_string_attribute(result, SpanAttributes.HTTP_FLAVOR, version) if _report_new(sem_conv_opt_in_mode): - set_string_attribute(result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version) + set_string_attribute( + result, SpanAttributes.NETWORK_PROTOCOL_VERSION, version + ) -def _set_status(span, metrics_attributes, status_code_str, status_code, sem_conv_opt_in_mode): - if (status_code < 0): +def _set_status( + span, + metrics_attributes, + status_code_str, + status_code, + sem_conv_opt_in_mode, +): + if status_code < 0: if _report_new(sem_conv_opt_in_mode): span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, status_code_str) metrics_attributes[_SPAN_ATTRIBUTES_ERROR_TYPE] = status_code_str @@ -358,11 +367,19 @@ def _set_status(span, metrics_attributes, status_code_str, status_code, sem_conv span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code) metrics_attributes[SpanAttributes.HTTP_STATUS_CODE] = status_code if _report_new(sem_conv_opt_in_mode): - span.set_attribute(SpanAttributes.HTTP_RESPONSE_STATUS_CODE, status_code) - metrics_attributes[SpanAttributes.HTTP_RESPONSE_STATUS_CODE] = status_code + span.set_attribute( + SpanAttributes.HTTP_RESPONSE_STATUS_CODE, status_code + ) + metrics_attributes[ + SpanAttributes.HTTP_RESPONSE_STATUS_CODE + ] = status_code if status == StatusCode.ERROR: - span.set_attribute(_SPAN_ATTRIBUTES_ERROR_TYPE, status_code_str) - metrics_attributes[_SPAN_ATTRIBUTES_ERROR_TYPE] = status_code_str + span.set_attribute( + _SPAN_ATTRIBUTES_ERROR_TYPE, status_code_str + ) + metrics_attributes[ + _SPAN_ATTRIBUTES_ERROR_TYPE + ] = status_code_str span.set_status(Status(status)) From 998b996b4278490861f88a6984e33c3519167257 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 18 Apr 2024 11:30:58 -0700 Subject: [PATCH 36/42] lint --- .../tests/test_falcon.py | 8 ++++---- .../tests/test_programmatic.py | 2 +- .../instrumentation/requests/__init__.py | 2 +- .../tests/test_requests_integration.py | 6 +++--- .../instrumentation/wsgi/__init__.py | 16 +++++++--------- .../tests/test_wsgi_middleware.py | 8 ++++---- .../opentelemetry/instrumentation/_semconv.py | 10 ++++------ 7 files changed, 24 insertions(+), 28 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 1ddecc3f21..bf7f1d4f49 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -21,16 +21,16 @@ from packaging import version as package_version from opentelemetry import trace +from opentelemetry.instrumentation._semconv import ( + _server_active_requests_count_attrs_old, + _server_duration_attrs_old, +) from opentelemetry.instrumentation.falcon import FalconInstrumentor from opentelemetry.instrumentation.propagators import ( TraceResponsePropagator, get_global_response_propagator, set_global_response_propagator, ) -from opentelemetry.instrumentation._semconv import ( - _server_active_requests_count_attrs_old, - _server_duration_attrs_old, -) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, NumberDataPoint, diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py index cb6b849d88..82ca88460c 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py +++ b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py @@ -19,8 +19,8 @@ from opentelemetry import trace from opentelemetry.instrumentation._semconv import ( - _server_duration_attrs_old, _server_active_requests_count_attrs_old, + _server_duration_attrs_old, ) from opentelemetry.instrumentation.flask import FlaskInstrumentor from opentelemetry.instrumentation.propagators import ( diff --git a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py index 8987c38764..2052fe47cd 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py @@ -67,8 +67,8 @@ _client_duration_attrs_old, _filter_semconv_duration_attrs, _get_schema_url, - _OpenTelemetrySemanticConventionStability, _HTTPStabilityMode, + _OpenTelemetrySemanticConventionStability, _OpenTelemetryStabilitySignalType, _report_new, _report_old, diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index 7dbcc0af05..edc8da301d 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -23,7 +23,7 @@ import opentelemetry.instrumentation.requests from opentelemetry import trace from opentelemetry.instrumentation._semconv import ( - _OTEL_SEMCONV_STABILITY_OPT_IN_KEY, + OTEL_SEMCONV_STABILITY_OPT_IN, _SPAN_ATTRIBUTES_ERROR_TYPE, _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS, _SPAN_ATTRIBUTES_NETWORK_PEER_PORT, @@ -88,7 +88,7 @@ def setUp(self): "os.environ", { "OTEL_PYTHON_REQUESTS_EXCLUDED_URLS": "http://localhost/env_excluded_arg/123,env_excluded_noarg", - _OTEL_SEMCONV_STABILITY_OPT_IN_KEY: sem_conv_mode, + OTEL_SEMCONV_STABILITY_OPT_IN: sem_conv_mode, }, ) @@ -670,7 +670,7 @@ def setUp(self): self.env_patch = mock.patch.dict( "os.environ", { - _OTEL_SEMCONV_STABILITY_OPT_IN_KEY: sem_conv_mode, + OTEL_SEMCONV_STABILITY_OPT_IN: sem_conv_mode, }, ) self.env_patch.start() diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index b8b9796a29..a57ea38e9b 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -214,16 +214,16 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he from opentelemetry import context, trace from opentelemetry.instrumentation._semconv import ( - _OpenTelemetrySemanticConventionStability, - _OpenTelemetryStabilitySignalType, _METRIC_ATTRIBUTES_SERVER_DURATION_NAME, - _HTTPStabilityMode, _SPAN_ATTRIBUTES_ERROR_TYPE, - _get_schema_url, _filter_semconv_active_request_count_attr, _filter_semconv_duration_attrs, - _report_old, + _get_schema_url, + _HTTPStabilityMode, + _OpenTelemetrySemanticConventionStability, + _OpenTelemetryStabilitySignalType, _report_new, + _report_old, _server_active_requests_count_attrs_new, _server_active_requests_count_attrs_old, _server_duration_attrs_new, @@ -240,9 +240,7 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he _set_http_user_agent, _set_status, ) -from opentelemetry.instrumentation.utils import ( - _start_internal_or_server_span, -) +from opentelemetry.instrumentation.utils import _start_internal_or_server_span from opentelemetry.instrumentation.wsgi.version import __version__ from opentelemetry.metrics import get_meter from opentelemetry.propagators.textmap import Getter @@ -250,11 +248,11 @@ def response_hook(span: Span, environ: WSGIEnvironment, status: str, response_he from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.trace.status import Status, StatusCode from opentelemetry.util.http import ( - _parse_url_query, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST, OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE, SanitizeValue, + _parse_url_query, get_custom_headers, normalise_request_header_name, normalise_response_header_name, diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index f232199dbd..ef35a7edf7 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -20,13 +20,13 @@ import opentelemetry.instrumentation.wsgi as otel_wsgi from opentelemetry import trace as trace_api from opentelemetry.instrumentation._semconv import ( + OTEL_SEMCONV_STABILITY_OPT_IN, + _HTTPStabilityMode, + _OpenTelemetrySemanticConventionStability, _server_active_requests_count_attrs_new, _server_active_requests_count_attrs_old, _server_duration_attrs_new, _server_duration_attrs_old, - _OTEL_SEMCONV_STABILITY_OPT_IN_KEY, - _OpenTelemetrySemanticConventionStability, - _HTTPStabilityMode, ) from opentelemetry.sdk.metrics.export import ( HistogramDataPoint, @@ -179,7 +179,7 @@ def setUp(self): self.env_patch = mock.patch.dict( "os.environ", { - _OTEL_SEMCONV_STABILITY_OPT_IN_KEY: sem_conv_mode, + OTEL_SEMCONV_STABILITY_OPT_IN: sem_conv_mode, }, ) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py index a4a7128327..31c2486acc 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/_semconv.py @@ -16,11 +16,9 @@ import threading from enum import Enum -from opentelemetry.instrumentation.utils import ( - http_status_to_status_code, -) -from opentelemetry.trace.status import Status, StatusCode +from opentelemetry.instrumentation.utils import http_status_to_status_code from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.trace.status import Status, StatusCode # TODO: will come through semconv package once updated _SPAN_ATTRIBUTES_ERROR_TYPE = "error.type" @@ -86,7 +84,7 @@ SpanAttributes.URL_SCHEME, ] -_OTEL_SEMCONV_STABILITY_OPT_IN_KEY = "OTEL_SEMCONV_STABILITY_OPT_IN" +OTEL_SEMCONV_STABILITY_OPT_IN = "OTEL_SEMCONV_STABILITY_OPT_IN" class _OpenTelemetryStabilitySignalType: @@ -121,7 +119,7 @@ def _initialize(cls): if not _OpenTelemetrySemanticConventionStability._initialized: # Users can pass in comma delimited string for opt-in options # Only values for http stability are supported for now - opt_in = os.environ.get(_OTEL_SEMCONV_STABILITY_OPT_IN_KEY, "") + opt_in = os.environ.get(OTEL_SEMCONV_STABILITY_OPT_IN, "") opt_in_list = [] if opt_in: opt_in_list = [s.strip() for s in opt_in.split(",")] From f0bf49de7544557ed1b366a2f38a0172a0876ba9 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Thu, 18 Apr 2024 13:34:29 -0700 Subject: [PATCH 37/42] Update test_requests_integration.py --- .../tests/test_requests_integration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py index edc8da301d..d85d70e20e 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py +++ b/instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py @@ -23,10 +23,10 @@ import opentelemetry.instrumentation.requests from opentelemetry import trace from opentelemetry.instrumentation._semconv import ( - OTEL_SEMCONV_STABILITY_OPT_IN, _SPAN_ATTRIBUTES_ERROR_TYPE, _SPAN_ATTRIBUTES_NETWORK_PEER_ADDRESS, _SPAN_ATTRIBUTES_NETWORK_PEER_PORT, + OTEL_SEMCONV_STABILITY_OPT_IN, _OpenTelemetrySemanticConventionStability, ) from opentelemetry.instrumentation.requests import RequestsInstrumentor From 34c320dae04d69bdaf8010fdabb6fb107b8c2d06 Mon Sep 17 00:00:00 2001 From: Diego Hurtado Date: Thu, 18 Apr 2024 17:46:14 -0500 Subject: [PATCH 38/42] Fix lint --- .../tests/test_wsgi_middleware.py | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index ef35a7edf7..779b598ddf 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -698,7 +698,6 @@ def test_request_attributes_with_faux_scheme_relative_raw_uri(self): def test_request_attributes_pathless(self): self.environ["RAW_URI"] = "" - expected = {SpanAttributes.HTTP_TARGET: ""} self.assertIsNone( otel_wsgi.collect_request_attributes(self.environ).get( SpanAttributes.HTTP_TARGET From ee05a47a10314146e48aa4a96838f34a50923313 Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Fri, 19 Apr 2024 09:23:33 -0700 Subject: [PATCH 39/42] lint --- .../src/opentelemetry/instrumentation/wsgi/__init__.py | 2 +- .../tests/test_wsgi_middleware.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index a57ea38e9b..06441cd38f 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -300,7 +300,7 @@ def setifnotnone(dic, key, value): if value is not None: dic[key] = value - +# pylint: enable=too-many-branches def collect_request_attributes( environ, sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT, diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index ef35a7edf7..0ffc906f7f 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -11,6 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + +# pylint: disable=too-many-lines + import sys import unittest import wsgiref.util as wsgiref_util From 0ab00978ccbaba4488e99ca8e4e701d047ac7acb Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Fri, 19 Apr 2024 09:38:06 -0700 Subject: [PATCH 40/42] Update __init__.py --- .../src/opentelemetry/instrumentation/wsgi/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 4b3afa05f1..5feffc2a22 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -300,8 +300,10 @@ def setifnotnone(dic, key, value): if value is not None: dic[key] = value + # pylint: enable=too-many-branches + def collect_request_attributes( environ, sem_conv_opt_in_mode=_HTTPStabilityMode.DEFAULT, From d683a720a195bfde42af77035f8ca88dc10001cb Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Fri, 19 Apr 2024 10:22:06 -0700 Subject: [PATCH 41/42] Update test_wsgi_middleware.py --- .../tests/test_wsgi_middleware.py | 1 - 1 file changed, 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py index 0ffc906f7f..985fbe0571 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/tests/test_wsgi_middleware.py @@ -701,7 +701,6 @@ def test_request_attributes_with_faux_scheme_relative_raw_uri(self): def test_request_attributes_pathless(self): self.environ["RAW_URI"] = "" - expected = {SpanAttributes.HTTP_TARGET: ""} self.assertIsNone( otel_wsgi.collect_request_attributes(self.environ).get( SpanAttributes.HTTP_TARGET From d2c12c2909bd477e57304b1635150d9880fc2e5d Mon Sep 17 00:00:00 2001 From: Leighton Chen Date: Fri, 19 Apr 2024 10:43:51 -0700 Subject: [PATCH 42/42] Update __init__.py --- .../src/opentelemetry/instrumentation/wsgi/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py index 5feffc2a22..0a873d0fc3 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/src/opentelemetry/instrumentation/wsgi/__init__.py @@ -301,7 +301,7 @@ def setifnotnone(dic, key, value): dic[key] = value -# pylint: enable=too-many-branches +# pylint: disable=too-many-branches def collect_request_attributes(