mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2025-07-28 04:34:01 +08:00
Support PEP 561 to opentelemetry-util-http
(#3127)
This commit is contained in:

committed by
GitHub

parent
cf6d45e96c
commit
9af3136e7f
@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
([#3148](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3148))
|
||||
- add support to Python 3.13
|
||||
([#3134](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3134))
|
||||
- `opentelemetry-util-http` Add `py.typed` file to enable PEP 561
|
||||
([#3127](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3127))
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@ -19,7 +19,7 @@ from os import environ
|
||||
from re import IGNORECASE as RE_IGNORECASE
|
||||
from re import compile as re_compile
|
||||
from re import search
|
||||
from typing import Callable, Iterable, Optional
|
||||
from typing import Callable, Iterable
|
||||
from urllib.parse import urlparse, urlunparse
|
||||
|
||||
from opentelemetry.semconv.trace import SpanAttributes
|
||||
@ -121,18 +121,16 @@ class SanitizeValue:
|
||||
_root = r"OTEL_PYTHON_{}"
|
||||
|
||||
|
||||
def get_traced_request_attrs(instrumentation):
|
||||
def get_traced_request_attrs(instrumentation: str) -> list[str]:
|
||||
traced_request_attrs = environ.get(
|
||||
_root.format(f"{instrumentation}_TRACED_REQUEST_ATTRS"), []
|
||||
_root.format(f"{instrumentation}_TRACED_REQUEST_ATTRS")
|
||||
)
|
||||
|
||||
if traced_request_attrs:
|
||||
traced_request_attrs = [
|
||||
return [
|
||||
traced_request_attr.strip()
|
||||
for traced_request_attr in traced_request_attrs.split(",")
|
||||
]
|
||||
|
||||
return traced_request_attrs
|
||||
return []
|
||||
|
||||
|
||||
def get_excluded_urls(instrumentation: str) -> ExcludeList:
|
||||
@ -193,7 +191,7 @@ def normalise_response_header_name(header: str) -> str:
|
||||
return f"http.response.header.{key}"
|
||||
|
||||
|
||||
def sanitize_method(method: Optional[str]) -> Optional[str]:
|
||||
def sanitize_method(method: str | None) -> str | None:
|
||||
if method is None:
|
||||
return None
|
||||
method = method.upper()
|
||||
|
@ -17,12 +17,14 @@ This library provides functionality to enrich HTTP client spans with IPs. It doe
|
||||
not create spans on its own.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import contextlib
|
||||
import http.client
|
||||
import logging
|
||||
import socket # pylint:disable=unused-import # Used for typing
|
||||
import typing
|
||||
from typing import Collection
|
||||
from typing import Any, Callable, Collection, TypedDict, cast
|
||||
|
||||
import wrapt
|
||||
|
||||
@ -36,20 +38,22 @@ _STATE_KEY = "httpbase_instrumentation_state"
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
R = typing.TypeVar("R")
|
||||
|
||||
|
||||
class HttpClientInstrumentor(BaseInstrumentor):
|
||||
def instrumentation_dependencies(self) -> Collection[str]:
|
||||
return () # This instruments http.client from stdlib; no extra deps.
|
||||
|
||||
def _instrument(self, **kwargs):
|
||||
def _instrument(self, **kwargs: Any):
|
||||
"""Instruments the http.client module (not creating spans on its own)"""
|
||||
_instrument()
|
||||
|
||||
def _uninstrument(self, **kwargs):
|
||||
def _uninstrument(self, **kwargs: Any):
|
||||
_uninstrument()
|
||||
|
||||
|
||||
def _remove_nonrecording(spanlist: typing.List[Span]):
|
||||
def _remove_nonrecording(spanlist: list[Span]) -> bool:
|
||||
idx = len(spanlist) - 1
|
||||
while idx >= 0:
|
||||
if not spanlist[idx].is_recording():
|
||||
@ -67,7 +71,9 @@ def _remove_nonrecording(spanlist: typing.List[Span]):
|
||||
return True
|
||||
|
||||
|
||||
def trysetip(conn: http.client.HTTPConnection, loglevel=logging.DEBUG) -> bool:
|
||||
def trysetip(
|
||||
conn: http.client.HTTPConnection, loglevel: int = logging.DEBUG
|
||||
) -> bool:
|
||||
"""Tries to set the net.peer.ip semantic attribute on the current span from the given
|
||||
HttpConnection.
|
||||
|
||||
@ -110,14 +116,17 @@ def trysetip(conn: http.client.HTTPConnection, loglevel=logging.DEBUG) -> bool:
|
||||
|
||||
|
||||
def _instrumented_connect(
|
||||
wrapped, instance: http.client.HTTPConnection, args, kwargs
|
||||
):
|
||||
wrapped: Callable[..., R],
|
||||
instance: http.client.HTTPConnection,
|
||||
args: tuple[Any, ...],
|
||||
kwargs: dict[str, Any],
|
||||
) -> R:
|
||||
result = wrapped(*args, **kwargs)
|
||||
trysetip(instance, loglevel=logging.WARNING)
|
||||
return result
|
||||
|
||||
|
||||
def instrument_connect(module, name="connect"):
|
||||
def instrument_connect(module: type[Any], name: str = "connect"):
|
||||
"""Instrument additional connect() methods, e.g. for derived classes."""
|
||||
|
||||
wrapt.wrap_function_wrapper(
|
||||
@ -129,8 +138,11 @@ def instrument_connect(module, name="connect"):
|
||||
|
||||
def _instrument():
|
||||
def instrumented_send(
|
||||
wrapped, instance: http.client.HTTPConnection, args, kwargs
|
||||
):
|
||||
wrapped: Callable[..., R],
|
||||
instance: http.client.HTTPConnection,
|
||||
args: tuple[Any, ...],
|
||||
kwargs: dict[str, Any],
|
||||
) -> R:
|
||||
done = trysetip(instance)
|
||||
result = wrapped(*args, **kwargs)
|
||||
if not done:
|
||||
@ -147,8 +159,12 @@ def _instrument():
|
||||
# No need to instrument HTTPSConnection, as it calls super().connect()
|
||||
|
||||
|
||||
def _getstate() -> typing.Optional[dict]:
|
||||
return context.get_value(_STATE_KEY)
|
||||
class _ConnectionState(TypedDict):
|
||||
need_ip: list[Span]
|
||||
|
||||
|
||||
def _getstate() -> _ConnectionState | None:
|
||||
return cast(_ConnectionState, context.get_value(_STATE_KEY))
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
@ -163,7 +179,7 @@ def set_ip_on_next_http_connection(span: Span):
|
||||
finally:
|
||||
context.detach(token)
|
||||
else:
|
||||
spans: typing.List[Span] = state["need_ip"]
|
||||
spans = state["need_ip"]
|
||||
spans.append(span)
|
||||
try:
|
||||
yield
|
||||
|
Reference in New Issue
Block a user