This commit is contained in:
macbook_max
2026-10-15 20:36:59 +08:00
commit 6bf949949c
7923 changed files with 1369582 additions and 0 deletions
@@ -0,0 +1,122 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0
from collections.abc import Mapping
from logging import getLogger
from re import compile
from types import MappingProxyType
from opentelemetry.context import create_key, get_value, set_value
from opentelemetry.context.context import Context
from opentelemetry.util.re import (
_BAGGAGE_PROPERTY_FORMAT,
_KEY_FORMAT,
_VALUE_FORMAT,
)
_BAGGAGE_KEY = create_key("baggage")
_logger = getLogger(__name__)
_KEY_PATTERN = compile(_KEY_FORMAT)
_VALUE_PATTERN = compile(_VALUE_FORMAT)
_PROPERT_PATTERN = compile(_BAGGAGE_PROPERTY_FORMAT)
def get_all(
context: Context | None = None,
) -> Mapping[str, object]:
"""Returns the name/value pairs in the Baggage
Args:
context: The Context to use. If not set, uses current Context
Returns:
The name/value pairs in the Baggage
"""
return MappingProxyType(_get_baggage_value(context=context))
def get_baggage(name: str, context: Context | None = None) -> object | None:
"""Provides access to the value for a name/value pair in the
Baggage
Args:
name: The name of the value to retrieve
context: The Context to use. If not set, uses current Context
Returns:
The value associated with the given name, or null if the given name is
not present.
"""
return _get_baggage_value(context=context).get(name)
def set_baggage(name: str, value: object, context: Context | None = None) -> Context:
"""Sets a value in the Baggage
Args:
name: The name of the value to set
value: The value to set
context: The Context to use. If not set, uses current Context
Returns:
A Context with the value updated
"""
baggage = _get_baggage_value(context=context).copy()
baggage[name] = value
return set_value(_BAGGAGE_KEY, baggage, context=context)
def remove_baggage(name: str, context: Context | None = None) -> Context:
"""Removes a value from the Baggage
Args:
name: The name of the value to remove
context: The Context to use. If not set, uses current Context
Returns:
A Context with the name/value removed
"""
baggage = _get_baggage_value(context=context).copy()
baggage.pop(name, None)
return set_value(_BAGGAGE_KEY, baggage, context=context)
def clear(context: Context | None = None) -> Context:
"""Removes all values from the Baggage
Args:
context: The Context to use. If not set, uses current Context
Returns:
A Context with all baggage entries removed
"""
return set_value(_BAGGAGE_KEY, {}, context=context)
def _get_baggage_value(context: Context | None = None) -> dict[str, object]:
baggage = get_value(_BAGGAGE_KEY, context=context)
if isinstance(baggage, dict):
return baggage
return {}
def _is_valid_key(name: str) -> bool:
return _KEY_PATTERN.fullmatch(str(name)) is not None
def _is_valid_value(value: object) -> bool:
parts = str(value).split(";")
# W3C Baggage allows optional whitespace between the value and ";"
is_valid_value = _VALUE_PATTERN.fullmatch(parts[0].rstrip(" \t")) is not None
if len(parts) > 1: # one or more properties metadata
for property in parts[1:]:
if _PROPERT_PATTERN.fullmatch(property) is None:
is_valid_value = False
break
return is_valid_value
def _is_valid_pair(key: str, value: str) -> bool:
return _is_valid_key(key) and _is_valid_value(value)
@@ -0,0 +1,179 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0
#
from collections.abc import Iterable, Iterator, Mapping
from logging import getLogger
from re import split
from urllib.parse import quote_plus, unquote_plus
from opentelemetry.baggage import _is_valid_pair, get_all, set_baggage
from opentelemetry.context import get_current
from opentelemetry.context.context import Context
from opentelemetry.propagators import textmap
from opentelemetry.util.re import _DELIMITER_PATTERN
_logger = getLogger(__name__)
def _filter_valid_entries(
entries: Iterable[str],
max_pair_length: int,
) -> Iterator[str]:
for entry in entries:
if not entry:
continue
if not entry.isascii():
_logger.warning(
"Baggage entry with key `%s` contains non-ASCII characters",
entry.split("=", 1)[0],
)
continue
if len(entry) > max_pair_length:
_logger.warning(
"Baggage entry with key `%s` exceeded the maximum number of bytes per list-member with length %d",
entry.split("=", 1)[0],
len(entry),
)
continue
yield entry
def _apply_baggage_limits(
entries: Iterable[str],
max_pairs: int,
max_pair_length: int,
max_header_length: int,
) -> Iterator[str]:
"""Apply W3C Baggage size limits to a sequence of baggage entries.
Yields entries that fit within the W3C specification limits.
Logs warnings when entries are dropped.
"""
length = 0
for index, entry in enumerate(_filter_valid_entries(entries, max_pair_length)):
if index >= max_pairs:
_logger.warning("Baggage exceeded the maximum number of list-members")
return
length += (1 if index > 0 else 0) + len(entry)
if length > max_header_length:
_logger.warning("Baggage exceeded the maximum number of bytes per baggage-string")
return
yield entry
class W3CBaggagePropagator(textmap.TextMapPropagator):
"""Extracts and injects Baggage which is used to annotate telemetry."""
_MAX_HEADER_LENGTH = 8192
_MAX_PAIR_LENGTH = 4096
_MAX_PAIRS = 180
_BAGGAGE_HEADER_NAME = "baggage"
def extract(
self,
carrier: textmap.CarrierT,
context: Context | None = None,
getter: textmap.Getter[textmap.CarrierT] = textmap.default_getter,
) -> Context:
"""Extract Baggage from the carrier.
See
`opentelemetry.propagators.textmap.TextMapPropagator.extract`
"""
if context is None:
context = get_current()
header = _extract_first_element(getter.get(carrier, self._BAGGAGE_HEADER_NAME))
if not header:
return context
if len(header.encode()) > self._MAX_HEADER_LENGTH:
_logger.warning(
"Baggage header `%s` exceeded the maximum number of bytes per baggage-string",
header,
)
return context
baggage_entries = split(_DELIMITER_PATTERN, header)
for entry in _apply_baggage_limits(
baggage_entries,
max_pairs=self._MAX_PAIRS,
max_pair_length=self._MAX_PAIR_LENGTH,
max_header_length=self._MAX_HEADER_LENGTH,
):
try:
name, value = entry.split("=", 1)
except Exception: # pylint: disable=broad-exception-caught
_logger.warning("Baggage list-member `%s` doesn't match the format", entry)
continue
# W3C Baggage allows optional whitespace around "="
name = name.strip(" \t")
value = value.strip(" \t")
if not _is_valid_pair(name, value):
_logger.warning("Invalid baggage entry: `%s`", entry)
continue
name = unquote_plus(name).strip()
value = unquote_plus(value).strip()
context = set_baggage(
name,
value,
context=context,
)
return context
def inject(
self,
carrier: textmap.CarrierT,
context: Context | None = None,
setter: textmap.Setter[textmap.CarrierT] = textmap.default_setter,
) -> None:
"""Injects Baggage into the carrier.
See
`opentelemetry.propagators.textmap.TextMapPropagator.inject`
"""
baggage_entries = get_all(context=context)
if not baggage_entries:
return
baggage_string = ",".join(
_apply_baggage_limits(
_encode_baggage_pairs(baggage_entries),
max_pairs=self._MAX_PAIRS,
max_pair_length=self._MAX_PAIR_LENGTH,
max_header_length=self._MAX_HEADER_LENGTH,
)
)
if baggage_string:
setter.set(carrier, self._BAGGAGE_HEADER_NAME, baggage_string)
@property
def fields(self) -> set[str]:
"""Returns a set with the fields set in `inject`."""
return {self._BAGGAGE_HEADER_NAME}
def _encode_baggage_pairs(
baggage_entries: Mapping[str, object],
) -> Iterator[str]:
"""Yield URL-encoded 'key=value' pairs from baggage entries."""
for key, value in baggage_entries.items():
yield quote_plus(str(key)) + "=" + quote_plus(str(value))
def _extract_first_element(
items: Iterable[textmap.CarrierT] | None,
) -> textmap.CarrierT | None:
if items is None:
return None
return next(iter(items), None)