chore: 添加虚拟环境到仓库
- 添加 backend_service/venv 虚拟环境 - 包含所有Python依赖包 - 注意:虚拟环境约393MB,包含12655个文件
This commit is contained in:
@@ -0,0 +1,648 @@
|
||||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# 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.
|
||||
|
||||
"""
|
||||
The OpenTelemetry tracing API describes the classes used to generate
|
||||
distributed traces.
|
||||
|
||||
The :class:`.Tracer` class controls access to the execution context, and
|
||||
manages span creation. Each operation in a trace is represented by a
|
||||
:class:`.Span`, which records the start, end time, and metadata associated with
|
||||
the operation.
|
||||
|
||||
This module provides abstract (i.e. unimplemented) classes required for
|
||||
tracing, and a concrete no-op :class:`.NonRecordingSpan` that allows applications
|
||||
to use the API package alone without a supporting implementation.
|
||||
|
||||
To get a tracer, you need to provide the package name from which you are
|
||||
calling the tracer APIs to OpenTelemetry by calling `TracerProvider.get_tracer`
|
||||
with the calling module name and the version of your package.
|
||||
|
||||
The tracer supports creating spans that are "attached" or "detached" from the
|
||||
context. New spans are "attached" to the context in that they are
|
||||
created as children of the currently active span, and the newly-created span
|
||||
can optionally become the new active span::
|
||||
|
||||
from opentelemetry import trace
|
||||
|
||||
tracer = trace.get_tracer(__name__)
|
||||
|
||||
# Create a new root span, set it as the current span in context
|
||||
with tracer.start_as_current_span("parent"):
|
||||
# Attach a new child and update the current span
|
||||
with tracer.start_as_current_span("child"):
|
||||
do_work():
|
||||
# Close child span, set parent as current
|
||||
# Close parent span, set default span as current
|
||||
|
||||
When creating a span that's "detached" from the context the active span doesn't
|
||||
change, and the caller is responsible for managing the span's lifetime::
|
||||
|
||||
# Explicit parent span assignment is done via the Context
|
||||
from opentelemetry.trace import set_span_in_context
|
||||
|
||||
context = set_span_in_context(parent)
|
||||
child = tracer.start_span("child", context=context)
|
||||
|
||||
try:
|
||||
do_work(span=child)
|
||||
finally:
|
||||
child.end()
|
||||
|
||||
Applications should generally use a single global TracerProvider, and use
|
||||
either implicit or explicit context propagation consistently throughout.
|
||||
|
||||
.. versionadded:: 0.1.0
|
||||
.. versionchanged:: 0.3.0
|
||||
`TracerProvider` was introduced and the global ``tracer`` getter was
|
||||
replaced by ``tracer_provider``.
|
||||
.. versionchanged:: 0.5.0
|
||||
``tracer_provider`` was replaced by `get_tracer_provider`,
|
||||
``set_preferred_tracer_provider_implementation`` was replaced by
|
||||
`set_tracer_provider`.
|
||||
"""
|
||||
|
||||
import os
|
||||
import typing
|
||||
from abc import ABC, abstractmethod
|
||||
from enum import Enum
|
||||
from logging import getLogger
|
||||
from typing import Iterator, Optional, Sequence, cast
|
||||
|
||||
from typing_extensions import deprecated
|
||||
|
||||
from opentelemetry import context as context_api
|
||||
from opentelemetry.attributes import BoundedAttributes
|
||||
from opentelemetry.context.context import Context
|
||||
from opentelemetry.environment_variables import OTEL_PYTHON_TRACER_PROVIDER
|
||||
from opentelemetry.trace.propagation import (
|
||||
_SPAN_KEY,
|
||||
get_current_span,
|
||||
set_span_in_context,
|
||||
)
|
||||
from opentelemetry.trace.span import (
|
||||
DEFAULT_TRACE_OPTIONS,
|
||||
DEFAULT_TRACE_STATE,
|
||||
INVALID_SPAN,
|
||||
INVALID_SPAN_CONTEXT,
|
||||
INVALID_SPAN_ID,
|
||||
INVALID_TRACE_ID,
|
||||
NonRecordingSpan,
|
||||
Span,
|
||||
SpanContext,
|
||||
TraceFlags,
|
||||
TraceState,
|
||||
format_span_id,
|
||||
format_trace_id,
|
||||
)
|
||||
from opentelemetry.trace.status import Status, StatusCode
|
||||
from opentelemetry.util import types
|
||||
from opentelemetry.util._decorator import _agnosticcontextmanager
|
||||
from opentelemetry.util._once import Once
|
||||
from opentelemetry.util._providers import _load_provider
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
|
||||
class _LinkBase(ABC):
|
||||
def __init__(self, context: "SpanContext") -> None:
|
||||
self._context = context
|
||||
|
||||
@property
|
||||
def context(self) -> "SpanContext":
|
||||
return self._context
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def attributes(self) -> types.Attributes:
|
||||
pass
|
||||
|
||||
|
||||
class Link(_LinkBase):
|
||||
"""A link to a `Span`. The attributes of a Link are immutable.
|
||||
|
||||
Args:
|
||||
context: `SpanContext` of the `Span` to link to.
|
||||
attributes: Link's attributes.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
context: "SpanContext",
|
||||
attributes: types.Attributes = None,
|
||||
) -> None:
|
||||
super().__init__(context)
|
||||
self._attributes = attributes
|
||||
|
||||
@property
|
||||
def attributes(self) -> types.Attributes:
|
||||
return self._attributes
|
||||
|
||||
@property
|
||||
def dropped_attributes(self) -> int:
|
||||
if isinstance(self._attributes, BoundedAttributes):
|
||||
return self._attributes.dropped
|
||||
return 0
|
||||
|
||||
|
||||
_Links = Optional[Sequence[Link]]
|
||||
|
||||
|
||||
class SpanKind(Enum):
|
||||
"""Specifies additional details on how this span relates to its parent span.
|
||||
|
||||
Note that this enumeration is experimental and likely to change. See
|
||||
https://github.com/open-telemetry/opentelemetry-specification/pull/226.
|
||||
"""
|
||||
|
||||
#: Default value. Indicates that the span is used internally in the
|
||||
# application.
|
||||
INTERNAL = 0
|
||||
|
||||
#: Indicates that the span describes an operation that handles a remote
|
||||
# request.
|
||||
SERVER = 1
|
||||
|
||||
#: Indicates that the span describes a request to some remote service.
|
||||
CLIENT = 2
|
||||
|
||||
#: Indicates that the span describes a producer sending a message to a
|
||||
#: broker. Unlike client and server, there is usually no direct critical
|
||||
#: path latency relationship between producer and consumer spans.
|
||||
PRODUCER = 3
|
||||
|
||||
#: Indicates that the span describes a consumer receiving a message from a
|
||||
#: broker. Unlike client and server, there is usually no direct critical
|
||||
#: path latency relationship between producer and consumer spans.
|
||||
CONSUMER = 4
|
||||
|
||||
|
||||
class TracerProvider(ABC):
|
||||
@abstractmethod
|
||||
def get_tracer(
|
||||
self,
|
||||
instrumenting_module_name: str,
|
||||
instrumenting_library_version: typing.Optional[str] = None,
|
||||
schema_url: typing.Optional[str] = None,
|
||||
attributes: typing.Optional[types.Attributes] = None,
|
||||
) -> "Tracer":
|
||||
"""Returns a `Tracer` for use by the given instrumentation library.
|
||||
|
||||
For any two calls it is undefined whether the same or different
|
||||
`Tracer` instances are returned, even for different library names.
|
||||
|
||||
This function may return different `Tracer` types (e.g. a no-op tracer
|
||||
vs. a functional tracer).
|
||||
|
||||
Args:
|
||||
instrumenting_module_name: The uniquely identifiable name for instrumentation
|
||||
scope, such as instrumentation library, package, module or class name.
|
||||
``__name__`` may not be used as this can result in
|
||||
different tracer names if the tracers are in different files.
|
||||
It is better to use a fixed string that can be imported where
|
||||
needed and used consistently as the name of the tracer.
|
||||
|
||||
This should *not* be the name of the module that is
|
||||
instrumented but the name of the module doing the instrumentation.
|
||||
E.g., instead of ``"requests"``, use
|
||||
``"opentelemetry.instrumentation.requests"``.
|
||||
|
||||
instrumenting_library_version: Optional. The version string of the
|
||||
instrumenting library. Usually this should be the same as
|
||||
``importlib.metadata.version(instrumenting_library_name)``.
|
||||
|
||||
schema_url: Optional. Specifies the Schema URL of the emitted telemetry.
|
||||
attributes: Optional. Specifies the attributes of the emitted telemetry.
|
||||
"""
|
||||
|
||||
|
||||
class NoOpTracerProvider(TracerProvider):
|
||||
"""The default TracerProvider, used when no implementation is available.
|
||||
|
||||
All operations are no-op.
|
||||
"""
|
||||
|
||||
def get_tracer(
|
||||
self,
|
||||
instrumenting_module_name: str,
|
||||
instrumenting_library_version: typing.Optional[str] = None,
|
||||
schema_url: typing.Optional[str] = None,
|
||||
attributes: typing.Optional[types.Attributes] = None,
|
||||
) -> "Tracer":
|
||||
# pylint:disable=no-self-use,unused-argument
|
||||
return NoOpTracer()
|
||||
|
||||
|
||||
@deprecated(
|
||||
"You should use NoOpTracerProvider. Deprecated since version 1.9.0."
|
||||
)
|
||||
class _DefaultTracerProvider(NoOpTracerProvider):
|
||||
"""The default TracerProvider, used when no implementation is available.
|
||||
|
||||
All operations are no-op.
|
||||
"""
|
||||
|
||||
|
||||
class ProxyTracerProvider(TracerProvider):
|
||||
def get_tracer(
|
||||
self,
|
||||
instrumenting_module_name: str,
|
||||
instrumenting_library_version: typing.Optional[str] = None,
|
||||
schema_url: typing.Optional[str] = None,
|
||||
attributes: typing.Optional[types.Attributes] = None,
|
||||
) -> "Tracer":
|
||||
if _TRACER_PROVIDER:
|
||||
return _TRACER_PROVIDER.get_tracer(
|
||||
instrumenting_module_name,
|
||||
instrumenting_library_version,
|
||||
schema_url,
|
||||
attributes,
|
||||
)
|
||||
return ProxyTracer(
|
||||
instrumenting_module_name,
|
||||
instrumenting_library_version,
|
||||
schema_url,
|
||||
attributes,
|
||||
)
|
||||
|
||||
|
||||
class Tracer(ABC):
|
||||
"""Handles span creation and in-process context propagation.
|
||||
|
||||
This class provides methods for manipulating the context, creating spans,
|
||||
and controlling spans' lifecycles.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def start_span(
|
||||
self,
|
||||
name: str,
|
||||
context: Optional[Context] = None,
|
||||
kind: SpanKind = SpanKind.INTERNAL,
|
||||
attributes: types.Attributes = None,
|
||||
links: _Links = None,
|
||||
start_time: Optional[int] = None,
|
||||
record_exception: bool = True,
|
||||
set_status_on_exception: bool = True,
|
||||
) -> "Span":
|
||||
"""Starts a span.
|
||||
|
||||
Create a new span. Start the span without setting it as the current
|
||||
span in the context. To start the span and use the context in a single
|
||||
method, see :meth:`start_as_current_span`.
|
||||
|
||||
By default the current span in the context will be used as parent, but an
|
||||
explicit context can also be specified, by passing in a `Context` containing
|
||||
a current `Span`. If there is no current span in the global `Context` or in
|
||||
the specified context, the created span will be a root span.
|
||||
|
||||
The span can be used as a context manager. On exiting the context manager,
|
||||
the span's end() method will be called.
|
||||
|
||||
Example::
|
||||
|
||||
# trace.get_current_span() will be used as the implicit parent.
|
||||
# If none is found, the created span will be a root instance.
|
||||
with tracer.start_span("one") as child:
|
||||
child.add_event("child's event")
|
||||
|
||||
Args:
|
||||
name: The name of the span to be created.
|
||||
context: An optional Context containing the span's parent. Defaults to the
|
||||
global context.
|
||||
kind: The span's kind (relationship to parent). Note that is
|
||||
meaningful even if there is no parent.
|
||||
attributes: The span's attributes.
|
||||
links: Links span to other spans
|
||||
start_time: Sets the start time of a span
|
||||
record_exception: Whether to record any exceptions raised within the
|
||||
context as error event on the span.
|
||||
set_status_on_exception: Only relevant if the returned span is used
|
||||
in a with/context manager. Defines whether the span status will
|
||||
be automatically set to ERROR when an uncaught exception is
|
||||
raised in the span with block. The span status won't be set by
|
||||
this mechanism if it was previously set manually.
|
||||
|
||||
Returns:
|
||||
The newly-created span.
|
||||
"""
|
||||
|
||||
@_agnosticcontextmanager
|
||||
@abstractmethod
|
||||
def start_as_current_span(
|
||||
self,
|
||||
name: str,
|
||||
context: Optional[Context] = None,
|
||||
kind: SpanKind = SpanKind.INTERNAL,
|
||||
attributes: types.Attributes = None,
|
||||
links: _Links = None,
|
||||
start_time: Optional[int] = None,
|
||||
record_exception: bool = True,
|
||||
set_status_on_exception: bool = True,
|
||||
end_on_exit: bool = True,
|
||||
) -> Iterator["Span"]:
|
||||
"""Context manager for creating a new span and set it
|
||||
as the current span in this tracer's context.
|
||||
|
||||
Exiting the context manager will call the span's end method,
|
||||
as well as return the current span to its previous value by
|
||||
returning to the previous context.
|
||||
|
||||
Example::
|
||||
|
||||
with tracer.start_as_current_span("one") as parent:
|
||||
parent.add_event("parent's event")
|
||||
with tracer.start_as_current_span("two") as child:
|
||||
child.add_event("child's event")
|
||||
trace.get_current_span() # returns child
|
||||
trace.get_current_span() # returns parent
|
||||
trace.get_current_span() # returns previously active span
|
||||
|
||||
This is a convenience method for creating spans attached to the
|
||||
tracer's context. Applications that need more control over the span
|
||||
lifetime should use :meth:`start_span` instead. For example::
|
||||
|
||||
with tracer.start_as_current_span(name) as span:
|
||||
do_work()
|
||||
|
||||
is equivalent to::
|
||||
|
||||
span = tracer.start_span(name)
|
||||
with opentelemetry.trace.use_span(span, end_on_exit=True):
|
||||
do_work()
|
||||
|
||||
This can also be used as a decorator::
|
||||
|
||||
@tracer.start_as_current_span("name")
|
||||
def function():
|
||||
...
|
||||
|
||||
function()
|
||||
|
||||
Args:
|
||||
name: The name of the span to be created.
|
||||
context: An optional Context containing the span's parent. Defaults to the
|
||||
global context.
|
||||
kind: The span's kind (relationship to parent). Note that is
|
||||
meaningful even if there is no parent.
|
||||
attributes: The span's attributes.
|
||||
links: Links span to other spans
|
||||
start_time: Sets the start time of a span
|
||||
record_exception: Whether to record any exceptions raised within the
|
||||
context as error event on the span.
|
||||
set_status_on_exception: Only relevant if the returned span is used
|
||||
in a with/context manager. Defines whether the span status will
|
||||
be automatically set to ERROR when an uncaught exception is
|
||||
raised in the span with block. The span status won't be set by
|
||||
this mechanism if it was previously set manually.
|
||||
end_on_exit: Whether to end the span automatically when leaving the
|
||||
context manager.
|
||||
|
||||
Yields:
|
||||
The newly-created span.
|
||||
"""
|
||||
|
||||
|
||||
class ProxyTracer(Tracer):
|
||||
# pylint: disable=W0222,signature-differs
|
||||
def __init__(
|
||||
self,
|
||||
instrumenting_module_name: str,
|
||||
instrumenting_library_version: typing.Optional[str] = None,
|
||||
schema_url: typing.Optional[str] = None,
|
||||
attributes: typing.Optional[types.Attributes] = None,
|
||||
):
|
||||
self._instrumenting_module_name = instrumenting_module_name
|
||||
self._instrumenting_library_version = instrumenting_library_version
|
||||
self._schema_url = schema_url
|
||||
self._attributes = attributes
|
||||
self._real_tracer: Optional[Tracer] = None
|
||||
self._noop_tracer = NoOpTracer()
|
||||
|
||||
@property
|
||||
def _tracer(self) -> Tracer:
|
||||
if self._real_tracer:
|
||||
return self._real_tracer
|
||||
|
||||
if _TRACER_PROVIDER:
|
||||
self._real_tracer = _TRACER_PROVIDER.get_tracer(
|
||||
self._instrumenting_module_name,
|
||||
self._instrumenting_library_version,
|
||||
self._schema_url,
|
||||
self._attributes,
|
||||
)
|
||||
return self._real_tracer
|
||||
return self._noop_tracer
|
||||
|
||||
def start_span(self, *args, **kwargs) -> Span: # type: ignore
|
||||
return self._tracer.start_span(*args, **kwargs) # type: ignore
|
||||
|
||||
@_agnosticcontextmanager # type: ignore
|
||||
def start_as_current_span(self, *args, **kwargs) -> Iterator[Span]:
|
||||
with self._tracer.start_as_current_span(*args, **kwargs) as span: # type: ignore
|
||||
yield span
|
||||
|
||||
|
||||
class NoOpTracer(Tracer):
|
||||
"""The default Tracer, used when no Tracer implementation is available.
|
||||
|
||||
All operations are no-op.
|
||||
"""
|
||||
|
||||
def start_span(
|
||||
self,
|
||||
name: str,
|
||||
context: Optional[Context] = None,
|
||||
kind: SpanKind = SpanKind.INTERNAL,
|
||||
attributes: types.Attributes = None,
|
||||
links: _Links = None,
|
||||
start_time: Optional[int] = None,
|
||||
record_exception: bool = True,
|
||||
set_status_on_exception: bool = True,
|
||||
) -> "Span":
|
||||
return INVALID_SPAN
|
||||
|
||||
@_agnosticcontextmanager
|
||||
def start_as_current_span(
|
||||
self,
|
||||
name: str,
|
||||
context: Optional[Context] = None,
|
||||
kind: SpanKind = SpanKind.INTERNAL,
|
||||
attributes: types.Attributes = None,
|
||||
links: _Links = None,
|
||||
start_time: Optional[int] = None,
|
||||
record_exception: bool = True,
|
||||
set_status_on_exception: bool = True,
|
||||
end_on_exit: bool = True,
|
||||
) -> Iterator["Span"]:
|
||||
yield INVALID_SPAN
|
||||
|
||||
|
||||
@deprecated("You should use NoOpTracer. Deprecated since version 1.9.0.")
|
||||
class _DefaultTracer(NoOpTracer):
|
||||
"""The default Tracer, used when no Tracer implementation is available.
|
||||
|
||||
All operations are no-op.
|
||||
"""
|
||||
|
||||
|
||||
_TRACER_PROVIDER_SET_ONCE = Once()
|
||||
_TRACER_PROVIDER: Optional[TracerProvider] = None
|
||||
_PROXY_TRACER_PROVIDER = ProxyTracerProvider()
|
||||
|
||||
|
||||
def get_tracer(
|
||||
instrumenting_module_name: str,
|
||||
instrumenting_library_version: typing.Optional[str] = None,
|
||||
tracer_provider: Optional[TracerProvider] = None,
|
||||
schema_url: typing.Optional[str] = None,
|
||||
attributes: typing.Optional[types.Attributes] = None,
|
||||
) -> "Tracer":
|
||||
"""Returns a `Tracer` for use by the given instrumentation library.
|
||||
|
||||
This function is a convenience wrapper for
|
||||
opentelemetry.trace.TracerProvider.get_tracer.
|
||||
|
||||
If tracer_provider is omitted the current configured one is used.
|
||||
"""
|
||||
if tracer_provider is None:
|
||||
tracer_provider = get_tracer_provider()
|
||||
return tracer_provider.get_tracer(
|
||||
instrumenting_module_name,
|
||||
instrumenting_library_version,
|
||||
schema_url,
|
||||
attributes,
|
||||
)
|
||||
|
||||
|
||||
def _set_tracer_provider(tracer_provider: TracerProvider, log: bool) -> None:
|
||||
def set_tp() -> None:
|
||||
global _TRACER_PROVIDER # pylint: disable=global-statement
|
||||
_TRACER_PROVIDER = tracer_provider
|
||||
|
||||
did_set = _TRACER_PROVIDER_SET_ONCE.do_once(set_tp)
|
||||
|
||||
if log and not did_set:
|
||||
logger.warning("Overriding of current TracerProvider is not allowed")
|
||||
|
||||
|
||||
def set_tracer_provider(tracer_provider: TracerProvider) -> None:
|
||||
"""Sets the current global :class:`~.TracerProvider` object.
|
||||
|
||||
This can only be done once, a warning will be logged if any further attempt
|
||||
is made.
|
||||
"""
|
||||
_set_tracer_provider(tracer_provider, log=True)
|
||||
|
||||
|
||||
def get_tracer_provider() -> TracerProvider:
|
||||
"""Gets the current global :class:`~.TracerProvider` object."""
|
||||
if _TRACER_PROVIDER is None:
|
||||
# if a global tracer provider has not been set either via code or env
|
||||
# vars, return a proxy tracer provider
|
||||
if OTEL_PYTHON_TRACER_PROVIDER not in os.environ:
|
||||
return _PROXY_TRACER_PROVIDER
|
||||
|
||||
tracer_provider: TracerProvider = _load_provider(
|
||||
OTEL_PYTHON_TRACER_PROVIDER, "tracer_provider"
|
||||
)
|
||||
_set_tracer_provider(tracer_provider, log=False)
|
||||
# _TRACER_PROVIDER will have been set by one thread
|
||||
return cast("TracerProvider", _TRACER_PROVIDER)
|
||||
|
||||
|
||||
@_agnosticcontextmanager
|
||||
def use_span(
|
||||
span: Span,
|
||||
end_on_exit: bool = False,
|
||||
record_exception: bool = True,
|
||||
set_status_on_exception: bool = True,
|
||||
) -> Iterator[Span]:
|
||||
"""Takes a non-active span and activates it in the current context.
|
||||
|
||||
Args:
|
||||
span: The span that should be activated in the current context.
|
||||
end_on_exit: Whether to end the span automatically when leaving the
|
||||
context manager scope.
|
||||
record_exception: Whether to record any exceptions raised within the
|
||||
context as error event on the span.
|
||||
set_status_on_exception: Only relevant if the returned span is used
|
||||
in a with/context manager. Defines whether the span status will
|
||||
be automatically set to ERROR when an uncaught exception is
|
||||
raised in the span with block. The span status won't be set by
|
||||
this mechanism if it was previously set manually.
|
||||
"""
|
||||
try:
|
||||
token = context_api.attach(context_api.set_value(_SPAN_KEY, span))
|
||||
try:
|
||||
yield span
|
||||
finally:
|
||||
context_api.detach(token)
|
||||
|
||||
# Record only exceptions that inherit Exception class but not BaseException, because
|
||||
# classes that directly inherit BaseException are not technically errors, e.g. GeneratorExit.
|
||||
# See https://github.com/open-telemetry/opentelemetry-python/issues/4484
|
||||
except Exception as exc: # pylint: disable=broad-exception-caught
|
||||
if isinstance(span, Span) and span.is_recording():
|
||||
# Record the exception as an event
|
||||
if record_exception:
|
||||
span.record_exception(exc)
|
||||
|
||||
# Set status in case exception was raised
|
||||
if set_status_on_exception:
|
||||
span.set_status(
|
||||
Status(
|
||||
status_code=StatusCode.ERROR,
|
||||
description=f"{type(exc).__name__}: {exc}",
|
||||
)
|
||||
)
|
||||
|
||||
# This causes parent spans to set their status to ERROR and to record
|
||||
# an exception as an event if a child span raises an exception even if
|
||||
# such child span was started with both record_exception and
|
||||
# set_status_on_exception attributes set to False.
|
||||
raise
|
||||
|
||||
finally:
|
||||
if end_on_exit:
|
||||
span.end()
|
||||
|
||||
|
||||
__all__ = [
|
||||
"DEFAULT_TRACE_OPTIONS",
|
||||
"DEFAULT_TRACE_STATE",
|
||||
"INVALID_SPAN",
|
||||
"INVALID_SPAN_CONTEXT",
|
||||
"INVALID_SPAN_ID",
|
||||
"INVALID_TRACE_ID",
|
||||
"NonRecordingSpan",
|
||||
"Link",
|
||||
"Span",
|
||||
"SpanContext",
|
||||
"SpanKind",
|
||||
"TraceFlags",
|
||||
"TraceState",
|
||||
"TracerProvider",
|
||||
"Tracer",
|
||||
"format_span_id",
|
||||
"format_trace_id",
|
||||
"get_current_span",
|
||||
"get_tracer",
|
||||
"get_tracer_provider",
|
||||
"set_tracer_provider",
|
||||
"set_span_in_context",
|
||||
"use_span",
|
||||
"Status",
|
||||
"StatusCode",
|
||||
]
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,51 @@
|
||||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# 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.
|
||||
from typing import Optional
|
||||
|
||||
from opentelemetry.context import create_key, get_value, set_value
|
||||
from opentelemetry.context.context import Context
|
||||
from opentelemetry.trace.span import INVALID_SPAN, Span
|
||||
|
||||
SPAN_KEY = "current-span"
|
||||
_SPAN_KEY = create_key("current-span")
|
||||
|
||||
|
||||
def set_span_in_context(
|
||||
span: Span, context: Optional[Context] = None
|
||||
) -> Context:
|
||||
"""Set the span in the given context.
|
||||
|
||||
Args:
|
||||
span: The Span to set.
|
||||
context: a Context object. if one is not passed, the
|
||||
default current context is used instead.
|
||||
"""
|
||||
ctx = set_value(_SPAN_KEY, span, context=context)
|
||||
return ctx
|
||||
|
||||
|
||||
def get_current_span(context: Optional[Context] = None) -> Span:
|
||||
"""Retrieve the current span.
|
||||
|
||||
Args:
|
||||
context: A Context object. If one is not passed, the
|
||||
default current context is used instead.
|
||||
|
||||
Returns:
|
||||
The Span set in the context if it exists. INVALID_SPAN otherwise.
|
||||
"""
|
||||
span = get_value(_SPAN_KEY, context=context)
|
||||
if span is None or not isinstance(span, Span):
|
||||
return INVALID_SPAN
|
||||
return span
|
||||
Binary file not shown.
@@ -0,0 +1,118 @@
|
||||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# 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 re
|
||||
import typing
|
||||
|
||||
from opentelemetry import trace
|
||||
from opentelemetry.context.context import Context
|
||||
from opentelemetry.propagators import textmap
|
||||
from opentelemetry.trace import format_span_id, format_trace_id
|
||||
from opentelemetry.trace.span import TraceState
|
||||
|
||||
|
||||
class TraceContextTextMapPropagator(textmap.TextMapPropagator):
|
||||
"""Extracts and injects using w3c TraceContext's headers."""
|
||||
|
||||
_TRACEPARENT_HEADER_NAME = "traceparent"
|
||||
_TRACESTATE_HEADER_NAME = "tracestate"
|
||||
_TRACEPARENT_HEADER_FORMAT = (
|
||||
"^[ \t]*([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})"
|
||||
+ "(-.*)?[ \t]*$"
|
||||
)
|
||||
_TRACEPARENT_HEADER_FORMAT_RE = re.compile(_TRACEPARENT_HEADER_FORMAT)
|
||||
|
||||
def extract(
|
||||
self,
|
||||
carrier: textmap.CarrierT,
|
||||
context: typing.Optional[Context] = None,
|
||||
getter: textmap.Getter[textmap.CarrierT] = textmap.default_getter,
|
||||
) -> Context:
|
||||
"""Extracts SpanContext from the carrier.
|
||||
|
||||
See `opentelemetry.propagators.textmap.TextMapPropagator.extract`
|
||||
"""
|
||||
if context is None:
|
||||
context = Context()
|
||||
|
||||
header = getter.get(carrier, self._TRACEPARENT_HEADER_NAME)
|
||||
|
||||
if not header:
|
||||
return context
|
||||
|
||||
match = re.search(self._TRACEPARENT_HEADER_FORMAT_RE, header[0])
|
||||
if not match:
|
||||
return context
|
||||
|
||||
version: str = match.group(1)
|
||||
trace_id: str = match.group(2)
|
||||
span_id: str = match.group(3)
|
||||
trace_flags: str = match.group(4)
|
||||
|
||||
if trace_id == "0" * 32 or span_id == "0" * 16:
|
||||
return context
|
||||
|
||||
if version == "00":
|
||||
if match.group(5): # type: ignore
|
||||
return context
|
||||
if version == "ff":
|
||||
return context
|
||||
|
||||
tracestate_headers = getter.get(carrier, self._TRACESTATE_HEADER_NAME)
|
||||
if tracestate_headers is None:
|
||||
tracestate = None
|
||||
else:
|
||||
tracestate = TraceState.from_header(tracestate_headers)
|
||||
|
||||
span_context = trace.SpanContext(
|
||||
trace_id=int(trace_id, 16),
|
||||
span_id=int(span_id, 16),
|
||||
is_remote=True,
|
||||
trace_flags=trace.TraceFlags(int(trace_flags, 16)),
|
||||
trace_state=tracestate,
|
||||
)
|
||||
return trace.set_span_in_context(
|
||||
trace.NonRecordingSpan(span_context), context
|
||||
)
|
||||
|
||||
def inject(
|
||||
self,
|
||||
carrier: textmap.CarrierT,
|
||||
context: typing.Optional[Context] = None,
|
||||
setter: textmap.Setter[textmap.CarrierT] = textmap.default_setter,
|
||||
) -> None:
|
||||
"""Injects SpanContext into the carrier.
|
||||
|
||||
See `opentelemetry.propagators.textmap.TextMapPropagator.inject`
|
||||
"""
|
||||
span = trace.get_current_span(context)
|
||||
span_context = span.get_span_context()
|
||||
if span_context == trace.INVALID_SPAN_CONTEXT:
|
||||
return
|
||||
traceparent_string = f"00-{format_trace_id(span_context.trace_id)}-{format_span_id(span_context.span_id)}-{span_context.trace_flags:02x}"
|
||||
setter.set(carrier, self._TRACEPARENT_HEADER_NAME, traceparent_string)
|
||||
if span_context.trace_state:
|
||||
tracestate_string = span_context.trace_state.to_header()
|
||||
setter.set(
|
||||
carrier, self._TRACESTATE_HEADER_NAME, tracestate_string
|
||||
)
|
||||
|
||||
@property
|
||||
def fields(self) -> typing.Set[str]:
|
||||
"""Returns a set with the fields set in `inject`.
|
||||
|
||||
See
|
||||
`opentelemetry.propagators.textmap.TextMapPropagator.fields`
|
||||
"""
|
||||
return {self._TRACEPARENT_HEADER_NAME, self._TRACESTATE_HEADER_NAME}
|
||||
@@ -0,0 +1,608 @@
|
||||
import abc
|
||||
import logging
|
||||
import re
|
||||
import types as python_types
|
||||
import typing
|
||||
import warnings
|
||||
|
||||
from opentelemetry.trace.status import Status, StatusCode
|
||||
from opentelemetry.util import types
|
||||
|
||||
# The key MUST begin with a lowercase letter or a digit,
|
||||
# and can only contain lowercase letters (a-z), digits (0-9),
|
||||
# underscores (_), dashes (-), asterisks (*), and forward slashes (/).
|
||||
# For multi-tenant vendor scenarios, an at sign (@) can be used to
|
||||
# prefix the vendor name. Vendors SHOULD set the tenant ID
|
||||
# at the beginning of the key.
|
||||
|
||||
# key = ( lcalpha ) 0*255( lcalpha / DIGIT / "_" / "-"/ "*" / "/" )
|
||||
# key = ( lcalpha / DIGIT ) 0*240( lcalpha / DIGIT / "_" / "-"/ "*" / "/" ) "@" lcalpha 0*13( lcalpha / DIGIT / "_" / "-"/ "*" / "/" )
|
||||
# lcalpha = %x61-7A ; a-z
|
||||
|
||||
_KEY_FORMAT = (
|
||||
r"[a-z][_0-9a-z\-\*\/]{0,255}|"
|
||||
r"[a-z0-9][_0-9a-z\-\*\/]{0,240}@[a-z][_0-9a-z\-\*\/]{0,13}"
|
||||
)
|
||||
_KEY_PATTERN = re.compile(_KEY_FORMAT)
|
||||
|
||||
# The value is an opaque string containing up to 256 printable
|
||||
# ASCII [RFC0020] characters (i.e., the range 0x20 to 0x7E)
|
||||
# except comma (,) and (=).
|
||||
# value = 0*255(chr) nblk-chr
|
||||
# nblk-chr = %x21-2B / %x2D-3C / %x3E-7E
|
||||
# chr = %x20 / nblk-chr
|
||||
|
||||
_VALUE_FORMAT = (
|
||||
r"[\x20-\x2b\x2d-\x3c\x3e-\x7e]{0,255}[\x21-\x2b\x2d-\x3c\x3e-\x7e]"
|
||||
)
|
||||
_VALUE_PATTERN = re.compile(_VALUE_FORMAT)
|
||||
|
||||
|
||||
_TRACECONTEXT_MAXIMUM_TRACESTATE_KEYS = 32
|
||||
_delimiter_pattern = re.compile(r"[ \t]*,[ \t]*")
|
||||
_member_pattern = re.compile(f"({_KEY_FORMAT})(=)({_VALUE_FORMAT})[ \t]*")
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _is_valid_pair(key: str, value: str) -> bool:
|
||||
return (
|
||||
isinstance(key, str)
|
||||
and _KEY_PATTERN.fullmatch(key) is not None
|
||||
and isinstance(value, str)
|
||||
and _VALUE_PATTERN.fullmatch(value) is not None
|
||||
)
|
||||
|
||||
|
||||
class Span(abc.ABC):
|
||||
"""A span represents a single operation within a trace."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def end(self, end_time: typing.Optional[int] = None) -> None:
|
||||
"""Sets the current time as the span's end time.
|
||||
|
||||
The span's end time is the wall time at which the operation finished.
|
||||
|
||||
Only the first call to `end` should modify the span, and
|
||||
implementations are free to ignore or raise on further calls.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_span_context(self) -> "SpanContext":
|
||||
"""Gets the span's SpanContext.
|
||||
|
||||
Get an immutable, serializable identifier for this span that can be
|
||||
used to create new child spans.
|
||||
|
||||
Returns:
|
||||
A :class:`opentelemetry.trace.SpanContext` with a copy of this span's immutable state.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_attributes(
|
||||
self, attributes: typing.Mapping[str, types.AttributeValue]
|
||||
) -> None:
|
||||
"""Sets Attributes.
|
||||
|
||||
Sets Attributes with the key and value passed as arguments dict.
|
||||
|
||||
Note: The behavior of `None` value attributes is undefined, and hence
|
||||
strongly discouraged. It is also preferred to set attributes at span
|
||||
creation, instead of calling this method later since samplers can only
|
||||
consider information already present during span creation.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_attribute(self, key: str, value: types.AttributeValue) -> None:
|
||||
"""Sets an Attribute.
|
||||
|
||||
Sets a single Attribute with the key and value passed as arguments.
|
||||
|
||||
Note: The behavior of `None` value attributes is undefined, and hence
|
||||
strongly discouraged. It is also preferred to set attributes at span
|
||||
creation, instead of calling this method later since samplers can only
|
||||
consider information already present during span creation.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def add_event(
|
||||
self,
|
||||
name: str,
|
||||
attributes: types.Attributes = None,
|
||||
timestamp: typing.Optional[int] = None,
|
||||
) -> None:
|
||||
"""Adds an `Event`.
|
||||
|
||||
Adds a single `Event` with the name and, optionally, a timestamp and
|
||||
attributes passed as arguments. Implementations should generate a
|
||||
timestamp if the `timestamp` argument is omitted.
|
||||
"""
|
||||
|
||||
def add_link( # pylint: disable=no-self-use
|
||||
self,
|
||||
context: "SpanContext",
|
||||
attributes: types.Attributes = None,
|
||||
) -> None:
|
||||
"""Adds a `Link`.
|
||||
|
||||
Adds a single `Link` with the `SpanContext` of the span to link to and,
|
||||
optionally, attributes passed as arguments. Implementations may ignore
|
||||
calls with an invalid span context if both attributes and TraceState
|
||||
are empty.
|
||||
|
||||
Note: It is preferred to add links at span creation, instead of calling
|
||||
this method later since samplers can only consider information already
|
||||
present during span creation.
|
||||
"""
|
||||
warnings.warn(
|
||||
"Span.add_link() not implemented and will be a no-op. "
|
||||
"Use opentelemetry-sdk >= 1.23 to add links after span creation"
|
||||
)
|
||||
|
||||
@abc.abstractmethod
|
||||
def update_name(self, name: str) -> None:
|
||||
"""Updates the `Span` name.
|
||||
|
||||
This will override the name provided via :func:`opentelemetry.trace.Tracer.start_span`.
|
||||
|
||||
Upon this update, any sampling behavior based on Span name will depend
|
||||
on the implementation.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def is_recording(self) -> bool:
|
||||
"""Returns whether this span will be recorded.
|
||||
|
||||
Returns true if this Span is active and recording information like
|
||||
events with the add_event operation and attributes using set_attribute.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_status(
|
||||
self,
|
||||
status: typing.Union[Status, StatusCode],
|
||||
description: typing.Optional[str] = None,
|
||||
) -> None:
|
||||
"""Sets the Status of the Span. If used, this will override the default
|
||||
Span status.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def record_exception(
|
||||
self,
|
||||
exception: BaseException,
|
||||
attributes: types.Attributes = None,
|
||||
timestamp: typing.Optional[int] = None,
|
||||
escaped: bool = False,
|
||||
) -> None:
|
||||
"""Records an exception as a span event."""
|
||||
|
||||
def __enter__(self) -> "Span":
|
||||
"""Invoked when `Span` is used as a context manager.
|
||||
|
||||
Returns the `Span` itself.
|
||||
"""
|
||||
return self
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: typing.Optional[typing.Type[BaseException]],
|
||||
exc_val: typing.Optional[BaseException],
|
||||
exc_tb: typing.Optional[python_types.TracebackType],
|
||||
) -> None:
|
||||
"""Ends context manager and calls `end` on the `Span`."""
|
||||
|
||||
self.end()
|
||||
|
||||
|
||||
class TraceFlags(int):
|
||||
"""A bitmask that represents options specific to the trace.
|
||||
|
||||
The only supported option is the "sampled" flag (``0x01``). If set, this
|
||||
flag indicates that the trace may have been sampled upstream.
|
||||
|
||||
See the `W3C Trace Context - Traceparent`_ spec for details.
|
||||
|
||||
.. _W3C Trace Context - Traceparent:
|
||||
https://www.w3.org/TR/trace-context/#trace-flags
|
||||
"""
|
||||
|
||||
DEFAULT = 0x00
|
||||
SAMPLED = 0x01
|
||||
|
||||
@classmethod
|
||||
def get_default(cls) -> "TraceFlags":
|
||||
return cls(cls.DEFAULT)
|
||||
|
||||
@property
|
||||
def sampled(self) -> bool:
|
||||
return bool(self & TraceFlags.SAMPLED)
|
||||
|
||||
|
||||
DEFAULT_TRACE_OPTIONS = TraceFlags.get_default()
|
||||
|
||||
|
||||
class TraceState(typing.Mapping[str, str]):
|
||||
"""A list of key-value pairs representing vendor-specific trace info.
|
||||
|
||||
Keys and values are strings of up to 256 printable US-ASCII characters.
|
||||
Implementations should conform to the `W3C Trace Context - Tracestate`_
|
||||
spec, which describes additional restrictions on valid field values.
|
||||
|
||||
.. _W3C Trace Context - Tracestate:
|
||||
https://www.w3.org/TR/trace-context/#tracestate-field
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
entries: typing.Optional[
|
||||
typing.Sequence[typing.Tuple[str, str]]
|
||||
] = None,
|
||||
) -> None:
|
||||
self._dict = {} # type: dict[str, str]
|
||||
if entries is None:
|
||||
return
|
||||
if len(entries) > _TRACECONTEXT_MAXIMUM_TRACESTATE_KEYS:
|
||||
_logger.warning(
|
||||
"There can't be more than %s key/value pairs.",
|
||||
_TRACECONTEXT_MAXIMUM_TRACESTATE_KEYS,
|
||||
)
|
||||
return
|
||||
|
||||
for key, value in entries:
|
||||
if _is_valid_pair(key, value):
|
||||
if key in self._dict:
|
||||
_logger.warning("Duplicate key: %s found.", key)
|
||||
continue
|
||||
self._dict[key] = value
|
||||
else:
|
||||
_logger.warning(
|
||||
"Invalid key/value pair (%s, %s) found.", key, value
|
||||
)
|
||||
|
||||
def __contains__(self, item: object) -> bool:
|
||||
return item in self._dict
|
||||
|
||||
def __getitem__(self, key: str) -> str:
|
||||
return self._dict[key]
|
||||
|
||||
def __iter__(self) -> typing.Iterator[str]:
|
||||
return iter(self._dict)
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self._dict)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
pairs = [
|
||||
f"{{key={key}, value={value}}}"
|
||||
for key, value in self._dict.items()
|
||||
]
|
||||
return str(pairs)
|
||||
|
||||
def add(self, key: str, value: str) -> "TraceState":
|
||||
"""Adds a key-value pair to tracestate. The provided pair should
|
||||
adhere to w3c tracestate identifiers format.
|
||||
|
||||
Args:
|
||||
key: A valid tracestate key to add
|
||||
value: A valid tracestate value to add
|
||||
|
||||
Returns:
|
||||
A new TraceState with the modifications applied.
|
||||
|
||||
If the provided key-value pair is invalid or results in tracestate
|
||||
that violates tracecontext specification, they are discarded and
|
||||
same tracestate will be returned.
|
||||
"""
|
||||
if not _is_valid_pair(key, value):
|
||||
_logger.warning(
|
||||
"Invalid key/value pair (%s, %s) found.", key, value
|
||||
)
|
||||
return self
|
||||
# There can be a maximum of 32 pairs
|
||||
if len(self) >= _TRACECONTEXT_MAXIMUM_TRACESTATE_KEYS:
|
||||
_logger.warning("There can't be more 32 key/value pairs.")
|
||||
return self
|
||||
# Duplicate entries are not allowed
|
||||
if key in self._dict:
|
||||
_logger.warning("The provided key %s already exists.", key)
|
||||
return self
|
||||
new_state = [(key, value)] + list(self._dict.items())
|
||||
return TraceState(new_state)
|
||||
|
||||
def update(self, key: str, value: str) -> "TraceState":
|
||||
"""Updates a key-value pair in tracestate. The provided pair should
|
||||
adhere to w3c tracestate identifiers format.
|
||||
|
||||
Args:
|
||||
key: A valid tracestate key to update
|
||||
value: A valid tracestate value to update for key
|
||||
|
||||
Returns:
|
||||
A new TraceState with the modifications applied.
|
||||
|
||||
If the provided key-value pair is invalid or results in tracestate
|
||||
that violates tracecontext specification, they are discarded and
|
||||
same tracestate will be returned.
|
||||
"""
|
||||
if not _is_valid_pair(key, value):
|
||||
_logger.warning(
|
||||
"Invalid key/value pair (%s, %s) found.", key, value
|
||||
)
|
||||
return self
|
||||
prev_state = self._dict.copy()
|
||||
prev_state.pop(key, None)
|
||||
new_state = [(key, value), *prev_state.items()]
|
||||
return TraceState(new_state)
|
||||
|
||||
def delete(self, key: str) -> "TraceState":
|
||||
"""Deletes a key-value from tracestate.
|
||||
|
||||
Args:
|
||||
key: A valid tracestate key to remove key-value pair from tracestate
|
||||
|
||||
Returns:
|
||||
A new TraceState with the modifications applied.
|
||||
|
||||
If the provided key-value pair is invalid or results in tracestate
|
||||
that violates tracecontext specification, they are discarded and
|
||||
same tracestate will be returned.
|
||||
"""
|
||||
if key not in self._dict:
|
||||
_logger.warning("The provided key %s doesn't exist.", key)
|
||||
return self
|
||||
prev_state = self._dict.copy()
|
||||
prev_state.pop(key)
|
||||
new_state = list(prev_state.items())
|
||||
return TraceState(new_state)
|
||||
|
||||
def to_header(self) -> str:
|
||||
"""Creates a w3c tracestate header from a TraceState.
|
||||
|
||||
Returns:
|
||||
A string that adheres to the w3c tracestate
|
||||
header format.
|
||||
"""
|
||||
return ",".join(key + "=" + value for key, value in self._dict.items())
|
||||
|
||||
@classmethod
|
||||
def from_header(cls, header_list: typing.List[str]) -> "TraceState":
|
||||
"""Parses one or more w3c tracestate header into a TraceState.
|
||||
|
||||
Args:
|
||||
header_list: one or more w3c tracestate headers.
|
||||
|
||||
Returns:
|
||||
A valid TraceState that contains values extracted from
|
||||
the tracestate header.
|
||||
|
||||
If the format of one headers is illegal, all values will
|
||||
be discarded and an empty tracestate will be returned.
|
||||
|
||||
If the number of keys is beyond the maximum, all values
|
||||
will be discarded and an empty tracestate will be returned.
|
||||
"""
|
||||
pairs = {} # type: dict[str, str]
|
||||
for header in header_list:
|
||||
members: typing.List[str] = re.split(_delimiter_pattern, header)
|
||||
for member in members:
|
||||
# empty members are valid, but no need to process further.
|
||||
if not member:
|
||||
continue
|
||||
match = _member_pattern.fullmatch(member)
|
||||
if not match:
|
||||
_logger.warning(
|
||||
"Member doesn't match the w3c identifiers format %s",
|
||||
member,
|
||||
)
|
||||
return cls()
|
||||
groups: typing.Tuple[str, ...] = match.groups()
|
||||
key, _eq, value = groups
|
||||
# duplicate keys are not legal in header
|
||||
if key in pairs:
|
||||
return cls()
|
||||
pairs[key] = value
|
||||
return cls(list(pairs.items()))
|
||||
|
||||
@classmethod
|
||||
def get_default(cls) -> "TraceState":
|
||||
return cls()
|
||||
|
||||
def keys(self) -> typing.KeysView[str]:
|
||||
return self._dict.keys()
|
||||
|
||||
def items(self) -> typing.ItemsView[str, str]:
|
||||
return self._dict.items()
|
||||
|
||||
def values(self) -> typing.ValuesView[str]:
|
||||
return self._dict.values()
|
||||
|
||||
|
||||
DEFAULT_TRACE_STATE = TraceState.get_default()
|
||||
_TRACE_ID_MAX_VALUE = 2**128 - 1
|
||||
_SPAN_ID_MAX_VALUE = 2**64 - 1
|
||||
|
||||
|
||||
class SpanContext(
|
||||
typing.Tuple[int, int, bool, "TraceFlags", "TraceState", bool]
|
||||
):
|
||||
"""The state of a Span to propagate between processes.
|
||||
|
||||
This class includes the immutable attributes of a :class:`.Span` that must
|
||||
be propagated to a span's children and across process boundaries.
|
||||
|
||||
Args:
|
||||
trace_id: The ID of the trace that this span belongs to.
|
||||
span_id: This span's ID.
|
||||
is_remote: True if propagated from a remote parent.
|
||||
trace_flags: Trace options to propagate.
|
||||
trace_state: Tracing-system-specific info to propagate.
|
||||
"""
|
||||
|
||||
def __new__(
|
||||
cls,
|
||||
trace_id: int,
|
||||
span_id: int,
|
||||
is_remote: bool,
|
||||
trace_flags: typing.Optional["TraceFlags"] = DEFAULT_TRACE_OPTIONS,
|
||||
trace_state: typing.Optional["TraceState"] = DEFAULT_TRACE_STATE,
|
||||
) -> "SpanContext":
|
||||
if trace_flags is None:
|
||||
trace_flags = DEFAULT_TRACE_OPTIONS
|
||||
if trace_state is None:
|
||||
trace_state = DEFAULT_TRACE_STATE
|
||||
|
||||
is_valid = (
|
||||
INVALID_TRACE_ID < trace_id <= _TRACE_ID_MAX_VALUE
|
||||
and INVALID_SPAN_ID < span_id <= _SPAN_ID_MAX_VALUE
|
||||
)
|
||||
|
||||
return tuple.__new__(
|
||||
cls,
|
||||
(trace_id, span_id, is_remote, trace_flags, trace_state, is_valid),
|
||||
)
|
||||
|
||||
def __getnewargs__(
|
||||
self,
|
||||
) -> typing.Tuple[int, int, bool, "TraceFlags", "TraceState"]:
|
||||
return (
|
||||
self.trace_id,
|
||||
self.span_id,
|
||||
self.is_remote,
|
||||
self.trace_flags,
|
||||
self.trace_state,
|
||||
)
|
||||
|
||||
@property
|
||||
def trace_id(self) -> int:
|
||||
return self[0] # pylint: disable=unsubscriptable-object
|
||||
|
||||
@property
|
||||
def span_id(self) -> int:
|
||||
return self[1] # pylint: disable=unsubscriptable-object
|
||||
|
||||
@property
|
||||
def is_remote(self) -> bool:
|
||||
return self[2] # pylint: disable=unsubscriptable-object
|
||||
|
||||
@property
|
||||
def trace_flags(self) -> "TraceFlags":
|
||||
return self[3] # pylint: disable=unsubscriptable-object
|
||||
|
||||
@property
|
||||
def trace_state(self) -> "TraceState":
|
||||
return self[4] # pylint: disable=unsubscriptable-object
|
||||
|
||||
@property
|
||||
def is_valid(self) -> bool:
|
||||
return self[5] # pylint: disable=unsubscriptable-object
|
||||
|
||||
def __setattr__(self, *args: str) -> None:
|
||||
_logger.debug(
|
||||
"Immutable type, ignoring call to set attribute", stack_info=True
|
||||
)
|
||||
|
||||
def __delattr__(self, *args: str) -> None:
|
||||
_logger.debug(
|
||||
"Immutable type, ignoring call to set attribute", stack_info=True
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{type(self).__name__}(trace_id=0x{format_trace_id(self.trace_id)}, span_id=0x{format_span_id(self.span_id)}, trace_flags=0x{self.trace_flags:02x}, trace_state={self.trace_state!r}, is_remote={self.is_remote})"
|
||||
|
||||
|
||||
class NonRecordingSpan(Span):
|
||||
"""The Span that is used when no Span implementation is available.
|
||||
|
||||
All operations are no-op except context propagation.
|
||||
"""
|
||||
|
||||
def __init__(self, context: "SpanContext") -> None:
|
||||
self._context = context
|
||||
|
||||
def get_span_context(self) -> "SpanContext":
|
||||
return self._context
|
||||
|
||||
def is_recording(self) -> bool:
|
||||
return False
|
||||
|
||||
def end(self, end_time: typing.Optional[int] = None) -> None:
|
||||
pass
|
||||
|
||||
def set_attributes(
|
||||
self, attributes: typing.Mapping[str, types.AttributeValue]
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
def set_attribute(self, key: str, value: types.AttributeValue) -> None:
|
||||
pass
|
||||
|
||||
def add_event(
|
||||
self,
|
||||
name: str,
|
||||
attributes: types.Attributes = None,
|
||||
timestamp: typing.Optional[int] = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
def add_link(
|
||||
self,
|
||||
context: "SpanContext",
|
||||
attributes: types.Attributes = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
def update_name(self, name: str) -> None:
|
||||
pass
|
||||
|
||||
def set_status(
|
||||
self,
|
||||
status: typing.Union[Status, StatusCode],
|
||||
description: typing.Optional[str] = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
def record_exception(
|
||||
self,
|
||||
exception: BaseException,
|
||||
attributes: types.Attributes = None,
|
||||
timestamp: typing.Optional[int] = None,
|
||||
escaped: bool = False,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"NonRecordingSpan({self._context!r})"
|
||||
|
||||
|
||||
INVALID_SPAN_ID = 0x0000000000000000
|
||||
INVALID_TRACE_ID = 0x00000000000000000000000000000000
|
||||
INVALID_SPAN_CONTEXT = SpanContext(
|
||||
trace_id=INVALID_TRACE_ID,
|
||||
span_id=INVALID_SPAN_ID,
|
||||
is_remote=False,
|
||||
trace_flags=DEFAULT_TRACE_OPTIONS,
|
||||
trace_state=DEFAULT_TRACE_STATE,
|
||||
)
|
||||
INVALID_SPAN = NonRecordingSpan(INVALID_SPAN_CONTEXT)
|
||||
|
||||
|
||||
def format_trace_id(trace_id: int) -> str:
|
||||
"""Convenience trace ID formatting method
|
||||
Args:
|
||||
trace_id: Trace ID int
|
||||
|
||||
Returns:
|
||||
The trace ID (16 bytes) cast to a 32-character hexadecimal string
|
||||
"""
|
||||
return format(trace_id, "032x")
|
||||
|
||||
|
||||
def format_span_id(span_id: int) -> str:
|
||||
"""Convenience span ID formatting method
|
||||
Args:
|
||||
span_id: Span ID int
|
||||
|
||||
Returns:
|
||||
The span ID (8 bytes) cast to a 16-character hexadecimal string
|
||||
"""
|
||||
return format(span_id, "016x")
|
||||
@@ -0,0 +1,82 @@
|
||||
# Copyright The OpenTelemetry Authors
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# 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 enum
|
||||
import logging
|
||||
import typing
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StatusCode(enum.Enum):
|
||||
"""Represents the canonical set of status codes of a finished Span."""
|
||||
|
||||
UNSET = 0
|
||||
"""The default status."""
|
||||
|
||||
OK = 1
|
||||
"""The operation has been validated by an Application developer or Operator to have completed successfully."""
|
||||
|
||||
ERROR = 2
|
||||
"""The operation contains an error."""
|
||||
|
||||
|
||||
class Status:
|
||||
"""Represents the status of a finished Span.
|
||||
|
||||
Args:
|
||||
status_code: The canonical status code that describes the result
|
||||
status of the operation.
|
||||
description: An optional description of the status.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
status_code: StatusCode = StatusCode.UNSET,
|
||||
description: typing.Optional[str] = None,
|
||||
):
|
||||
self._status_code = status_code
|
||||
self._description = None
|
||||
|
||||
if description:
|
||||
if not isinstance(description, str):
|
||||
logger.warning("Invalid status description type, expected str")
|
||||
return
|
||||
if status_code is not StatusCode.ERROR:
|
||||
logger.warning(
|
||||
"description should only be set when status_code is set to StatusCode.ERROR"
|
||||
)
|
||||
return
|
||||
|
||||
self._description = description
|
||||
|
||||
@property
|
||||
def status_code(self) -> StatusCode:
|
||||
"""Represents the canonical status code of a finished Span."""
|
||||
return self._status_code
|
||||
|
||||
@property
|
||||
def description(self) -> typing.Optional[str]:
|
||||
"""Status description"""
|
||||
return self._description
|
||||
|
||||
@property
|
||||
def is_ok(self) -> bool:
|
||||
"""Returns false if this represents an error, true otherwise."""
|
||||
return self.is_unset or self._status_code is StatusCode.OK
|
||||
|
||||
@property
|
||||
def is_unset(self) -> bool:
|
||||
"""Returns true if unset, false otherwise."""
|
||||
return self._status_code is StatusCode.UNSET
|
||||
Reference in New Issue
Block a user