From 7c1575719f6054f6a9f5b9176852f5ecde61d59c Mon Sep 17 00:00:00 2001 From: Corvin Lasogga Date: Sun, 15 Jan 2023 20:29:16 +0100 Subject: [PATCH 01/10] adapted design from _aio_client.py to support official grpc.*ClientInterceptor-interfaces --- .../instrumentation/grpc/__init__.py | 17 +- .../instrumentation/grpc/_client.py | 342 ++++++++++++------ 2 files changed, 239 insertions(+), 120 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/__init__.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/__init__.py index 25010e147b..1f224b1c31 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/__init__.py @@ -285,7 +285,6 @@ async def serve(): negate, service_name, ) -from opentelemetry.instrumentation.grpc.grpcext import intercept_channel from opentelemetry.instrumentation.grpc.package import _instruments from opentelemetry.instrumentation.grpc.version import __version__ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor @@ -469,11 +468,10 @@ def _uninstrument(self, **kwargs): def wrapper_fn(self, original_func, instance, args, kwargs): channel = original_func(*args, **kwargs) tracer_provider = kwargs.get("tracer_provider") - return intercept_channel( + return grpc.intercept_channel( channel, - client_interceptor( - tracer_provider=tracer_provider, - filter_=self._filter, + *client_interceptors( + tracer_provider=tracer_provider, filter_=self._filter ), ) @@ -541,7 +539,7 @@ def _uninstrument(self, **kwargs): grpc.aio.secure_channel = self._original_secure -def client_interceptor(tracer_provider=None, filter_=None): +def client_interceptors(tracer_provider=None, filter_=None): """Create a gRPC client channel interceptor. Args: @@ -558,7 +556,12 @@ def client_interceptor(tracer_provider=None, filter_=None): tracer = trace.get_tracer(__name__, __version__, tracer_provider) - return _client.OpenTelemetryClientInterceptor(tracer, filter_=filter_) + return [ + _client.UnaryUnaryClientInterceptor(tracer, filter_=filter_), + _client.UnaryStreamClientInterceptor(tracer, filter_=filter_), + _client.StreamUnaryClientInterceptor(tracer, filter_=filter_), + _client.StreamStreamClientInterceptor(tracer, filter_=filter_), + ] def server_interceptor(tracer_provider=None, filter_=None): diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py index 55a46d4a49..dbefba320b 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py @@ -20,17 +20,17 @@ """Implementation of the invocation-side open-telemetry interceptor.""" from collections import OrderedDict +import functools from typing import MutableMapping import grpc +from grpc._interceptor import _ClientCallDetails from opentelemetry import context, trace -from opentelemetry.instrumentation.grpc import grpcext -from opentelemetry.instrumentation.grpc._utilities import RpcInfo from opentelemetry.instrumentation.utils import _SUPPRESS_INSTRUMENTATION_KEY from opentelemetry.propagate import inject from opentelemetry.propagators.textmap import Setter -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv.trace import RpcSystemValues, SpanAttributes from opentelemetry.trace.status import Status, StatusCode @@ -46,33 +46,87 @@ def set(self, carrier: MutableMapping[str, str], key: str, value: str): _carrier_setter = _CarrierSetter() -def _make_future_done_callback(span, rpc_info): +def _unary_done_callback(span): def callback(response_future): with trace.use_span(span, end_on_exit=True): code = response_future.code() if code != grpc.StatusCode.OK: - rpc_info.error = code - return - response = response_future.result() - rpc_info.response = response + details = response_future.details() + span.set_attribute( + SpanAttributes.RPC_GRPC_STATUS_CODE, code.value[0] + ) + span.set_status( + Status( + status_code=StatusCode.ERROR, + description=f"{code}: {details}", + ) + ) + + try: + span.record_exception(response_future.exception()) + except grpc.FutureCancelledError: + pass return callback -class OpenTelemetryClientInterceptor( - grpcext.UnaryClientInterceptor, grpcext.StreamClientInterceptor -): +class _BaseClientInterceptor: + def __init__(self, tracer, filter_=None): self._tracer = tracer self._filter = filter_ + @staticmethod + def propagate_trace_in_details(client_call_details): + """Propagates """ + metadata = client_call_details.metadata + if not metadata: + mutable_metadata = OrderedDict() + else: + mutable_metadata = OrderedDict(metadata) + + inject(mutable_metadata, setter=_carrier_setter) + metadata = tuple(mutable_metadata.items()) + + return _ClientCallDetails( + client_call_details.method, + client_call_details.timeout, + metadata, + # credentials, wait_for_ready, compression + *client_call_details[3:] + ) + + @staticmethod + def add_error_details_to_span( + span: trace.Span, + exc: Exception, + ) -> None: + """Adds error and details to an active span. + + Args: + span: The active span. + exc: The exception to get code and details from. + """ + if isinstance(exc, grpc.RpcError): + span.set_attribute( + SpanAttributes.RPC_GRPC_STATUS_CODE, + exc.code().value[0], + ) + span.set_status( + Status( + status_code=StatusCode.ERROR, + description=f"{type(exc).__name__}: {exc}", + ) + ) + span.record_exception(exc) + def _start_span(self, method, **kwargs): service, meth = method.lstrip("/").split("/", 1) attributes = { - SpanAttributes.RPC_SYSTEM: "grpc", - SpanAttributes.RPC_GRPC_STATUS_CODE: grpc.StatusCode.OK.value[0], - SpanAttributes.RPC_METHOD: meth, + SpanAttributes.RPC_SYSTEM: RpcSystemValues.GRPC.value, SpanAttributes.RPC_SERVICE: service, + SpanAttributes.RPC_METHOD: meth, + SpanAttributes.RPC_GRPC_STATUS_CODE: grpc.StatusCode.OK.value[0], } return self._tracer.start_as_current_span( @@ -82,123 +136,185 @@ def _start_span(self, method, **kwargs): **kwargs, ) - # pylint:disable=no-self-use - def _trace_result(self, span, rpc_info, result): + def _wrap_unary_response(self, span, continuation): + response_future = None + try: + response_future = continuation() + except Exception as exc: + self.add_error_details_to_span(span, exc) + raise exc + finally: + if not response_future: + span.end() + # If the RPC is called asynchronously, add a callback to end the span # when the future is done, else end the span immediately - if isinstance(result, grpc.Future): - result.add_done_callback( - _make_future_done_callback(span, rpc_info) - ) - return result - response = result - # Handle the case when the RPC is initiated via the with_call - # method and the result is a tuple with the first element as the - # response. - # http://www.grpc.io/grpc/python/grpc.html#grpc.UnaryUnaryMultiCallable.with_call - if isinstance(result, tuple): - response = result[0] - rpc_info.response = response + if isinstance( response_future, grpc.Future): + response_future.add_done_callback(_unary_done_callback(span)) + return response_future + span.end() - return result + return response_future - def _intercept(self, request, metadata, client_info, invoker): - if context.get_value(_SUPPRESS_INSTRUMENTATION_KEY): - return invoker(request, metadata) + def _wrap_stream_response(self, span, call): + try: + yield from call + except Exception as exc: + self.add_error_details_to_span(span, exc) + raise exc + finally: + span.end() + + + def tracing_skipped( + self, + client_call_details: grpc.ClientCallDetails + ) -> bool: + """Returns whether a call is supposed to be skipped for tracing. + + Args: + client_call_details: A :py:class:`~grpc.ClientCallDetails`-object, + describing the outgoing RPC. + + Returns: + True if: + + - no filter is set, + - the :py:class:`~grpc.ClientCallDetails` matches a set filter, + - the instrumentation is suppressed, + + False otherwise. + """ + return ( + context.get_value(_SUPPRESS_INSTRUMENTATION_KEY) + or not self.rpc_matches_filters(client_call_details) + ) + + def rpc_matches_filters( + self, + client_call_details: grpc.ClientCallDetails + ) -> bool: + """Returns whether the :py:class:`~grpc.ClientCallDetails` matches a + set `filter_`. + + Args: + client_call_details: A :py:class:`~grpc.ClientCallDetails`-object, + describing the outgoing RPC. + + Returns: + True if no filter is set or the :py:class:`~grpc.ClientCallDetails` + matches a set filter, False otherwise. + """ + return self._filter is None or self._filter(client_call_details) + + +class UnaryUnaryClientInterceptor( + grpc.UnaryUnaryClientInterceptor, + _BaseClientInterceptor, +): + + def intercept_unary_unary( + self, + continuation, + client_call_details, + request + ): + if self.tracing_skipped(client_call_details): + return continuation(client_call_details, request) - if not metadata: - mutable_metadata = OrderedDict() - else: - mutable_metadata = OrderedDict(metadata) with self._start_span( - client_info.full_method, + client_call_details.method, end_on_exit=False, record_exception=False, set_status_on_exception=False, ) as span: - result = None - try: - inject(mutable_metadata, setter=_carrier_setter) - metadata = tuple(mutable_metadata.items()) - - rpc_info = RpcInfo( - full_method=client_info.full_method, - metadata=metadata, - timeout=client_info.timeout, - request=request, - ) + new_details = self.propagate_trace_in_details(client_call_details) - result = invoker(request, metadata) - except Exception as exc: - if isinstance(exc, grpc.RpcError): - span.set_attribute( - SpanAttributes.RPC_GRPC_STATUS_CODE, - exc.code().value[0], - ) - span.set_status( - Status( - status_code=StatusCode.ERROR, - description=f"{type(exc).__name__}: {exc}", - ) - ) - span.record_exception(exc) - raise exc - finally: - if not result: - span.end() - return self._trace_result(span, rpc_info, result) - - def intercept_unary(self, request, metadata, client_info, invoker): - if self._filter is not None and not self._filter(client_info): - return invoker(request, metadata) - return self._intercept(request, metadata, client_info, invoker) - - # For RPCs that stream responses, the result can be a generator. To record - # the span across the generated responses and detect any errors, we wrap - # the result in a new generator that yields the response values. - def _intercept_server_stream( - self, request_or_iterator, metadata, client_info, invoker + continuation_with_args = functools.partial( + continuation, new_details, request + ) + + return self._wrap_unary_response(span, continuation_with_args) + + +class StreamUnaryClientInterceptor( + grpc.StreamUnaryClientInterceptor, + _BaseClientInterceptor, +): + + def intercept_stream_unary( + self, + continuation, + client_call_details, + request_iterator ): - if not metadata: - mutable_metadata = OrderedDict() - else: - mutable_metadata = OrderedDict(metadata) + if self.tracing_skipped(client_call_details): + return continuation(client_call_details, request_iterator) + + with self._start_span( + client_call_details.method, + end_on_exit=False, + record_exception=False, + set_status_on_exception=False, + ) as span: + new_details = self.propagate_trace_in_details(client_call_details) - with self._start_span(client_info.full_method) as span: - inject(mutable_metadata, setter=_carrier_setter) - metadata = tuple(mutable_metadata.items()) - rpc_info = RpcInfo( - full_method=client_info.full_method, - metadata=metadata, - timeout=client_info.timeout, + continuation_with_args = functools.partial( + continuation, new_details, request_iterator ) + return self._wrap_unary_response(span, continuation_with_args) - if client_info.is_client_stream: - rpc_info.request = request_or_iterator - try: - yield from invoker(request_or_iterator, metadata) - except grpc.RpcError as err: - span.set_status(Status(StatusCode.ERROR)) - span.set_attribute( - SpanAttributes.RPC_GRPC_STATUS_CODE, err.code().value[0] - ) - raise err +class UnaryStreamClientInterceptor( + grpc.UnaryStreamClientInterceptor, + _BaseClientInterceptor, +): - def intercept_stream( - self, request_or_iterator, metadata, client_info, invoker + def intercept_unary_stream( + self, + continuation, + client_call_details, + request ): - if context.get_value(_SUPPRESS_INSTRUMENTATION_KEY): - return invoker(request_or_iterator, metadata) + if self.tracing_skipped(client_call_details): + return continuation(client_call_details, request) + + with self._start_span( + client_call_details.method, + end_on_exit=False, + record_exception=False, + set_status_on_exception=False, + ) as span: + new_details = self.propagate_trace_in_details(client_call_details) - if self._filter is not None and not self._filter(client_info): - return invoker(request_or_iterator, metadata) + resp = continuation(new_details, request) - if client_info.is_server_stream: - return self._intercept_server_stream( - request_or_iterator, metadata, client_info, invoker - ) + return self._wrap_stream_response(span, resp) - return self._intercept( - request_or_iterator, metadata, client_info, invoker - ) + +class StreamStreamClientInterceptor( + grpc.StreamStreamClientInterceptor, + _BaseClientInterceptor, +): + + def intercept_stream_stream( + self, + continuation, + client_call_details, + request_iterator + ): + if self.tracing_skipped(client_call_details): + return continuation(client_call_details, request_iterator) + + with self._start_span( + client_call_details.method, + end_on_exit=False, + record_exception=False, + set_status_on_exception=False, + ) as span: + + new_details = self.propagate_trace_in_details(client_call_details) + + resp = continuation(new_details, request_iterator) + + return self._wrap_stream_response(span, resp) From 003560149687361543dca120d293a56f84a4f386 Mon Sep 17 00:00:00 2001 From: Corvin Lasogga Date: Sun, 15 Jan 2023 20:29:22 +0100 Subject: [PATCH 02/10] Made _BaseAioClientInterceptor independent of OpenTelemetryClientInterceptor --- .../instrumentation/grpc/_aio_client.py | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_aio_client.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_aio_client.py index c7630bfe9f..1b38e61c55 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_aio_client.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_aio_client.py @@ -18,14 +18,11 @@ import grpc from grpc.aio import ClientCallDetails -from opentelemetry import context -from opentelemetry.instrumentation.grpc._client import ( - OpenTelemetryClientInterceptor, - _carrier_setter, -) +from opentelemetry import context, trace +from opentelemetry.instrumentation.grpc._client import _carrier_setter from opentelemetry.instrumentation.utils import _SUPPRESS_INSTRUMENTATION_KEY from opentelemetry.propagate import inject -from opentelemetry.semconv.trace import SpanAttributes +from opentelemetry.semconv.trace import RpcSystemValues, SpanAttributes from opentelemetry.trace.status import Status, StatusCode @@ -49,7 +46,12 @@ def callback(call): return callback -class _BaseAioClientInterceptor(OpenTelemetryClientInterceptor): +class _BaseAioClientInterceptor: + + def __init__(self, tracer, filter_=None): + self._tracer = tracer + self._filter = filter_ + @staticmethod def propagate_trace_in_details(client_call_details): metadata = client_call_details.metadata @@ -99,6 +101,22 @@ def _start_interceptor_span(self, method): set_status_on_exception=False, ) + def _start_span(self, method, **kwargs): + service, meth = method.lstrip("/").split("/", 1) + attributes = { + SpanAttributes.RPC_SYSTEM: RpcSystemValues.GRPC.value, + SpanAttributes.RPC_SERVICE: service, + SpanAttributes.RPC_METHOD: meth, + SpanAttributes.RPC_GRPC_STATUS_CODE: grpc.StatusCode.OK.value[0], + } + + return self._tracer.start_as_current_span( + name=method, + kind=trace.SpanKind.CLIENT, + attributes=attributes, + **kwargs, + ) + async def _wrap_unary_response(self, continuation, span): try: call = await continuation() From dff2b06094a6925dd217a8e295b6d1092868d2d5 Mon Sep 17 00:00:00 2001 From: Corvin Lasogga Date: Sun, 15 Jan 2023 20:29:50 +0100 Subject: [PATCH 03/10] Removed external implementation of gRPC Python interceptors --- .../instrumentation/grpc/grpcext/__init__.py | 125 ------- .../grpc/grpcext/_interceptor.py | 350 ------------------ 2 files changed, 475 deletions(-) delete mode 100644 instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/__init__.py delete mode 100644 instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/__init__.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/__init__.py deleted file mode 100644 index d5e2549bab..0000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/__init__.py +++ /dev/null @@ -1,125 +0,0 @@ -# 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. - -# pylint:disable=import-outside-toplevel -# pylint:disable=import-self -# pylint:disable=no-name-in-module - -import abc - - -class UnaryClientInfo(abc.ABC): - """Consists of various information about a unary RPC on the - invocation-side. - - Attributes: - full_method: A string of the full RPC method, i.e., - /package.service/method. - timeout: The length of time in seconds to wait for the computation to - terminate or be cancelled, or None if this method should block until - the computation is terminated or is cancelled no matter how long that - takes. - """ - - -class StreamClientInfo(abc.ABC): - """Consists of various information about a stream RPC on the - invocation-side. - - Attributes: - full_method: A string of the full RPC method, i.e., - /package.service/method. - is_client_stream: Indicates whether the RPC is client-streaming. - is_server_stream: Indicates whether the RPC is server-streaming. - timeout: The length of time in seconds to wait for the computation to - terminate or be cancelled, or None if this method should block until - the computation is terminated or is cancelled no matter how long that - takes. - """ - - -class UnaryClientInterceptor(abc.ABC): - """Affords intercepting unary-unary RPCs on the invocation-side.""" - - @abc.abstractmethod - def intercept_unary(self, request, metadata, client_info, invoker): - """Intercepts unary-unary RPCs on the invocation-side. - - Args: - request: The request value for the RPC. - metadata: Optional :term:`metadata` to be transmitted to the - service-side of the RPC. - client_info: A UnaryClientInfo containing various information about - the RPC. - invoker: The handler to complete the RPC on the client. It is the - interceptor's responsibility to call it. - - Returns: - The result from calling invoker(request, metadata). - """ - raise NotImplementedError() - - -class StreamClientInterceptor(abc.ABC): - """Affords intercepting stream RPCs on the invocation-side.""" - - @abc.abstractmethod - def intercept_stream( - self, request_or_iterator, metadata, client_info, invoker - ): - """Intercepts stream RPCs on the invocation-side. - - Args: - request_or_iterator: The request value for the RPC if - `client_info.is_client_stream` is `false`; otherwise, an iterator of - request values. - metadata: Optional :term:`metadata` to be transmitted to the service-side - of the RPC. - client_info: A StreamClientInfo containing various information about - the RPC. - invoker: The handler to complete the RPC on the client. It is the - interceptor's responsibility to call it. - - Returns: - The result from calling invoker(metadata). - """ - raise NotImplementedError() - - -def intercept_channel(channel, *interceptors): - """Creates an intercepted channel. - - Args: - channel: A Channel. - interceptors: Zero or more UnaryClientInterceptors or - StreamClientInterceptors - - Returns: - A Channel. - - Raises: - TypeError: If an interceptor derives from neither UnaryClientInterceptor - nor StreamClientInterceptor. - """ - from . import _interceptor - - return _interceptor.intercept_channel(channel, *interceptors) - - -__all__ = ( - "UnaryClientInterceptor", - "StreamClientInfo", - "StreamClientInterceptor", - "intercept_channel", -) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py deleted file mode 100644 index 53ee46a20d..0000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py +++ /dev/null @@ -1,350 +0,0 @@ -# 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. - -# pylint:disable=relative-beyond-top-level -# pylint:disable=no-member - -"""Implementation of gRPC Python interceptors.""" - - -import collections - -import grpc - -from opentelemetry.instrumentation.grpc import grpcext - - -class _UnaryClientInfo( - collections.namedtuple("_UnaryClientInfo", ("full_method", "timeout")) -): - pass - - -class _StreamClientInfo( - collections.namedtuple( - "_StreamClientInfo", - ("full_method", "is_client_stream", "is_server_stream", "timeout"), - ) -): - pass - - -class _InterceptorUnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable): - def __init__(self, method, base_callable, interceptor): - self._method = method - self._base_callable = base_callable - self._interceptor = interceptor - - def __call__( - self, - request, - timeout=None, - metadata=None, - credentials=None, - wait_for_ready=None, - compression=None, - ): - def invoker(request, metadata): - return self._base_callable( - request, - timeout, - metadata, - credentials, - wait_for_ready, - compression, - ) - - client_info = _UnaryClientInfo(self._method, timeout) - return self._interceptor.intercept_unary( - request, metadata, client_info, invoker - ) - - def with_call( - self, - request, - timeout=None, - metadata=None, - credentials=None, - wait_for_ready=None, - compression=None, - ): - def invoker(request, metadata): - return self._base_callable.with_call( - request, - timeout, - metadata, - credentials, - wait_for_ready, - compression, - ) - - client_info = _UnaryClientInfo(self._method, timeout) - return self._interceptor.intercept_unary( - request, metadata, client_info, invoker - ) - - def future( - self, - request, - timeout=None, - metadata=None, - credentials=None, - wait_for_ready=None, - compression=None, - ): - def invoker(request, metadata): - return self._base_callable.future( - request, - timeout, - metadata, - credentials, - wait_for_ready, - compression, - ) - - client_info = _UnaryClientInfo(self._method, timeout) - return self._interceptor.intercept_unary( - request, metadata, client_info, invoker - ) - - -class _InterceptorUnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable): - def __init__(self, method, base_callable, interceptor): - self._method = method - self._base_callable = base_callable - self._interceptor = interceptor - - def __call__( - self, - request, - timeout=None, - metadata=None, - credentials=None, - wait_for_ready=None, - compression=None, - ): - def invoker(request, metadata): - return self._base_callable( - request, - timeout, - metadata, - credentials, - wait_for_ready, - compression, - ) - - client_info = _StreamClientInfo(self._method, False, True, timeout) - return self._interceptor.intercept_stream( - request, metadata, client_info, invoker - ) - - -class _InterceptorStreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable): - def __init__(self, method, base_callable, interceptor): - self._method = method - self._base_callable = base_callable - self._interceptor = interceptor - - def __call__( - self, - request_iterator, - timeout=None, - metadata=None, - credentials=None, - wait_for_ready=None, - compression=None, - ): - def invoker(request_iterator, metadata): - return self._base_callable( - request_iterator, - timeout, - metadata, - credentials, - wait_for_ready, - compression, - ) - - client_info = _StreamClientInfo(self._method, True, False, timeout) - return self._interceptor.intercept_stream( - request_iterator, metadata, client_info, invoker - ) - - def with_call( - self, - request_iterator, - timeout=None, - metadata=None, - credentials=None, - wait_for_ready=None, - compression=None, - ): - def invoker(request_iterator, metadata): - return self._base_callable.with_call( - request_iterator, - timeout, - metadata, - credentials, - wait_for_ready, - compression, - ) - - client_info = _StreamClientInfo(self._method, True, False, timeout) - return self._interceptor.intercept_stream( - request_iterator, metadata, client_info, invoker - ) - - def future( - self, - request_iterator, - timeout=None, - metadata=None, - credentials=None, - wait_for_ready=None, - compression=None, - ): - def invoker(request_iterator, metadata): - return self._base_callable.future( - request_iterator, - timeout, - metadata, - credentials, - wait_for_ready, - compression, - ) - - client_info = _StreamClientInfo(self._method, True, False, timeout) - return self._interceptor.intercept_stream( - request_iterator, metadata, client_info, invoker - ) - - -class _InterceptorStreamStreamMultiCallable(grpc.StreamStreamMultiCallable): - def __init__(self, method, base_callable, interceptor): - self._method = method - self._base_callable = base_callable - self._interceptor = interceptor - - def __call__( - self, - request_iterator, - timeout=None, - metadata=None, - credentials=None, - wait_for_ready=None, - compression=None, - ): - def invoker(request_iterator, metadata): - return self._base_callable( - request_iterator, - timeout, - metadata, - credentials, - wait_for_ready, - compression, - ) - - client_info = _StreamClientInfo(self._method, True, True, timeout) - return self._interceptor.intercept_stream( - request_iterator, metadata, client_info, invoker - ) - - -class _InterceptorChannel(grpc.Channel): - def __init__(self, channel, interceptor): - self._channel = channel - self._interceptor = interceptor - - def subscribe(self, *args, **kwargs): - self._channel.subscribe(*args, **kwargs) - - def unsubscribe(self, *args, **kwargs): - self._channel.unsubscribe(*args, **kwargs) - - def unary_unary( - self, method, request_serializer=None, response_deserializer=None - ): - base_callable = self._channel.unary_unary( - method, request_serializer, response_deserializer - ) - if isinstance(self._interceptor, grpcext.UnaryClientInterceptor): - return _InterceptorUnaryUnaryMultiCallable( - method, base_callable, self._interceptor - ) - return base_callable - - def unary_stream( - self, method, request_serializer=None, response_deserializer=None - ): - base_callable = self._channel.unary_stream( - method, request_serializer, response_deserializer - ) - if isinstance(self._interceptor, grpcext.StreamClientInterceptor): - return _InterceptorUnaryStreamMultiCallable( - method, base_callable, self._interceptor - ) - return base_callable - - def stream_unary( - self, method, request_serializer=None, response_deserializer=None - ): - base_callable = self._channel.stream_unary( - method, request_serializer, response_deserializer - ) - if isinstance(self._interceptor, grpcext.StreamClientInterceptor): - return _InterceptorStreamUnaryMultiCallable( - method, base_callable, self._interceptor - ) - return base_callable - - def stream_stream( - self, method, request_serializer=None, response_deserializer=None - ): - base_callable = self._channel.stream_stream( - method, request_serializer, response_deserializer - ) - if isinstance(self._interceptor, grpcext.StreamClientInterceptor): - return _InterceptorStreamStreamMultiCallable( - method, base_callable, self._interceptor - ) - return base_callable - - def close(self): - if not hasattr(self._channel, "close"): - raise RuntimeError( - "close() is not supported with the installed version of grpcio" - ) - self._channel.close() - - def __enter__(self): - """Enters the runtime context related to the channel object.""" - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - """Exits the runtime context related to the channel object.""" - self.close() - - -def intercept_channel(channel, *interceptors): - result = channel - for interceptor in interceptors: - if not isinstance( - interceptor, grpcext.UnaryClientInterceptor - ) and not isinstance(interceptor, grpcext.StreamClientInterceptor): - raise TypeError( - "interceptor must be either a " - "grpcext.UnaryClientInterceptor or a " - "grpcext.StreamClientInterceptor" - ) - result = _InterceptorChannel(result, interceptor) - return result From 055a5d4ec6a92ae162b49a4b5458c1c6f93889dd Mon Sep 17 00:00:00 2001 From: Corvin Lasogga Date: Sun, 15 Jan 2023 20:30:15 +0100 Subject: [PATCH 04/10] removed RpcInfo, since not needed any more --- .../instrumentation/grpc/_utilities.py | 33 ------------------- 1 file changed, 33 deletions(-) delete mode 100644 instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_utilities.py diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_utilities.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_utilities.py deleted file mode 100644 index b6ff7d311a..0000000000 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_utilities.py +++ /dev/null @@ -1,33 +0,0 @@ -# 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. - -"""Internal utilities.""" - - -class RpcInfo: - def __init__( - self, - full_method=None, - metadata=None, - timeout=None, - request=None, - response=None, - error=None, - ): - self.full_method = full_method - self.metadata = metadata - self.timeout = timeout - self.request = request - self.response = response - self.error = error From 9573cd4363fb2dc802855c62881a2908aa9574a7 Mon Sep 17 00:00:00 2001 From: Corvin Lasogga Date: Sun, 15 Jan 2023 20:30:58 +0100 Subject: [PATCH 05/10] adapted test_client_interceptor_trace_context_propagation accordingly to aio-test --- .../tests/test_client_interceptor.py | 56 ++++---- .../tests/test_client_interceptor_filter.py | 124 ++++++++---------- 2 files changed, 82 insertions(+), 98 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py index 810ee930dd..75384ae83f 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor.py @@ -20,12 +20,7 @@ import opentelemetry.instrumentation.grpc from opentelemetry import context, trace from opentelemetry.instrumentation.grpc import GrpcInstrumentorClient -from opentelemetry.instrumentation.grpc._client import ( - OpenTelemetryClientInterceptor, -) -from opentelemetry.instrumentation.grpc.grpcext._interceptor import ( - _UnaryClientInfo, -) +from opentelemetry.instrumentation.grpc._client import UnaryUnaryClientInterceptor from opentelemetry.instrumentation.utils import _SUPPRESS_INSTRUMENTATION_KEY from opentelemetry.propagate import get_global_textmap, set_global_textmap from opentelemetry.semconv.trace import SpanAttributes @@ -84,6 +79,16 @@ def _intercept_call( return continuation(client_call_details, request_or_iterator) +class RecordingInterceptor(grpc.UnaryUnaryClientInterceptor): + recorded_details = None + + def intercept_unary_unary( + self, continuation, client_call_details, request + ): + self.recorded_details = client_call_details + return continuation(client_call_details, request) + + class TestClientProto(TestBase): def setUp(self): super().setUp() @@ -277,30 +282,25 @@ def test_client_interceptor_trace_context_propagation( previous_propagator = get_global_textmap() try: set_global_textmap(MockTextMapPropagator()) - interceptor = OpenTelemetryClientInterceptor(trace.NoOpTracer()) - - carrier = tuple() - - def invoker(request, metadata): - nonlocal carrier - carrier = metadata - return {} - - request = Request(client_id=1, request_data="data") - interceptor.intercept_unary( - request, - {}, - _UnaryClientInfo( - full_method="/GRPCTestServer/SimpleMethod", timeout=None - ), - invoker=invoker, + interceptor = UnaryUnaryClientInterceptor(trace.NoOpTracer()) + + recording_interceptor = RecordingInterceptor() + interceptors = [interceptor, recording_interceptor] + + channel = grpc.intercept_channel( + grpc.insecure_channel("localhost:25565"), + *interceptors ) - assert len(carrier) == 2 - assert carrier[0][0] == "mock-traceid" - assert carrier[0][1] == "0" - assert carrier[1][0] == "mock-spanid" - assert carrier[1][1] == "0" + stub = test_server_pb2_grpc.GRPCTestServerStub(channel) + simple_method(stub) + + metadata = recording_interceptor.recorded_details.metadata + assert len(metadata) == 2 + assert metadata[0][0] == "mock-traceid" + assert metadata[0][1] == "0" + assert metadata[1][0] == "mock-spanid" + assert metadata[1][1] == "0" finally: set_global_textmap(previous_propagator) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_filter.py b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_filter.py index a15268464b..8b118b94a5 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_filter.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/tests/test_client_interceptor_filter.py @@ -24,10 +24,8 @@ from opentelemetry import context, trace from opentelemetry.instrumentation.grpc import GrpcInstrumentorClient, filters from opentelemetry.instrumentation.grpc._client import ( - OpenTelemetryClientInterceptor, -) -from opentelemetry.instrumentation.grpc.grpcext._interceptor import ( - _UnaryClientInfo, + UnaryUnaryClientInterceptor, StreamUnaryClientInterceptor, + UnaryStreamClientInterceptor, StreamStreamClientInterceptor ) from opentelemetry.instrumentation.utils import _SUPPRESS_INSTRUMENTATION_KEY from opentelemetry.propagate import get_global_textmap, set_global_textmap @@ -43,6 +41,7 @@ simple_method_future, ) from ._server import create_test_server +from . test_client_interceptor import RecordingInterceptor from .protobuf.test_server_pb2 import Request @@ -202,30 +201,25 @@ def test_client_interceptor_trace_context_propagation( previous_propagator = get_global_textmap() try: set_global_textmap(MockTextMapPropagator()) - interceptor = OpenTelemetryClientInterceptor(trace.NoOpTracer()) - - carrier = tuple() - - def invoker(request, metadata): - nonlocal carrier - carrier = metadata - return {} - - request = Request(client_id=1, request_data="data") - interceptor.intercept_unary( - request, - {}, - _UnaryClientInfo( - full_method="/GRPCTestServer/SimpleMethod", timeout=None - ), - invoker=invoker, + interceptor = UnaryUnaryClientInterceptor(trace.NoOpTracer()) + + recording_interceptor = RecordingInterceptor() + interceptors = [interceptor, recording_interceptor] + + channel = grpc.intercept_channel( + grpc.insecure_channel("localhost:25565"), + *interceptors ) - assert len(carrier) == 2 - assert carrier[0][0] == "mock-traceid" - assert carrier[0][1] == "0" - assert carrier[1][0] == "mock-spanid" - assert carrier[1][1] == "0" + stub = test_server_pb2_grpc.GRPCTestServerStub(channel) + simple_method(stub) + + metadata = recording_interceptor.recorded_details.metadata + assert len(metadata) == 2 + assert metadata[0][0] == "mock-traceid" + assert metadata[0][1] == "0" + assert metadata[1][0] == "mock-spanid" + assert metadata[1][1] == "0" finally: set_global_textmap(previous_propagator) @@ -346,30 +340,25 @@ def test_client_interceptor_trace_context_propagation( previous_propagator = get_global_textmap() try: set_global_textmap(MockTextMapPropagator()) - interceptor = OpenTelemetryClientInterceptor(trace.NoOpTracer()) - - carrier = tuple() - - def invoker(request, metadata): - nonlocal carrier - carrier = metadata - return {} - - request = Request(client_id=1, request_data="data") - interceptor.intercept_unary( - request, - {}, - _UnaryClientInfo( - full_method="/GRPCTestServer/SimpleMethod", timeout=None - ), - invoker=invoker, + interceptor = UnaryUnaryClientInterceptor(trace.NoOpTracer()) + + recording_interceptor = RecordingInterceptor() + interceptors = [interceptor, recording_interceptor] + + channel = grpc.intercept_channel( + grpc.insecure_channel("localhost:25565"), + *interceptors ) - assert len(carrier) == 2 - assert carrier[0][0] == "mock-traceid" - assert carrier[0][1] == "0" - assert carrier[1][0] == "mock-spanid" - assert carrier[1][1] == "0" + stub = test_server_pb2_grpc.GRPCTestServerStub(channel) + simple_method(stub) + + metadata = recording_interceptor.recorded_details.metadata + assert len(metadata) == 2 + assert metadata[0][0] == "mock-traceid" + assert metadata[0][1] == "0" + assert metadata[1][0] == "mock-spanid" + assert metadata[1][1] == "0" finally: set_global_textmap(previous_propagator) @@ -609,30 +598,25 @@ def test_client_interceptor_trace_context_propagation( previous_propagator = get_global_textmap() try: set_global_textmap(MockTextMapPropagator()) - interceptor = OpenTelemetryClientInterceptor(trace.NoOpTracer()) - - carrier = tuple() - - def invoker(request, metadata): - nonlocal carrier - carrier = metadata - return {} - - request = Request(client_id=1, request_data="data") - interceptor.intercept_unary( - request, - {}, - _UnaryClientInfo( - full_method="/GRPCTestServer/SimpleMethod", timeout=None - ), - invoker=invoker, + interceptor = UnaryUnaryClientInterceptor(trace.NoOpTracer()) + + recording_interceptor = RecordingInterceptor() + interceptors = [interceptor, recording_interceptor] + + channel = grpc.intercept_channel( + grpc.insecure_channel("localhost:25565"), + *interceptors ) - assert len(carrier) == 2 - assert carrier[0][0] == "mock-traceid" - assert carrier[0][1] == "0" - assert carrier[1][0] == "mock-spanid" - assert carrier[1][1] == "0" + stub = test_server_pb2_grpc.GRPCTestServerStub(channel) + simple_method(stub) + + metadata = recording_interceptor.recorded_details.metadata + assert len(metadata) == 2 + assert metadata[0][0] == "mock-traceid" + assert metadata[0][1] == "0" + assert metadata[1][0] == "mock-spanid" + assert metadata[1][1] == "0" finally: set_global_textmap(previous_propagator) From 715086e4f572f001fd4cc360815c772e0e803ac8 Mon Sep 17 00:00:00 2001 From: Corvin Lasogga Date: Sun, 15 Jan 2023 20:35:57 +0100 Subject: [PATCH 06/10] updatet documentation to use ClientInterceptors manually --- .../instrumentation/grpc/__init__.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/__init__.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/__init__.py index 1f224b1c31..d5d4a97334 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/__init__.py @@ -59,6 +59,18 @@ def run(): logging.basicConfig() run() +You can also add the interceptors manually, rather than using +:py:class:`~opentelemetry.instrumentation.grpc.GrpcInstrumentorClient`: + +.. code-block:: python + + from opentelemetry.instrumentation.grpc import client_interceptors + + channel = grpc.intercept_channel( + grpc.insecure_channel("localhost:50051"), + *client_interceptors() + ) + Usage Server ------------ .. code-block:: python @@ -540,7 +552,7 @@ def _uninstrument(self, **kwargs): def client_interceptors(tracer_provider=None, filter_=None): - """Create a gRPC client channel interceptor. + """Create gRPC client channel interceptors. Args: tracer: The tracer to use to create client-side spans. @@ -550,7 +562,7 @@ def client_interceptors(tracer_provider=None, filter_=None): all requests. Returns: - An invocation-side interceptor object. + A list of invocation-side interceptor objects. """ from . import _client From 15a13877bc3fec9b57cfe9a7f5de1d08c748f725 Mon Sep 17 00:00:00 2001 From: Corvin Lasogga Date: Sun, 15 Jan 2023 21:18:19 +0100 Subject: [PATCH 07/10] Added documentation --- .../instrumentation/grpc/_client.py | 74 ++++++++++++++++++- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py index dbefba320b..11ebbd0a74 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py @@ -47,6 +47,7 @@ def set(self, carrier: MutableMapping[str, str], key: str, value: str): def _unary_done_callback(span): + def callback(response_future): with trace.use_span(span, end_on_exit=True): code = response_future.code() @@ -71,14 +72,37 @@ def callback(response_future): class _BaseClientInterceptor: + """Base for client interceptors. + + Supplies convenient functions which are required by all four client + interceptors. + """ def __init__(self, tracer, filter_=None): + """Initializes the base for client interceptors. + + Args: + tracer: The tracer to use for tracing. + filter_: An optional filter to filter specific requests to be + instrumented. + """ self._tracer = tracer self._filter = filter_ @staticmethod def propagate_trace_in_details(client_call_details): - """Propagates """ + """Propagates the trace into the metadata of the call. + + Args: + client_call_details: The original + :py:class:`~grpc.ClientCallDetails`, describing the outgoing + RPC. + + Returns: + An adapted version of the original + :py:class:`~grpc.ClientCallDetails`, describing the outgoing RPC, + whereby the metadata contains the trace ID. + """ metadata = client_call_details.metadata if not metadata: mutable_metadata = OrderedDict() @@ -92,7 +116,8 @@ def propagate_trace_in_details(client_call_details): client_call_details.method, client_call_details.timeout, metadata, - # credentials, wait_for_ready, compression + # credentials, wait_for_ready, and compression, depending on + # grpc-version *client_call_details[3:] ) @@ -121,6 +146,21 @@ def add_error_details_to_span( span.record_exception(exc) def _start_span(self, method, **kwargs): + """Context manager for creating a new span and set it as the current + span in the 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. + + Args: + method: The method name of the RPC. + **kwargs: Further keyword arguments, passed through to + :py:meth:`~opentelemetry.trace.Tracer.start_as_current_span`. + + Yields: + The newly-created span. + """ service, meth = method.lstrip("/").split("/", 1) attributes = { SpanAttributes.RPC_SYSTEM: RpcSystemValues.GRPC.value, @@ -137,6 +177,22 @@ def _start_span(self, method, **kwargs): ) def _wrap_unary_response(self, span, continuation): + """Wraps a unary-response-RPC to record a possible exception. + + Args: + span: The active span. + continuation: A callable which is created by: + + .. code-block:: python + + functools.partial( + continuation, client_call_details, request_or_iterator + ) + + Returns: + The response if the RPC is called synchonously, or the + :py:class:`~grpc.Future` if the RPC is called asnchronously. + """ response_future = None try: response_future = continuation() @@ -157,6 +213,19 @@ def _wrap_unary_response(self, span, continuation): return response_future def _wrap_stream_response(self, span, call): + """Wraps a stream-response-RPC to record a possible exception. + + Args: + span: The active span. + call: The response iterator which is created by: + + .. code-block:: python + + continuation(client_call_details, request_or_iterator) + + Returns: + The response iterator. + """ try: yield from call except Exception as exc: @@ -165,7 +234,6 @@ def _wrap_stream_response(self, span, call): finally: span.end() - def tracing_skipped( self, client_call_details: grpc.ClientCallDetails From 3fcc8cf0fefd33b13cfbab652fe6ee39ef5cf328 Mon Sep 17 00:00:00 2001 From: Corvin Lasogga Date: Sun, 15 Jan 2023 21:30:45 +0100 Subject: [PATCH 08/10] run pylint --- .../src/opentelemetry/instrumentation/grpc/_client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py index 11ebbd0a74..9f1cb32516 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/_client.py @@ -92,7 +92,7 @@ def __init__(self, tracer, filter_=None): @staticmethod def propagate_trace_in_details(client_call_details): """Propagates the trace into the metadata of the call. - + Args: client_call_details: The original :py:class:`~grpc.ClientCallDetails`, describing the outgoing @@ -243,7 +243,7 @@ def tracing_skipped( Args: client_call_details: A :py:class:`~grpc.ClientCallDetails`-object, describing the outgoing RPC. - + Returns: True if: @@ -268,7 +268,7 @@ def rpc_matches_filters( Args: client_call_details: A :py:class:`~grpc.ClientCallDetails`-object, describing the outgoing RPC. - + Returns: True if no filter is set or the :py:class:`~grpc.ClientCallDetails` matches a set filter, False otherwise. From 2c2c8f391e803f3ab92b161248309d68ced23383 Mon Sep 17 00:00:00 2001 From: Corvin Lasogga Date: Sun, 15 Jan 2023 21:34:47 +0100 Subject: [PATCH 09/10] Updated changelog --- CHANGELOG.md | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f60d4041f2..c75251cc14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fix grpc instrumentation to use official grpc.*ClientInterceptor-interfaces + ([#1583](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1583)) - Fix aiopg instrumentation to work with aiopg < 2.0.0 ([#1473](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1473)) - `opentelemetry-instrumentation-aws-lambda` Adds an option to configure `disable_aws_context_propagation` by @@ -24,7 +26,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-asgi` Fix keys() in class ASGIGetter to correctly fetch values from carrier headers. ([#1435](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1435)) - ## Version 1.15.0/0.36b0 (2022-12-10) - Add uninstrument test for sqlalchemy @@ -144,7 +145,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add metric instrumentation in starlette ([#1327](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1327)) - ### Fixed - `opentelemetry-instrumentation-kafka-python`: wait for metadata @@ -157,12 +157,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#1208](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1208)) - `opentelemetry-instrumentation-aiohttp-client` Fix producing additional spans with each newly created ClientSession - ([#1246](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1246)) -- Add _is_opentelemetry_instrumented check in _InstrumentedFastAPI class +- Add _is_opentelemetry_instrumented check in_InstrumentedFastAPI class ([#1313](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1313)) - Fix uninstrumentation of existing app instances in FastAPI ([#1258](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1258)) - Fix uninstrumentation of existing app instances in falcon - ([#1341]https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1341) + ([#1341] ## Version 1.12.0/0.33b0 (2022-08-08) @@ -176,6 +176,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#1203](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1203)) ### Added + - `opentelemetry-instrumentation-redis` add support to instrument RedisCluster clients ([#1177](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1177)) - `opentelemetry-instrumentation-sqlalchemy` Added span for the connection phase ([#1133](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1133)) @@ -188,11 +189,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.12.0rc2-0.32b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc2-0.32b0) - 2022-07-01 - - Pyramid: Only categorize 500s server exceptions as errors ([#1037](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1037)) ### Fixed + - Fix bug in system metrics by checking their configuration ([#1129](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1129)) - Adding escape call to fix [auto-instrumentation not producing spans on Windows](https://github.com/open-telemetry/opentelemetry-python/issues/2703). @@ -205,8 +206,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fixed typo in `system.network.io` metric configuration ([#1135](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1135)) - ### Added + - `opentelemetry-instrumentation-aiohttp-client` Add support for optional custom trace_configs argument. ([1079](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1079)) - `opentelemetry-instrumentation-sqlalchemy` add support to instrument multiple engines @@ -230,10 +231,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Integrated sqlcommenter plugin into opentelemetry-instrumentation-django ([#896](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/896)) - ## Version 1.12.0rc1/0.31b0 (2022-05-17) ### Fixed + - `opentelemetry-instrumentation-aiohttp-client` make span attributes available to sampler ([#1072](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1072)) - `opentelemetry-instrumentation-aws-lambda` Fixed an issue - in some rare cases (API GW proxy integration test) @@ -246,6 +247,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-sdk-extension-aws` change timeout for AWS EC2 and EKS metadata requests from 1000 seconds and 2000 seconds to 1 second ### Added + - `opentelemetry-instrument` and `opentelemetry-bootstrap` now include a `--version` flag ([#1065](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1065)) - `opentelemetry-instrumentation-redis` now instruments asynchronous Redis clients, if the installed redis-py includes async support (>=4.2.0). @@ -253,21 +255,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-boto3sqs` added AWS's SQS instrumentation. ([#1081](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1081)) - ## Version 1.11.1/0.30b1 (2022-04-21) ### Added + - `opentelemetry-instrumentation-starlette` Capture custom request/response headers in span attributes ([#1046](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1046)) ### Fixed + - Prune autoinstrumentation sitecustomize module directory from PYTHONPATH immediately ([#1066](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1066)) - ## Version 1.11.0/0.30b0 (2022-04-18) ### Fixed + - `opentelemetry-instrumentation-pyramid` Fixed which package is the correct caller in _traced_init. ([#830](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/830)) - `opentelemetry-instrumentation-tornado` Fix Tornado errors mapping to 500 @@ -302,7 +305,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-pyramid` Pyramid: Capture custom request/response headers in span attributes ([#1022](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1022)) - ## Version 1.10.0/0.29b0 (2022-03-10) - `opentelemetry-instrumentation-wsgi` Capture custom request/response headers in span attributes @@ -355,7 +357,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Version 1.9.0/0.28b0 (2022-01-26) - ### Added - `opentelemetry-instrumentation-pyramid` Pyramid: Conditionally create SERVER spans @@ -394,7 +395,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-aiohttp-client` aiohttp: Remove `span_name` from docs ([#857](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/857)) - ## Version 1.8.0/0.27b0 (2021-12-17) ### Added @@ -469,13 +469,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#755](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/755)) ### Added + - `opentelemetry-instrumentation-pika` Add `publish_hook` and `consume_hook` callbacks passed as arguments to the instrument method ([#763](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/763)) - ## Version 1.6.1/0.25b1 (2021-10-18) ### Changed + - `opentelemetry-util-http` no longer contains an instrumentation entrypoint and will not be loaded automatically by the auto instrumentor. ([#745](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/745)) @@ -489,7 +490,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#760](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/760)) ## Version 1.6.0/0.25b0 (2021-10-13) + ### Added + - `opentelemetry-sdk-extension-aws` Release AWS Python SDK Extension as 1.0.0 ([#667](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/667)) - `opentelemetry-instrumentation-urllib3`, `opentelemetry-instrumentation-requests` @@ -516,6 +519,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#391](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/391)) ### Changed + - `opentelemetry-instrumentation-flask` Fix `RuntimeError: Working outside of request context` ([#734](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/734)) - `opentelemetry-propagators-aws-xray` Rename `AwsXRayFormat` to `AwsXRayPropagator` @@ -546,6 +550,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Version 1.5.0/0.24b0 (2021-08-26) ### Added + - `opentelemetry-sdk-extension-aws` Add AWS resource detectors to extension package ([#586](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/586)) - `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-aiohttp-client`, `openetelemetry-instrumentation-fastapi`, @@ -564,10 +569,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Version 1.4.0/0.23b0 (2021-07-21) ### Removed + - Move `opentelemetry-instrumentation` to the core repo. ([#595](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/595)) ### Changed + - `opentelemetry-instrumentation-falcon` added support for Falcon 3. ([#607](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/607)) - `opentelemetry-instrumentation-tornado` properly instrument work done in tornado on_finish method. @@ -615,12 +622,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#568](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/568)) ### Added + - `opentelemetry-instrumentation-httpx` Add `httpx` instrumentation ([#461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/461)) ## Version 1.3.0/0.22b0 (2021-06-01) ### Changed + - `opentelemetry-bootstrap` not longer forcibly removes and re-installs libraries and their instrumentations. This means running bootstrap will not auto-upgrade existing dependencies and as a result not cause dependency conflicts. @@ -637,6 +646,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#488](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/488)) ### Added + - `opentelemetry-instrumentation-botocore` now supports context propagation for lambda invoke via Payload embedded headers. ([#458](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/458)) @@ -646,6 +656,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Version 1.2.0/0.21b0 (2021-05-11) ### Changed + - Instrumentation packages don't specify the libraries they instrument as dependencies anymore. Instead, they verify the correct version of libraries are installed at runtime. ([#475](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/475)) @@ -1095,7 +1106,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-ext-boto` Initial release - `opentelemetry-ext-botocore` Initial release - `opentelemetry-ext-system-metrics` Initial release - (https://github.com/open-telemetry/opentelemetry-python/pull/652) + () ## Version 0.8b0 (2020-05-27) From f43e164a5e1ea614e8ffa639e9cb261662cebe59 Mon Sep 17 00:00:00 2001 From: Corvin Lasogga Date: Sun, 15 Jan 2023 21:47:23 +0100 Subject: [PATCH 10/10] moved change into added --- CHANGELOG.md | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c75251cc14..49ccf79087 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,13 +9,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- grpc instrumentation uses official grpc.*ClientInterceptor-interfaces + ([#1583](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1583)) - `opentelemetry/sdk/extension/aws` Implement [`aws.ecs.*`](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/cloud_provider/aws/ecs.md) and [`aws.logs.*`](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/cloud_provider/aws/logs/) resource attributes in the `AwsEcsResourceDetector` detector when the ECS Metadata v4 is available ([#1212](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1212)) ### Fixed -- Fix grpc instrumentation to use official grpc.*ClientInterceptor-interfaces - ([#1583](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1583)) - Fix aiopg instrumentation to work with aiopg < 2.0.0 ([#1473](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1473)) - `opentelemetry-instrumentation-aws-lambda` Adds an option to configure `disable_aws_context_propagation` by @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-asgi` Fix keys() in class ASGIGetter to correctly fetch values from carrier headers. ([#1435](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1435)) + ## Version 1.15.0/0.36b0 (2022-12-10) - Add uninstrument test for sqlalchemy @@ -145,6 +146,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add metric instrumentation in starlette ([#1327](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1327)) + ### Fixed - `opentelemetry-instrumentation-kafka-python`: wait for metadata @@ -157,12 +159,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#1208](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1208)) - `opentelemetry-instrumentation-aiohttp-client` Fix producing additional spans with each newly created ClientSession - ([#1246](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1246)) -- Add _is_opentelemetry_instrumented check in_InstrumentedFastAPI class +- Add _is_opentelemetry_instrumented check in _InstrumentedFastAPI class ([#1313](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1313)) - Fix uninstrumentation of existing app instances in FastAPI ([#1258](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1258)) - Fix uninstrumentation of existing app instances in falcon - ([#1341] + ([#1341]https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1341) ## Version 1.12.0/0.33b0 (2022-08-08) @@ -176,7 +178,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#1203](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1203)) ### Added - - `opentelemetry-instrumentation-redis` add support to instrument RedisCluster clients ([#1177](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1177)) - `opentelemetry-instrumentation-sqlalchemy` Added span for the connection phase ([#1133](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1133)) @@ -189,11 +190,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.12.0rc2-0.32b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.12.0rc2-0.32b0) - 2022-07-01 + - Pyramid: Only categorize 500s server exceptions as errors ([#1037](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1037)) ### Fixed - - Fix bug in system metrics by checking their configuration ([#1129](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1129)) - Adding escape call to fix [auto-instrumentation not producing spans on Windows](https://github.com/open-telemetry/opentelemetry-python/issues/2703). @@ -206,8 +207,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fixed typo in `system.network.io` metric configuration ([#1135](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1135)) -### Added +### Added - `opentelemetry-instrumentation-aiohttp-client` Add support for optional custom trace_configs argument. ([1079](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1079)) - `opentelemetry-instrumentation-sqlalchemy` add support to instrument multiple engines @@ -231,10 +232,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Integrated sqlcommenter plugin into opentelemetry-instrumentation-django ([#896](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/896)) + ## Version 1.12.0rc1/0.31b0 (2022-05-17) ### Fixed - - `opentelemetry-instrumentation-aiohttp-client` make span attributes available to sampler ([#1072](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1072)) - `opentelemetry-instrumentation-aws-lambda` Fixed an issue - in some rare cases (API GW proxy integration test) @@ -247,7 +248,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-sdk-extension-aws` change timeout for AWS EC2 and EKS metadata requests from 1000 seconds and 2000 seconds to 1 second ### Added - - `opentelemetry-instrument` and `opentelemetry-bootstrap` now include a `--version` flag ([#1065](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1065)) - `opentelemetry-instrumentation-redis` now instruments asynchronous Redis clients, if the installed redis-py includes async support (>=4.2.0). @@ -255,22 +255,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-boto3sqs` added AWS's SQS instrumentation. ([#1081](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1081)) + ## Version 1.11.1/0.30b1 (2022-04-21) ### Added - - `opentelemetry-instrumentation-starlette` Capture custom request/response headers in span attributes ([#1046](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1046)) ### Fixed - - Prune autoinstrumentation sitecustomize module directory from PYTHONPATH immediately ([#1066](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1066)) + ## Version 1.11.0/0.30b0 (2022-04-18) ### Fixed - - `opentelemetry-instrumentation-pyramid` Fixed which package is the correct caller in _traced_init. ([#830](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/830)) - `opentelemetry-instrumentation-tornado` Fix Tornado errors mapping to 500 @@ -305,6 +304,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-pyramid` Pyramid: Capture custom request/response headers in span attributes ([#1022](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1022)) + ## Version 1.10.0/0.29b0 (2022-03-10) - `opentelemetry-instrumentation-wsgi` Capture custom request/response headers in span attributes @@ -357,6 +357,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Version 1.9.0/0.28b0 (2022-01-26) + ### Added - `opentelemetry-instrumentation-pyramid` Pyramid: Conditionally create SERVER spans @@ -395,6 +396,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-aiohttp-client` aiohttp: Remove `span_name` from docs ([#857](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/857)) + ## Version 1.8.0/0.27b0 (2021-12-17) ### Added @@ -469,14 +471,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#755](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/755)) ### Added - - `opentelemetry-instrumentation-pika` Add `publish_hook` and `consume_hook` callbacks passed as arguments to the instrument method ([#763](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/763)) + ## Version 1.6.1/0.25b1 (2021-10-18) ### Changed - - `opentelemetry-util-http` no longer contains an instrumentation entrypoint and will not be loaded automatically by the auto instrumentor. ([#745](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/745)) @@ -490,9 +491,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#760](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/760)) ## Version 1.6.0/0.25b0 (2021-10-13) - ### Added - - `opentelemetry-sdk-extension-aws` Release AWS Python SDK Extension as 1.0.0 ([#667](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/667)) - `opentelemetry-instrumentation-urllib3`, `opentelemetry-instrumentation-requests` @@ -519,7 +518,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#391](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/391)) ### Changed - - `opentelemetry-instrumentation-flask` Fix `RuntimeError: Working outside of request context` ([#734](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/734)) - `opentelemetry-propagators-aws-xray` Rename `AwsXRayFormat` to `AwsXRayPropagator` @@ -550,7 +548,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Version 1.5.0/0.24b0 (2021-08-26) ### Added - - `opentelemetry-sdk-extension-aws` Add AWS resource detectors to extension package ([#586](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/586)) - `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-aiohttp-client`, `openetelemetry-instrumentation-fastapi`, @@ -569,12 +566,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Version 1.4.0/0.23b0 (2021-07-21) ### Removed - - Move `opentelemetry-instrumentation` to the core repo. ([#595](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/595)) ### Changed - - `opentelemetry-instrumentation-falcon` added support for Falcon 3. ([#607](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/607)) - `opentelemetry-instrumentation-tornado` properly instrument work done in tornado on_finish method. @@ -622,14 +617,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#568](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/568)) ### Added - - `opentelemetry-instrumentation-httpx` Add `httpx` instrumentation ([#461](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/461)) ## Version 1.3.0/0.22b0 (2021-06-01) ### Changed - - `opentelemetry-bootstrap` not longer forcibly removes and re-installs libraries and their instrumentations. This means running bootstrap will not auto-upgrade existing dependencies and as a result not cause dependency conflicts. @@ -646,7 +639,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#488](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/488)) ### Added - - `opentelemetry-instrumentation-botocore` now supports context propagation for lambda invoke via Payload embedded headers. ([#458](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/458)) @@ -656,7 +648,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Version 1.2.0/0.21b0 (2021-05-11) ### Changed - - Instrumentation packages don't specify the libraries they instrument as dependencies anymore. Instead, they verify the correct version of libraries are installed at runtime. ([#475](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/475)) @@ -1106,7 +1097,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-ext-boto` Initial release - `opentelemetry-ext-botocore` Initial release - `opentelemetry-ext-system-metrics` Initial release - () + (https://github.com/open-telemetry/opentelemetry-python/pull/652) ## Version 0.8b0 (2020-05-27)